1 /*
2  * libde265 example application "dec265".
3  * Copyright (c) 2013-2014 struktur AG, Dirk Farin <farin@struktur.de>
4  *
5  * This file is part of dec265, an example application using libde265.
6  *
7  * dec265 is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * dec265 is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with dec265.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #define DO_MEMORY_LOGGING 0
22 
23 #include "de265.h"
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <limits>
30 #include <getopt.h>
31 #ifdef HAVE_MALLOC_H
32 #include <malloc.h>
33 #endif
34 #include <signal.h>
35 
36 #ifndef _MSC_VER
37 #include <sys/time.h>
38 #include <unistd.h>
39 #endif
40 
41 #include "libde265/quality.h"
42 
43 #if HAVE_VIDEOGFX
44 #include <libvideogfx.hh>
45 using namespace videogfx;
46 #endif
47 
48 #if HAVE_SDL
49 #include "sdl.hh"
50 #endif
51 
52 
53 #define BUFFER_SIZE 40960
54 #define NUM_THREADS 4
55 
56 int nThreads=0;
57 bool nal_input=false;
58 int quiet=0;
59 bool check_hash=false;
60 bool show_help=false;
61 bool dump_headers=false;
62 bool write_yuv=false;
63 bool output_with_videogfx=false;
64 bool logging=true;
65 bool no_acceleration=false;
66 const char *output_filename = "out.yuv";
67 uint32_t max_frames=UINT32_MAX;
68 bool write_bytestream=false;
69 const char *bytestream_filename;
70 bool measure_quality=false;
71 bool show_ssim_map=false;
72 bool show_psnr_map=false;
73 const char* reference_filename;
74 FILE* reference_file;
75 int highestTID = 100;
76 int verbosity=0;
77 int disable_deblocking=0;
78 int disable_sao=0;
79 
80 static struct option long_options[] = {
81   {"quiet",      no_argument,       0, 'q' },
82   {"threads",    required_argument, 0, 't' },
83   {"check-hash", no_argument,       0, 'c' },
84   {"profile",    no_argument,       0, 'p' },
85   {"frames",     required_argument, 0, 'f' },
86   {"output",     required_argument, 0, 'o' },
87   {"dump",       no_argument,       0, 'd' },
88   {"nal",        no_argument,       0, 'n' },
89   {"videogfx",   no_argument,       0, 'V' },
90   {"no-logging", no_argument,       0, 'L' },
91   {"help",       no_argument,       0, 'h' },
92   {"noaccel",    no_argument,       0, '0' },
93   {"write-bytestream", required_argument,0, 'B' },
94   {"measure",     required_argument, 0, 'm' },
95   {"ssim",        no_argument,       0, 's' },
96   {"errmap",      no_argument,       0, 'e' },
97   {"highest-TID", required_argument, 0, 'T' },
98   {"verbose",    no_argument,       0, 'v' },
99   {"disable-deblocking", no_argument, &disable_deblocking, 1 },
100   {"disable-sao",        no_argument, &disable_sao, 1 },
101   {0,         0,                 0,  0 }
102 };
103 
104 
105 
write_picture(const de265_image * img)106 static void write_picture(const de265_image* img)
107 {
108   static FILE* fh = NULL;
109   if (fh==NULL) { fh = fopen(output_filename, "wb"); }
110 
111 
112 
113   for (int c=0;c<3;c++) {
114     int stride;
115     const uint8_t* p = de265_get_image_plane(img, c, &stride);
116     int width = de265_get_image_width(img,c);
117 
118     if (de265_get_bits_per_pixel(img,c)<=8) {
119       // --- save 8 bit YUV ---
120 
121       for (int y=0;y<de265_get_image_height(img,c);y++) {
122         fwrite(p + y*stride, width, 1, fh);
123       }
124     }
125     else {
126       // --- save 16 bit YUV ---
127 
128       uint8_t* buf = new uint8_t[width*2];
129       uint16_t* p16 = (uint16_t*)p;
130 
131       for (int y=0;y<de265_get_image_height(img,c);y++) {
132         for (int x=0;x<width;x++) {
133           uint16_t pixel_value = (p16+y*stride)[x];
134           buf[2*x+0] = pixel_value & 0xFF;
135           buf[2*x+1] = pixel_value >> 8;
136         }
137 
138         fwrite(buf, width*2, 1, fh);
139       }
140 
141       delete[] buf;
142     }
143   }
144 
145   fflush(fh);
146 }
147 
148 
149 
150 #if HAVE_VIDEOGFX
display_image(const struct de265_image * img)151 void display_image(const struct de265_image* img)
152 {
153   static X11Win win;
154 
155   // display picture
156 
157   static bool first=true;
158 
159   if (first) {
160     first=false;
161     win.Create(de265_get_image_width(img,0),
162                de265_get_image_height(img,0),
163                "de265 output");
164   }
165 
166 
167 
168   int width  = de265_get_image_width(img,0);
169   int height = de265_get_image_height(img,0);
170   de265_chroma chroma = de265_get_chroma_format(img);
171 
172   ChromaFormat vgfx_chroma;
173   Colorspace   vgfx_cs = Colorspace_YUV;
174 
175   switch (chroma) {
176   case de265_chroma_420:  vgfx_chroma = Chroma_420; break;
177   case de265_chroma_422:  vgfx_chroma = Chroma_422; break;
178   case de265_chroma_444:  vgfx_chroma = Chroma_444; break;
179   case de265_chroma_mono: vgfx_cs = Colorspace_Greyscale; break;
180   }
181 
182   Image<Pixel> visu;
183   visu.Create(width, height, vgfx_cs, vgfx_chroma);
184 
185   int nChannels = 3;
186   if (chroma == de265_chroma_mono) {
187     nChannels = 1;
188   }
189 
190   for (int ch=0;ch<nChannels;ch++) {
191     const uint8_t* data;
192     int stride;
193 
194     data   = de265_get_image_plane(img,ch,&stride);
195     width  = de265_get_image_width(img,ch);
196     height = de265_get_image_height(img,ch);
197 
198     int bit_depth = de265_get_bits_per_pixel(img,ch);
199 
200     if (bit_depth==8) {
201       for (int y=0;y<height;y++) {
202         memcpy(visu.AskFrame((BitmapChannel)ch)[y], data + y*stride, width);
203       }
204     }
205     else {
206       const uint16_t* data16 = (const uint16_t*)data;
207       for (int y=0;y<height;y++) {
208         for (int x=0;x<width;x++) {
209           visu.AskFrame((BitmapChannel)ch)[y][x] = *(data16 + y*stride +x) >> (bit_depth-8);
210         }
211       }
212     }
213   }
214 
215   win.Display(visu);
216   win.WaitForKeypress();
217 }
218 #endif
219 
convert_to_8bit(const uint8_t * data,int width,int height,int stride,int bit_depth)220 static uint8_t* convert_to_8bit(const uint8_t* data, int width, int height, int stride, int bit_depth)
221 {
222   const uint16_t* data16 = (const uint16_t*)data;
223   uint8_t* out = new uint8_t[stride*height];
224 
225   for (int y=0;y<height;y++) {
226     for (int x=0;x<width;x++) {
227       out[y*stride + x] = *(data16 + y*stride +x) >> (bit_depth-8);
228     }
229   }
230 
231   return out;
232 }
233 
234 
235 #if HAVE_SDL
236 SDL_YUV_Display sdlWin;
237 bool sdl_active=false;
238 
display_sdl(const struct de265_image * img)239 bool display_sdl(const struct de265_image* img)
240 {
241   int width  = de265_get_image_width(img,0);
242   int height = de265_get_image_height(img,0);
243 
244   int chroma_width  = de265_get_image_width(img,1);
245   int chroma_height = de265_get_image_height(img,1);
246 
247   de265_chroma chroma = de265_get_chroma_format(img);
248 
249   if (!sdl_active) {
250     sdl_active=true;
251     enum SDL_YUV_Display::SDL_Chroma sdlChroma;
252     switch (chroma) {
253     case de265_chroma_420:  sdlChroma = SDL_YUV_Display::SDL_CHROMA_420;  break;
254     case de265_chroma_422:  sdlChroma = SDL_YUV_Display::SDL_CHROMA_422;  break;
255     case de265_chroma_444:  sdlChroma = SDL_YUV_Display::SDL_CHROMA_444;  break;
256     case de265_chroma_mono: sdlChroma = SDL_YUV_Display::SDL_CHROMA_MONO; break;
257     }
258 
259     sdlWin.init(width,height, sdlChroma);
260   }
261 
262   int stride,chroma_stride;
263   const uint8_t* y = de265_get_image_plane(img,0,&stride);
264   const uint8_t* cb =de265_get_image_plane(img,1,&chroma_stride);
265   const uint8_t* cr =de265_get_image_plane(img,2,NULL);
266 
267   uint8_t* y16  = NULL;
268   uint8_t* cb16 = NULL;
269   uint8_t* cr16 = NULL;
270   int bd;
271 
272   if ((bd=de265_get_bits_per_pixel(img, 0)) > 8) {
273     y16  = convert_to_8bit(y,  width,height,stride,bd); y=y16;
274   }
275 
276   if (chroma != de265_chroma_mono) {
277     if ((bd=de265_get_bits_per_pixel(img, 1)) > 8) {
278       cb16 = convert_to_8bit(cb, chroma_width,chroma_height,chroma_stride,bd); cb=cb16;
279     }
280     if ((bd=de265_get_bits_per_pixel(img, 2)) > 8) {
281       cr16 = convert_to_8bit(cr, chroma_width,chroma_height,chroma_stride,bd); cr=cr16;
282     }
283   }
284 
285   sdlWin.display(y,cb,cr, stride, chroma_stride);
286 
287   delete[] y16;
288   delete[] cb16;
289   delete[] cr16;
290 
291   return sdlWin.doQuit();
292 }
293 #endif
294 
295 
296 static int width,height;
297 static uint32_t framecnt=0;
298 
output_image(const de265_image * img)299 bool output_image(const de265_image* img)
300 {
301   bool stop=false;
302 
303   width  = de265_get_image_width(img,0);
304   height = de265_get_image_height(img,0);
305 
306   framecnt++;
307   //printf("SHOW POC: %d / PTS: %ld / integrity: %d\n",img->PicOrderCntVal, img->pts, img->integrity);
308 
309 
310   if (0) {
311     const char* nal_unit_name;
312     int nuh_layer_id;
313     int nuh_temporal_id;
314     de265_get_image_NAL_header(img, NULL, &nal_unit_name, &nuh_layer_id, &nuh_temporal_id);
315 
316     printf("NAL: %s layer:%d temporal:%d\n",nal_unit_name, nuh_layer_id, nuh_temporal_id);
317   }
318 
319 
320   if (!quiet) {
321 #if HAVE_SDL && HAVE_VIDEOGFX
322     if (output_with_videogfx) {
323       display_image(img);
324     } else {
325       stop = display_sdl(img);
326     }
327 #elif HAVE_SDL
328     stop = display_sdl(img);
329 #elif HAVE_VIDEOGFX
330     display_image(img);
331 #endif
332   }
333   if (write_yuv) {
334     write_picture(img);
335   }
336 
337   if ((framecnt%100)==0) {
338     fprintf(stderr,"frame %d\r",framecnt);
339   }
340 
341   if (framecnt>=max_frames) {
342     stop=true;
343   }
344 
345   return stop;
346 }
347 
348 
349 static double mse_y=0.0, mse_cb=0.0, mse_cr=0.0;
350 static int    mse_frames=0;
351 
352 static double ssim_y=0.0;
353 static int    ssim_frames=0;
354 
measure(const de265_image * img)355 void measure(const de265_image* img)
356 {
357   // --- compute PSNR ---
358 
359   int width  = de265_get_image_width(img,0);
360   int height = de265_get_image_height(img,0);
361 
362   uint8_t* p = (uint8_t*)malloc(width*height*3/2);
363   if (p == NULL) {
364     return;
365   }
366 
367   size_t toread = width*height*3/2;
368   if (fread(p,1,toread,reference_file) != toread) {
369     free(p);
370     return;
371   }
372 
373   int stride, cstride;
374   const uint8_t* yptr  = de265_get_image_plane(img,0, &stride);
375   const uint8_t* cbptr = de265_get_image_plane(img,1, &cstride);
376   const uint8_t* crptr = de265_get_image_plane(img,2, &cstride);
377 
378   double img_mse_y  = MSE( yptr,  stride, p, width,   width, height);
379   double img_mse_cb = MSE(cbptr, cstride, p+width*height,      width/2, width/2,height/2);
380   double img_mse_cr = MSE(crptr, cstride, p+width*height*5/4,  width/2, width/2,height/2);
381 
382   mse_frames++;
383 
384   mse_y  += img_mse_y;
385   mse_cb += img_mse_cb;
386   mse_cr += img_mse_cr;
387 
388 
389 
390   // --- compute SSIM ---
391 
392   double ssimSum = 0.0;
393 
394 #if HAVE_VIDEOGFX
395   Bitmap<Pixel> ref, coded;
396   ref  .Create(width, height); // reference image
397   coded.Create(width, height); // coded image
398 
399   const uint8_t* data;
400   data = de265_get_image_plane(img,0,&stride);
401 
402   for (int y=0;y<height;y++) {
403     memcpy(coded[y], data + y*stride, width);
404     memcpy(ref[y],   p    + y*stride, width);
405   }
406 
407   SSIM ssimAlgo;
408   Bitmap<float> ssim = ssimAlgo.calcSSIM(ref,coded);
409 
410   Bitmap<Pixel> ssimMap;
411   ssimMap.Create(width,height);
412 
413   for (int y=0;y<height;y++)
414     for (int x=0;x<width;x++)
415       {
416         float v = ssim[y][x];
417         ssimSum += v;
418         v = v*v;
419         v = 255*v; //pow(v, 20);
420 
421         //assert(v<=255.0);
422         ssimMap[y][x] = v;
423       }
424 
425   ssimSum /= width*height;
426 
427 
428   Bitmap<Pixel> error_map = CalcErrorMap(ref, coded, TransferCurve_Sqrt);
429 
430 
431   // display PSNR error map
432 
433   if (show_psnr_map) {
434     static X11Win win;
435     static bool first=true;
436 
437     if (first) {
438       first=false;
439       win.Create(de265_get_image_width(img,0),
440                  de265_get_image_height(img,0),
441                  "psnr output");
442     }
443 
444     win.Display(MakeImage(error_map));
445   }
446 
447 
448   // display SSIM error map
449 
450   if (show_ssim_map) {
451     static X11Win win;
452     static bool first=true;
453 
454     if (first) {
455       first=false;
456       win.Create(de265_get_image_width(img,0),
457                  de265_get_image_height(img,0),
458                  "ssim output");
459     }
460 
461     win.Display(MakeImage(ssimMap));
462   }
463 #endif
464 
465   ssim_frames++;
466   ssim_y += ssimSum;
467 
468   printf("%5d   %6f %6f %6f %6f\n",
469          framecnt,
470          PSNR(img_mse_y), PSNR(img_mse_cb), PSNR(img_mse_cr),
471          ssimSum);
472 
473   free(p);
474 }
475 
476 
477 #ifdef WIN32
478 #include <time.h>
479 #define WIN32_LEAN_AND_MEAN
480 #include <winsock.h>
gettimeofday(struct timeval * tp,void *)481 int gettimeofday(struct timeval *tp, void *)
482 {
483     time_t clock;
484     struct tm tm;
485     SYSTEMTIME wtm;
486 
487     GetLocalTime(&wtm);
488     tm.tm_year      = wtm.wYear - 1900;
489     tm.tm_mon       = wtm.wMonth - 1;
490     tm.tm_mday      = wtm.wDay;
491     tm.tm_hour      = wtm.wHour;
492     tm.tm_min       = wtm.wMinute;
493     tm.tm_sec       = wtm.wSecond;
494     tm. tm_isdst    = -1;
495     clock = mktime(&tm);
496     tp->tv_sec = (long) clock;
497     tp->tv_usec = wtm.wMilliseconds * 1000;
498 
499     return (0);
500 }
501 #endif
502 
503 #ifdef HAVE___MALLOC_HOOK
504 #ifdef __GNUC__
505 #pragma GCC diagnostic push
506 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
507 #endif
508 static void *(*old_malloc_hook)(size_t, const void *);
509 
new_malloc_hook(size_t size,const void * caller)510 static void *new_malloc_hook(size_t size, const void *caller) {
511   void *mem;
512 
513   /*
514   if (size>1000000) {
515     raise(SIGINT);
516   }
517   */
518 
519   __malloc_hook = old_malloc_hook;
520   mem = malloc(size);
521   fprintf(stderr, "%p: malloc(%zu) = %p\n", caller, size, mem);
522   __malloc_hook = new_malloc_hook;
523 
524   return mem;
525 }
526 
init_my_hooks(void)527 static void init_my_hooks(void) {
528   old_malloc_hook = __malloc_hook;
529   __malloc_hook = new_malloc_hook;
530 }
531 
532 #if DO_MEMORY_LOGGING
533 void (*volatile __malloc_initialize_hook)(void) = init_my_hooks;
534 #endif
535 #ifdef __GNUC__
536 #pragma GCC diagnostic pop
537 #endif
538 #endif
539 
540 
main(int argc,char ** argv)541 int main(int argc, char** argv)
542 {
543   while (1) {
544     int option_index = 0;
545 
546     int c = getopt_long(argc, argv, "qt:chf:o:dLB:n0vT:m:se"
547 #if HAVE_VIDEOGFX && HAVE_SDL
548                         "V"
549 #endif
550                         , long_options, &option_index);
551     if (c == -1)
552       break;
553 
554     switch (c) {
555     case 'q': quiet++; break;
556     case 't': nThreads=atoi(optarg); break;
557     case 'c': check_hash=true; break;
558     case 'f': max_frames=atoi(optarg); break;
559     case 'o': write_yuv=true; output_filename=optarg; break;
560     case 'h': show_help=true; break;
561     case 'd': dump_headers=true; break;
562     case 'n': nal_input=true; break;
563     case 'V': output_with_videogfx=true; break;
564     case 'L': logging=false; break;
565     case '0': no_acceleration=true; break;
566     case 'B': write_bytestream=true; bytestream_filename=optarg; break;
567     case 'm': measure_quality=true; reference_filename=optarg; break;
568     case 's': show_ssim_map=true; break;
569     case 'e': show_psnr_map=true; break;
570     case 'T': highestTID=atoi(optarg); break;
571     case 'v': verbosity++; break;
572     }
573   }
574 
575   if (optind != argc-1 || show_help) {
576     fprintf(stderr," dec265  v%s\n", de265_get_version());
577     fprintf(stderr,"--------------\n");
578     fprintf(stderr,"usage: dec265 [options] videofile.bin\n");
579     fprintf(stderr,"The video file must be a raw bitstream, or a stream with NAL units (option -n).\n");
580     fprintf(stderr,"\n");
581     fprintf(stderr,"options:\n");
582     fprintf(stderr,"  -q, --quiet       do not show decoded image\n");
583     fprintf(stderr,"  -t, --threads N   set number of worker threads (0 - no threading)\n");
584     fprintf(stderr,"  -c, --check-hash  perform hash check\n");
585     fprintf(stderr,"  -n, --nal         input is a stream with 4-byte length prefixed NAL units\n");
586     fprintf(stderr,"  -f, --frames N    set number of frames to process\n");
587     fprintf(stderr,"  -o, --output      write YUV reconstruction\n");
588     fprintf(stderr,"  -d, --dump        dump headers\n");
589 #if HAVE_VIDEOGFX && HAVE_SDL
590     fprintf(stderr,"  -V, --videogfx    output with videogfx instead of SDL\n");
591 #endif
592     fprintf(stderr,"  -0, --noaccel     do not use any accelerated code (SSE)\n");
593     fprintf(stderr,"  -v, --verbose     increase verbosity level (up to 3 times)\n");
594     fprintf(stderr,"  -L, --no-logging  disable logging\n");
595     fprintf(stderr,"  -B, --write-bytestream FILENAME  write raw bytestream (from NAL input)\n");
596     fprintf(stderr,"  -m, --measure YUV compute PSNRs relative to reference YUV\n");
597 #if HAVE_VIDEOGFX
598     fprintf(stderr,"  -s, --ssim        show SSIM-map (only when -m active)\n");
599     fprintf(stderr,"  -e, --errmap      show error-map (only when -m active)\n");
600 #endif
601     fprintf(stderr,"  -T, --highest-TID select highest temporal sublayer to decode\n");
602     fprintf(stderr,"      --disable-deblocking   disable deblocking filter\n");
603     fprintf(stderr,"      --disable-sao          disable sample-adaptive offset filter\n");
604     fprintf(stderr,"  -h, --help        show help\n");
605 
606     exit(show_help ? 0 : 5);
607   }
608 
609 
610   de265_error err =DE265_OK;
611 
612   de265_decoder_context* ctx = de265_new_decoder();
613 
614   de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_BOOL_SEI_CHECK_HASH, check_hash);
615   de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_SUPPRESS_FAULTY_PICTURES, false);
616 
617   de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_DISABLE_DEBLOCKING, disable_deblocking);
618   de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_DISABLE_SAO, disable_sao);
619 
620   if (dump_headers) {
621     de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_SPS_HEADERS, 1);
622     de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_VPS_HEADERS, 1);
623     de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_PPS_HEADERS, 1);
624     de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_SLICE_HEADERS, 1);
625   }
626 
627   if (no_acceleration) {
628     de265_set_parameter_int(ctx, DE265_DECODER_PARAM_ACCELERATION_CODE, de265_acceleration_SCALAR);
629   }
630 
631   if (!logging) {
632     de265_disable_logging();
633   }
634 
635   de265_set_verbosity(verbosity);
636 
637 
638   if (argc>=3) {
639     if (nThreads>0) {
640       err = de265_start_worker_threads(ctx, nThreads);
641     }
642   }
643 
644   de265_set_limit_TID(ctx, highestTID);
645 
646 
647   if (measure_quality) {
648     reference_file = fopen(reference_filename, "rb");
649   }
650 
651 
652   FILE* fh = fopen(argv[optind], "rb");
653   if (fh==NULL) {
654     fprintf(stderr,"cannot open file %s!\n", argv[1]);
655     exit(10);
656   }
657 
658   FILE* bytestream_fh = NULL;
659 
660   if (write_bytestream) {
661     bytestream_fh = fopen(bytestream_filename, "wb");
662   }
663 
664   bool stop=false;
665 
666   struct timeval tv_start;
667   gettimeofday(&tv_start, NULL);
668 
669   int pos=0;
670 
671   while (!stop)
672     {
673       //tid = (framecnt/1000) & 1;
674       //de265_set_limit_TID(ctx, tid);
675 
676       if (nal_input) {
677         uint8_t len[4];
678         int n = fread(len,1,4,fh);
679         int length = (len[0]<<24) + (len[1]<<16) + (len[2]<<8) + len[3];
680 
681         uint8_t* buf = (uint8_t*)malloc(length);
682         n = fread(buf,1,length,fh);
683         err = de265_push_NAL(ctx, buf,n,  pos, (void*)1);
684 
685         if (write_bytestream) {
686           uint8_t sc[3] = { 0,0,1 };
687           fwrite(sc ,1,3,bytestream_fh);
688           fwrite(buf,1,n,bytestream_fh);
689         }
690 
691         free(buf);
692         pos+=n;
693       }
694       else {
695         // read a chunk of input data
696         uint8_t buf[BUFFER_SIZE];
697         int n = fread(buf,1,BUFFER_SIZE,fh);
698 
699         // decode input data
700         if (n) {
701           err = de265_push_data(ctx, buf, n, pos, (void*)2);
702           if (err != DE265_OK) {
703             break;
704           }
705         }
706 
707         pos+=n;
708 
709         if (0) { // fake skipping
710           if (pos>1000000) {
711             printf("RESET\n");
712             de265_reset(ctx);
713             pos=0;
714 
715             fseek(fh,-200000,SEEK_CUR);
716           }
717         }
718       }
719 
720       // printf("pending data: %d\n", de265_get_number_of_input_bytes_pending(ctx));
721 
722       if (feof(fh)) {
723         err = de265_flush_data(ctx); // indicate end of stream
724         stop = true;
725       }
726 
727 
728       // decoding / display loop
729 
730       int more=1;
731       while (more)
732         {
733           more = 0;
734 
735           // decode some more
736 
737           err = de265_decode(ctx, &more);
738           if (err != DE265_OK) {
739             // if (quiet<=1) fprintf(stderr,"ERROR: %s\n", de265_get_error_text(err));
740 
741             if (check_hash && err == DE265_ERROR_CHECKSUM_MISMATCH)
742               stop = 1;
743             more = 0;
744             break;
745           }
746 
747           // show available images
748 
749           const de265_image* img = de265_get_next_picture(ctx);
750           if (img) {
751             if (measure_quality) {
752               measure(img);
753             }
754 
755             stop = output_image(img);
756             if (stop) more=0;
757             else      more=1;
758           }
759 
760           // show warnings
761 
762           for (;;) {
763             de265_error warning = de265_get_warning(ctx);
764             if (warning==DE265_OK) {
765               break;
766             }
767 
768             if (quiet<=1) fprintf(stderr,"WARNING: %s\n", de265_get_error_text(warning));
769           }
770         }
771     }
772 
773   fclose(fh);
774 
775   if (write_bytestream) {
776     fclose(bytestream_fh);
777   }
778 
779   if (measure_quality) {
780     printf("#total  %6f %6f %6f %6f\n",
781            PSNR(mse_y /mse_frames),
782            PSNR(mse_cb/mse_frames),
783            PSNR(mse_cr/mse_frames),
784            ssim_y/ssim_frames);
785 
786     fclose(reference_file);
787   }
788 
789   de265_free_decoder(ctx);
790 
791   struct timeval tv_end;
792   gettimeofday(&tv_end, NULL);
793 
794   if (err != DE265_OK) {
795     if (quiet<=1) fprintf(stderr,"decoding error: %s (code=%d)\n", de265_get_error_text(err), err);
796   }
797 
798   double secs = tv_end.tv_sec-tv_start.tv_sec;
799   secs += (tv_end.tv_usec - tv_start.tv_usec)*0.001*0.001;
800 
801   if (quiet<=1) fprintf(stderr,"nFrames decoded: %d (%dx%d @ %5.2f fps)\n",framecnt,
802                         width,height,framecnt/secs);
803 
804 
805   return err==DE265_OK ? 0 : 10;
806 }
807