/* libde265 example application "dec265". MIT License Copyright (c) 2013-2014 struktur AG, Dirk Farin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define DO_MEMORY_LOGGING 0 #include "de265.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_MALLOC_H #include #endif #include #ifndef _MSC_VER #include #include #endif #include "libde265/quality.h" #if HAVE_VIDEOGFX #include using namespace videogfx; #endif #if HAVE_SDL #include "sdl.hh" #endif #define BUFFER_SIZE 40960 #define NUM_THREADS 4 int nThreads=0; bool nal_input=false; int quiet=0; bool check_hash=false; bool show_help=false; bool dump_headers=false; bool write_yuv=false; bool output_with_videogfx=false; bool logging=true; bool no_acceleration=false; const char *output_filename = "out.yuv"; uint32_t max_frames=UINT32_MAX; bool write_bytestream=false; const char *bytestream_filename; bool measure_quality=false; bool show_ssim_map=false; bool show_psnr_map=false; const char* reference_filename; FILE* reference_file; int highestTID = 100; int verbosity=0; int disable_deblocking=0; int disable_sao=0; static struct option long_options[] = { {"quiet", no_argument, 0, 'q' }, {"threads", required_argument, 0, 't' }, {"check-hash", no_argument, 0, 'c' }, {"profile", no_argument, 0, 'p' }, {"frames", required_argument, 0, 'f' }, {"output", required_argument, 0, 'o' }, {"dump", no_argument, 0, 'd' }, {"nal", no_argument, 0, 'n' }, {"videogfx", no_argument, 0, 'V' }, {"no-logging", no_argument, 0, 'L' }, {"help", no_argument, 0, 'h' }, {"noaccel", no_argument, 0, '0' }, {"write-bytestream", required_argument,0, 'B' }, {"measure", required_argument, 0, 'm' }, {"ssim", no_argument, 0, 's' }, {"errmap", no_argument, 0, 'e' }, {"highest-TID", required_argument, 0, 'T' }, {"verbose", no_argument, 0, 'v' }, {"disable-deblocking", no_argument, &disable_deblocking, 1 }, {"disable-sao", no_argument, &disable_sao, 1 }, {0, 0, 0, 0 } }; static void write_picture(const de265_image* img) { static FILE* fh = NULL; if (fh==NULL) { if (strcmp(output_filename, "-") == 0) { fh = stdout; } else { fh = fopen(output_filename, "wb"); } } for (int c=0;c<3;c++) { int stride; const uint8_t* p = de265_get_image_plane(img, c, &stride); int width = de265_get_image_width(img,c); if (de265_get_bits_per_pixel(img,c)<=8) { // --- save 8 bit YUV --- for (int y=0;y> 8; } fwrite(buf, width*2, 1, fh); } delete[] buf; } } fflush(fh); } #if HAVE_VIDEOGFX void display_image(const struct de265_image* img) { static X11Win win; // display picture static bool first=true; if (first) { first=false; win.Create(de265_get_image_width(img,0), de265_get_image_height(img,0), "de265 output"); } int width = de265_get_image_width(img,0); int height = de265_get_image_height(img,0); de265_chroma chroma = de265_get_chroma_format(img); ChromaFormat vgfx_chroma; Colorspace vgfx_cs = Colorspace_YUV; switch (chroma) { case de265_chroma_420: vgfx_chroma = Chroma_420; break; case de265_chroma_422: vgfx_chroma = Chroma_422; break; case de265_chroma_444: vgfx_chroma = Chroma_444; break; case de265_chroma_mono: vgfx_cs = Colorspace_Greyscale; break; } Image visu; visu.Create(width, height, vgfx_cs, vgfx_chroma); int nChannels = 3; if (chroma == de265_chroma_mono) { nChannels = 1; } for (int ch=0;ch> (bit_depth-8); } } } } win.Display(visu); win.WaitForKeypress(); } #endif static uint8_t* convert_to_8bit(const uint8_t* data, int width, int height, int pixelsPerLine, int bit_depth) { const uint16_t* data16 = (const uint16_t*)data; uint8_t* out = new uint8_t[pixelsPerLine*height]; for (int y=0;y> (bit_depth-8); } } return out; } #if HAVE_SDL SDL_YUV_Display sdlWin; bool sdl_active=false; bool display_sdl(const struct de265_image* img) { int width = de265_get_image_width(img,0); int height = de265_get_image_height(img,0); int chroma_width = de265_get_image_width(img,1); int chroma_height = de265_get_image_height(img,1); de265_chroma chroma = de265_get_chroma_format(img); if (!sdl_active) { sdl_active=true; enum SDL_YUV_Display::SDL_Chroma sdlChroma; switch (chroma) { case de265_chroma_420: sdlChroma = SDL_YUV_Display::SDL_CHROMA_420; break; case de265_chroma_422: sdlChroma = SDL_YUV_Display::SDL_CHROMA_422; break; case de265_chroma_444: sdlChroma = SDL_YUV_Display::SDL_CHROMA_444; break; case de265_chroma_mono: sdlChroma = SDL_YUV_Display::SDL_CHROMA_MONO; break; } sdlWin.init(width,height, sdlChroma); } int stride,chroma_stride; const uint8_t* y = de265_get_image_plane(img,0,&stride); const uint8_t* cb =de265_get_image_plane(img,1,&chroma_stride); const uint8_t* cr =de265_get_image_plane(img,2,NULL); int bpp_y = (de265_get_bits_per_pixel(img,0)+7)/8; int bpp_c = (de265_get_bits_per_pixel(img,1)+7)/8; int ppl_y = stride/bpp_y; int ppl_c = chroma_stride/bpp_c; uint8_t* y16 = NULL; uint8_t* cb16 = NULL; uint8_t* cr16 = NULL; int bd; if ((bd=de265_get_bits_per_pixel(img, 0)) > 8) { y16 = convert_to_8bit(y, width,height,ppl_y,bd); y=y16; } if (chroma != de265_chroma_mono) { if ((bd=de265_get_bits_per_pixel(img, 1)) > 8) { cb16 = convert_to_8bit(cb, chroma_width,chroma_height,ppl_c,bd); cb=cb16; } if ((bd=de265_get_bits_per_pixel(img, 2)) > 8) { cr16 = convert_to_8bit(cr, chroma_width,chroma_height,ppl_c,bd); cr=cr16; } } sdlWin.display(y,cb,cr, ppl_y, ppl_c); delete[] y16; delete[] cb16; delete[] cr16; return sdlWin.doQuit(); } #endif static int width,height; static uint32_t framecnt=0; bool output_image(const de265_image* img) { bool stop=false; width = de265_get_image_width(img,0); height = de265_get_image_height(img,0); framecnt++; //printf("SHOW POC: %d / PTS: %ld / integrity: %d\n",img->PicOrderCntVal, img->pts, img->integrity); if (0) { const char* nal_unit_name; int nuh_layer_id; int nuh_temporal_id; de265_get_image_NAL_header(img, NULL, &nal_unit_name, &nuh_layer_id, &nuh_temporal_id); printf("NAL: %s layer:%d temporal:%d\n",nal_unit_name, nuh_layer_id, nuh_temporal_id); } if (!quiet) { #if HAVE_SDL && HAVE_VIDEOGFX if (output_with_videogfx) { display_image(img); } else { stop = display_sdl(img); } #elif HAVE_SDL stop = display_sdl(img); #elif HAVE_VIDEOGFX display_image(img); #endif } if (write_yuv) { write_picture(img); } if ((framecnt%100)==0) { fprintf(stderr,"frame %d\r",framecnt); } if (framecnt>=max_frames) { stop=true; } return stop; } static double mse_y=0.0, mse_cb=0.0, mse_cr=0.0; static int mse_frames=0; static double ssim_y=0.0; static int ssim_frames=0; void measure(const de265_image* img) { // --- compute PSNR --- int width = de265_get_image_width(img,0); int height = de265_get_image_height(img,0); uint8_t* p = (uint8_t*)malloc(width*height*3/2); if (p == NULL) { return; } size_t toread = width*height*3/2; if (fread(p,1,toread,reference_file) != toread) { free(p); return; } int stride, cstride; const uint8_t* yptr = de265_get_image_plane(img,0, &stride); const uint8_t* cbptr = de265_get_image_plane(img,1, &cstride); const uint8_t* crptr = de265_get_image_plane(img,2, &cstride); double img_mse_y = MSE( yptr, stride, p, width, width, height); double img_mse_cb = MSE(cbptr, cstride, p+width*height, width/2, width/2,height/2); double img_mse_cr = MSE(crptr, cstride, p+width*height*5/4, width/2, width/2,height/2); mse_frames++; mse_y += img_mse_y; mse_cb += img_mse_cb; mse_cr += img_mse_cr; // --- compute SSIM --- double ssimSum = 0.0; #if HAVE_VIDEOGFX Bitmap ref, coded; ref .Create(width, height); // reference image coded.Create(width, height); // coded image const uint8_t* data; data = de265_get_image_plane(img,0,&stride); for (int y=0;y ssim = ssimAlgo.calcSSIM(ref,coded); Bitmap ssimMap; ssimMap.Create(width,height); for (int y=0;y error_map = CalcErrorMap(ref, coded, TransferCurve_Sqrt); // display PSNR error map if (show_psnr_map) { static X11Win win; static bool first=true; if (first) { first=false; win.Create(de265_get_image_width(img,0), de265_get_image_height(img,0), "psnr output"); } win.Display(MakeImage(error_map)); } // display SSIM error map if (show_ssim_map) { static X11Win win; static bool first=true; if (first) { first=false; win.Create(de265_get_image_width(img,0), de265_get_image_height(img,0), "ssim output"); } win.Display(MakeImage(ssimMap)); } #endif ssim_frames++; ssim_y += ssimSum; printf("%5d %6f %6f %6f %6f\n", framecnt, PSNR(img_mse_y), PSNR(img_mse_cb), PSNR(img_mse_cr), ssimSum); free(p); } #ifdef WIN32 #include #define WIN32_LEAN_AND_MEAN #include int gettimeofday(struct timeval *tp, void *) { time_t clock; struct tm tm; SYSTEMTIME wtm; GetLocalTime(&wtm); tm.tm_year = wtm.wYear - 1900; tm.tm_mon = wtm.wMonth - 1; tm.tm_mday = wtm.wDay; tm.tm_hour = wtm.wHour; tm.tm_min = wtm.wMinute; tm.tm_sec = wtm.wSecond; tm. tm_isdst = -1; clock = mktime(&tm); tp->tv_sec = (long) clock; tp->tv_usec = wtm.wMilliseconds * 1000; return (0); } #endif #ifdef HAVE___MALLOC_HOOK #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif static void *(*old_malloc_hook)(size_t, const void *); static void *new_malloc_hook(size_t size, const void *caller) { void *mem; /* if (size>1000000) { raise(SIGINT); } */ __malloc_hook = old_malloc_hook; mem = malloc(size); fprintf(stderr, "%p: malloc(%zu) = %p\n", caller, size, mem); __malloc_hook = new_malloc_hook; return mem; } static void init_my_hooks(void) { old_malloc_hook = __malloc_hook; __malloc_hook = new_malloc_hook; } #if DO_MEMORY_LOGGING void (*volatile __malloc_initialize_hook)(void) = init_my_hooks; #endif #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #endif int main(int argc, char** argv) { while (1) { int option_index = 0; int c = getopt_long(argc, argv, "qt:chf:o:dLB:n0vT:m:se" #if HAVE_VIDEOGFX && HAVE_SDL "V" #endif , long_options, &option_index); if (c == -1) break; switch (c) { case 'q': quiet++; break; case 't': nThreads=atoi(optarg); break; case 'c': check_hash=true; break; case 'f': max_frames=atoi(optarg); break; case 'o': write_yuv=true; output_filename=optarg; break; case 'h': show_help=true; break; case 'd': dump_headers=true; break; case 'n': nal_input=true; break; case 'V': output_with_videogfx=true; break; case 'L': logging=false; break; case '0': no_acceleration=true; break; case 'B': write_bytestream=true; bytestream_filename=optarg; break; case 'm': measure_quality=true; reference_filename=optarg; break; case 's': show_ssim_map=true; break; case 'e': show_psnr_map=true; break; case 'T': highestTID=atoi(optarg); break; case 'v': verbosity++; break; } } if (optind != argc-1 || show_help) { fprintf(stderr," dec265 v%s\n", de265_get_version()); fprintf(stderr,"--------------\n"); fprintf(stderr,"usage: dec265 [options] videofile.bin\n"); fprintf(stderr,"The video file must be a raw bitstream, or a stream with NAL units (option -n).\n"); fprintf(stderr,"\n"); fprintf(stderr,"options:\n"); fprintf(stderr," -q, --quiet do not show decoded image\n"); fprintf(stderr," -t, --threads N set number of worker threads (0 - no threading)\n"); fprintf(stderr," -c, --check-hash perform hash check\n"); fprintf(stderr," -n, --nal input is a stream with 4-byte length prefixed NAL units\n"); fprintf(stderr," -f, --frames N set number of frames to process\n"); fprintf(stderr," -o, --output write YUV reconstruction\n"); fprintf(stderr," -d, --dump dump headers\n"); #if HAVE_VIDEOGFX && HAVE_SDL fprintf(stderr," -V, --videogfx output with videogfx instead of SDL\n"); #endif fprintf(stderr," -0, --noaccel do not use any accelerated code (SSE)\n"); fprintf(stderr," -v, --verbose increase verbosity level (up to 3 times)\n"); fprintf(stderr," -L, --no-logging disable logging\n"); fprintf(stderr," -B, --write-bytestream FILENAME write raw bytestream (from NAL input)\n"); fprintf(stderr," -m, --measure YUV compute PSNRs relative to reference YUV\n"); #if HAVE_VIDEOGFX fprintf(stderr," -s, --ssim show SSIM-map (only when -m active)\n"); fprintf(stderr," -e, --errmap show error-map (only when -m active)\n"); #endif fprintf(stderr," -T, --highest-TID select highest temporal sublayer to decode\n"); fprintf(stderr," --disable-deblocking disable deblocking filter\n"); fprintf(stderr," --disable-sao disable sample-adaptive offset filter\n"); fprintf(stderr," -h, --help show help\n"); exit(show_help ? 0 : 5); } de265_error err =DE265_OK; de265_decoder_context* ctx = de265_new_decoder(); de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_BOOL_SEI_CHECK_HASH, check_hash); de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_SUPPRESS_FAULTY_PICTURES, false); de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_DISABLE_DEBLOCKING, disable_deblocking); de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_DISABLE_SAO, disable_sao); if (dump_headers) { de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_SPS_HEADERS, 1); de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_VPS_HEADERS, 1); de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_PPS_HEADERS, 1); de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_SLICE_HEADERS, 1); } if (no_acceleration) { de265_set_parameter_int(ctx, DE265_DECODER_PARAM_ACCELERATION_CODE, de265_acceleration_SCALAR); } if (!logging) { de265_disable_logging(); } de265_set_verbosity(verbosity); if (argc>=3) { if (nThreads>0) { err = de265_start_worker_threads(ctx, nThreads); } } de265_set_limit_TID(ctx, highestTID); if (measure_quality) { reference_file = fopen(reference_filename, "rb"); } FILE* fh; if (strcmp(argv[optind],"-")==0) { fh = stdin; } else { fh = fopen(argv[optind], "rb"); } if (fh==NULL) { fprintf(stderr,"cannot open file %s!\n", argv[optind]); exit(10); } FILE* bytestream_fh = NULL; if (write_bytestream) { bytestream_fh = fopen(bytestream_filename, "wb"); } bool stop=false; struct timeval tv_start; gettimeofday(&tv_start, NULL); int pos=0; while (!stop) { //tid = (framecnt/1000) & 1; //de265_set_limit_TID(ctx, tid); if (nal_input) { uint8_t len[4]; int n = fread(len,1,4,fh); int length = (len[0]<<24) + (len[1]<<16) + (len[2]<<8) + len[3]; uint8_t* buf = (uint8_t*)malloc(length); n = fread(buf,1,length,fh); err = de265_push_NAL(ctx, buf,n, pos, (void*)1); if (write_bytestream) { uint8_t sc[3] = { 0,0,1 }; fwrite(sc ,1,3,bytestream_fh); fwrite(buf,1,n,bytestream_fh); } free(buf); pos+=n; } else { // read a chunk of input data uint8_t buf[BUFFER_SIZE]; int n = fread(buf,1,BUFFER_SIZE,fh); // decode input data if (n) { err = de265_push_data(ctx, buf, n, pos, (void*)2); if (err != DE265_OK) { break; } } pos+=n; if (0) { // fake skipping if (pos>1000000) { printf("RESET\n"); de265_reset(ctx); pos=0; fseek(fh,-200000,SEEK_CUR); } } } // printf("pending data: %d\n", de265_get_number_of_input_bytes_pending(ctx)); if (feof(fh)) { err = de265_flush_data(ctx); // indicate end of stream stop = true; } // decoding / display loop int more=1; while (more) { more = 0; // decode some more err = de265_decode(ctx, &more); if (err != DE265_OK) { // if (quiet<=1) fprintf(stderr,"ERROR: %s\n", de265_get_error_text(err)); if (check_hash && err == DE265_ERROR_CHECKSUM_MISMATCH) stop = 1; more = 0; break; } // show available images const de265_image* img = de265_get_next_picture(ctx); if (img) { if (measure_quality) { measure(img); } stop = output_image(img); if (stop) more=0; else more=1; } // show warnings for (;;) { de265_error warning = de265_get_warning(ctx); if (warning==DE265_OK) { break; } if (quiet<=1) fprintf(stderr,"WARNING: %s\n", de265_get_error_text(warning)); } } } fclose(fh); if (write_bytestream) { fclose(bytestream_fh); } if (measure_quality) { printf("#total %6f %6f %6f %6f\n", PSNR(mse_y /mse_frames), PSNR(mse_cb/mse_frames), PSNR(mse_cr/mse_frames), ssim_y/ssim_frames); fclose(reference_file); } de265_free_decoder(ctx); struct timeval tv_end; gettimeofday(&tv_end, NULL); if (err != DE265_OK) { if (quiet<=1) fprintf(stderr,"decoding error: %s (code=%d)\n", de265_get_error_text(err), err); } double secs = tv_end.tv_sec-tv_start.tv_sec; secs += (tv_end.tv_usec - tv_start.tv_usec)*0.001*0.001; if (quiet<=1) fprintf(stderr,"nFrames decoded: %d (%dx%d @ %5.2f fps)\n",framecnt, width,height,framecnt/secs); return err==DE265_OK ? 0 : 10; }