Add c64x changes for r17563, as they were accidentally omitted.
[theora.git] / examples / dump_video.c
1 /********************************************************************
2  *                                                                  *
3  * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE.   *
4  * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
5  * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
6  * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
7  *                                                                  *
8  * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009                *
9  * by the Xiph.Org Foundation http://www.xiph.org/                  *
10  *                                                                  *
11  ********************************************************************
12
13   function: example dumpvid application; dumps  Theora streams
14   last mod: $Id: dump_video.c,v 1.2 2004/03/24 19:12:42 derf Exp $
15
16  ********************************************************************/
17
18 /* By Mauricio Piacentini (mauricio at xiph.org) */
19 /*  simply dump decoded YUV data, for verification of theora bitstream */
20
21 #if !defined(_REENTRANT)
22 #define _REENTRANT
23 #endif
24 #if !defined(_GNU_SOURCE)
25 #define _GNU_SOURCE
26 #endif
27 #if !defined(_LARGEFILE_SOURCE)
28 #define _LARGEFILE_SOURCE
29 #endif
30 #if !defined(_LARGEFILE64_SOURCE)
31 #define _LARGEFILE64_SOURCE
32 #endif
33 #if !defined(_FILE_OFFSET_BITS)
34 #define _FILE_OFFSET_BITS 64
35 #endif
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/timeb.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 /*Yes, yes, we're going to hell.*/
44 #if defined(_WIN32)
45 #include <io.h>
46 #endif
47 #include <fcntl.h>
48 #include <limits.h>
49 #include <math.h>
50 #include <signal.h>
51 #include "getopt.h"
52 #include "theora/theoradec.h"
53
54 const char *optstring = "o:crf";
55 struct option options [] = {
56   {"output",required_argument,NULL,'o'},
57   {"crop",no_argument,NULL,'c'}, /*Crop down to the picture size.*/
58   {"raw",no_argument, NULL,'r'}, /*Disable YUV4MPEG2 headers:*/
59   {"fps-only",no_argument, NULL, 'f'}, /* Only interested in fps of decode loop */
60   {NULL,0,NULL,0}
61 };
62
63 /* Helper; just grab some more compressed bitstream and sync it for
64    page extraction */
65 int buffer_data(FILE *in,ogg_sync_state *oy){
66   char *buffer=ogg_sync_buffer(oy,4096);
67   int bytes=fread(buffer,1,4096,in);
68   ogg_sync_wrote(oy,bytes);
69   return(bytes);
70 }
71
72 /* never forget that globals are a one-way ticket to Hell */
73 /* Ogg and codec state for demux/decode */
74 ogg_sync_state    oy;
75 ogg_page          og;
76 ogg_stream_state  vo;
77 ogg_stream_state  to;
78 th_info           ti;
79 th_comment        tc;
80 th_setup_info    *ts;
81 th_dec_ctx       *td;
82
83 int              theora_p=0;
84 int              theora_processing_headers;
85 int              stateflag=0;
86
87 /* single frame video buffering */
88 int          videobuf_ready=0;
89 ogg_int64_t  videobuf_granulepos=-1;
90 double       videobuf_time=0;
91 int          raw=0;
92 int          crop=0;
93
94 FILE* outfile = NULL;
95
96 int got_sigint=0;
97 static void sigint_handler (int signal) {
98   got_sigint = 1;
99 }
100
101 static th_ycbcr_buffer ycbcr;
102
103 static void stripe_decoded(th_ycbcr_buffer _dst,th_ycbcr_buffer _src,
104  int _fragy0,int _fragy_end){
105   int pli;
106   for(pli=0;pli<3;pli++){
107     int yshift;
108     int y_end;
109     int y;
110     yshift=pli!=0&&!(ti.pixel_fmt&2);
111     y_end=_fragy_end<<3-yshift;
112     /*An implemention intending to display this data would need to check the
113        crop rectangle before proceeding.*/
114     for(y=_fragy0<<3-yshift;y<y_end;y++){
115       memcpy(_dst[pli].data+y*_dst[pli].stride,
116        _src[pli].data+y*_src[pli].stride,_src[pli].width);
117     }
118   }
119 }
120
121 static void open_video(void){
122   th_stripe_callback cb;
123   int                pli;
124   /*Here we allocate a buffer so we can use the striped decode feature.
125     There's no real reason to do this in this application, because we want to
126      write to the file top-down, but the frame gets decoded bottom up, so we
127      have to buffer it all anyway.
128     But this illustrates how the API works.*/
129   for(pli=0;pli<3;pli++){
130     int xshift;
131     int yshift;
132     xshift=pli!=0&&!(ti.pixel_fmt&1);
133     yshift=pli!=0&&!(ti.pixel_fmt&2);
134     ycbcr[pli].data=(unsigned char *)malloc(
135      (ti.frame_width>>xshift)*(ti.frame_height>>yshift)*sizeof(char));
136     ycbcr[pli].stride=ti.frame_width>>xshift;
137     ycbcr[pli].width=ti.frame_width>>xshift;
138     ycbcr[pli].height=ti.frame_height>>yshift;
139   }
140   /*Similarly, since ycbcr is a global, there's no real reason to pass it as
141      the context.
142     In a more object-oriented decoder, we could pass the "this" pointer
143      instead (though in C++, platform-dependent calling convention differences
144      prevent us from using a real member function pointer).*/
145   cb.ctx=ycbcr;
146   cb.stripe_decoded=(th_stripe_decoded_func)stripe_decoded;
147   th_decode_ctl(td,TH_DECCTL_SET_STRIPE_CB,&cb,sizeof(cb));
148 }
149
150 /*Write out the planar YUV frame, uncropped.*/
151 static void video_write(void){
152   int pli;
153   int i;
154   /*Uncomment the following to do normal, non-striped decoding.
155   th_ycbcr_buffer ycbcr;
156   th_decode_ycbcr_out(td,ycbcr);*/
157   if(outfile){
158     int x0;
159     int y0;
160     int xend;
161     int yend;
162     int hdec;
163     int vdec;
164     if(crop){
165       x0=ti.pic_x;
166       y0=ti.pic_y;
167       xend=x0+ti.pic_width;
168       yend=y0+ti.pic_height;
169     }
170     else{
171       x0=y0=0;
172       xend=ti.frame_width;
173       yend=ti.frame_height;
174     }
175     hdec=vdec=0;
176     if(!raw)fprintf(outfile, "FRAME\n");
177     for(pli=0;pli<3;pli++){
178       for(i=y0>>vdec;i<(yend+vdec>>vdec);i++){
179         fwrite(ycbcr[pli].data+ycbcr[pli].stride*i+(x0>>hdec), 1,
180          (xend+hdec>>hdec)-(x0>>hdec), outfile);
181       }
182       hdec=!(ti.pixel_fmt&1);
183       vdec=!(ti.pixel_fmt&2);
184     }
185   }
186 }
187
188 /* dump the theora comment header */
189 static int dump_comments(th_comment *_tc){
190   int   i;
191   int   len;
192   FILE *out;
193   out=stderr;
194   fprintf(out,"Encoded by %s\n",_tc->vendor);
195   if(_tc->comments){
196     fprintf(out,"theora comment header:\n");
197     for(i=0;i<_tc->comments;i++){
198       if(_tc->user_comments[i]){
199         len=_tc->comment_lengths[i]<INT_MAX?_tc->comment_lengths[i]:INT_MAX;
200         fprintf(out,"\t%.*s\n",len,_tc->user_comments[i]);
201       }
202     }
203   }
204   return 0;
205 }
206
207 /* helper: push a page into the appropriate steam */
208 /* this can be done blindly; a stream won't accept a page
209                 that doesn't belong to it */
210 static int queue_page(ogg_page *page){
211   if(theora_p)ogg_stream_pagein(&to,page);
212   return 0;
213 }
214
215 static void usage(void){
216   fprintf(stderr,
217    "Usage: dumpvid [options] [<infile.ogv>] [-o <outfile.y4m>]\n\n"
218    "If no input file is given, stdin is used.\n"
219    "Options:\n\n"
220    "  -o --output <outfile.y4m> File name for decoded output. If\n"
221    "                            this option is not given, the\n"
222    "                            decompressed data is sent to stdout.\n"
223    "  -c --crop                 Crop the output to the picture region.\n"
224    "                            By default, the entire encoded frame\n"
225    "                            is output, including the padding\n"
226    "                            require to make the image dimensions\n"
227    "                            a multiple of 16.\n"
228    "  -r --raw                  Output raw YUV with no framing instead\n"
229    "                            of YUV4MPEG2 (the default).\n"
230    "  -f --fps-only             Only report the decoding frame rate.\n");
231   exit(1);
232 }
233
234 int main(int argc,char *argv[]){
235
236   ogg_packet op;
237
238   int long_option_index;
239   int c;
240
241   struct timeb start;
242   struct timeb after;
243   struct timeb last;
244   int fps_only=0;
245   int frames = 0;
246
247   FILE *infile = stdin;
248   outfile = stdout;
249
250 #ifdef _WIN32 /* We need to set stdin/stdout to binary mode on windows. */
251   /* Beware the evil ifdef. We avoid these where we can, but this one we
252      cannot. Don't add any more, you'll probably go to hell if you do. */
253   _setmode( _fileno( stdin ), _O_BINARY );
254   _setmode( _fileno( stdout ), _O_BINARY );
255 #endif
256
257   /* Process option arguments. */
258   while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
259     switch(c){
260     case 'o':
261       if(strcmp(optarg,"-")!=0){
262         outfile=fopen(optarg,"wb");
263         if(outfile==NULL){
264           fprintf(stderr,"Unable to open output file '%s'\n", optarg);
265           exit(1);
266         }
267       }else{
268         outfile=stdout;
269       }
270       break;
271
272     case 'c':
273       crop=1;
274       break;
275
276     case 'r':
277       raw=1;
278       break;
279
280     case 'f':
281       fps_only = 1;
282       outfile = NULL;
283       break;
284
285     default:
286       usage();
287     }
288   }
289   if(optind<argc){
290     infile=fopen(argv[optind],"rb");
291     if(infile==NULL){
292       fprintf(stderr,"Unable to open '%s' for extraction.\n", argv[optind]);
293       exit(1);
294     }
295     if(++optind<argc){
296       usage();
297       exit(1);
298     }
299   }
300   /*Ok, Ogg parsing.
301     The idea here is we have a bitstream that is made up of Ogg pages.
302     The libogg sync layer will find them for us.
303     There may be pages from several logical streams interleaved; we find the
304      first theora stream and ignore any others.
305     Then we pass the pages for our stream to the libogg stream layer which
306      assembles our original set of packets out of them.
307     It's the packets that libtheora actually knows how to handle.*/
308
309   /* start up Ogg stream synchronization layer */
310   ogg_sync_init(&oy);
311
312   /* init supporting Theora structures needed in header parsing */
313   th_comment_init(&tc);
314   th_info_init(&ti);
315
316   /*Ogg file open; parse the headers.
317     Theora (like Vorbis) depends on some initial header packets for decoder
318      setup and initialization.
319     We retrieve these first before entering the main decode loop.*/
320
321   /* Only interested in Theora streams */
322   while(!stateflag){
323     int ret=buffer_data(infile,&oy);
324     if(ret==0)break;
325     while(ogg_sync_pageout(&oy,&og)>0){
326       int got_packet;
327       ogg_stream_state test;
328
329       /* is this a mandated initial header? If not, stop parsing */
330       if(!ogg_page_bos(&og)){
331         /* don't leak the page; get it into the appropriate stream */
332         queue_page(&og);
333         stateflag=1;
334         break;
335       }
336
337       ogg_stream_init(&test,ogg_page_serialno(&og));
338       ogg_stream_pagein(&test,&og);
339       got_packet = ogg_stream_packetpeek(&test,&op);
340
341       /* identify the codec: try theora */
342       if((got_packet==1) && !theora_p && (theora_processing_headers=
343        th_decode_headerin(&ti,&tc,&ts,&op))>=0){
344         /* it is theora -- save this stream state */
345         memcpy(&to,&test,sizeof(test));
346         theora_p=1;
347         /*Advance past the successfully processed header.*/
348         if(theora_processing_headers)ogg_stream_packetout(&to,NULL);
349       }else{
350         /* whatever it is, we don't care about it */
351         ogg_stream_clear(&test);
352       }
353     }
354     /* fall through to non-bos page parsing */
355   }
356
357   /* we're expecting more header packets. */
358   while(theora_p && theora_processing_headers){
359     int ret;
360
361     /* look for further theora headers */
362     while(theora_processing_headers&&(ret=ogg_stream_packetpeek(&to,&op))){
363       if(ret<0)continue;
364       theora_processing_headers=th_decode_headerin(&ti,&tc,&ts,&op);
365       if(theora_processing_headers<0){
366         fprintf(stderr,"Error parsing Theora stream headers; "
367          "corrupt stream?\n");
368         exit(1);
369       }
370       else if(theora_processing_headers>0){
371         /*Advance past the successfully processed header.*/
372         ogg_stream_packetout(&to,NULL);
373       }
374       theora_p++;
375     }
376
377     /*Stop now so we don't fail if there aren't enough pages in a short
378        stream.*/
379     if(!(theora_p && theora_processing_headers))break;
380
381     /* The header pages/packets will arrive before anything else we
382        care about, or the stream is not obeying spec */
383
384     if(ogg_sync_pageout(&oy,&og)>0){
385       queue_page(&og); /* demux into the appropriate stream */
386     }else{
387       int ret=buffer_data(infile,&oy); /* someone needs more data */
388       if(ret==0){
389         fprintf(stderr,"End of file while searching for codec headers.\n");
390         exit(1);
391       }
392     }
393   }
394
395   /* and now we have it all.  initialize decoders */
396   if(theora_p){
397     dump_comments(&tc);
398     td=th_decode_alloc(&ti,ts);
399     fprintf(stderr,"Ogg logical stream %lx is Theora %dx%d %.02f fps video\n"
400      "Encoded frame content is %dx%d with %dx%d offset\n",
401      to.serialno,ti.frame_width,ti.frame_height,
402      (double)ti.fps_numerator/ti.fps_denominator,
403      ti.pic_width,ti.pic_height,ti.pic_x,ti.pic_y);
404   }else{
405     /* tear down the partial theora setup */
406     th_info_clear(&ti);
407     th_comment_clear(&tc);
408   }
409   /*Either way, we're done with the codec setup data.*/
410   th_setup_free(ts);
411
412   /* open video */
413   if(theora_p)open_video();
414
415   if(!raw && outfile){
416     static const char *CHROMA_TYPES[4]={"420jpeg",NULL,"422jpeg","444"};
417     int width;
418     int height;
419     if(ti.pixel_fmt>=4||ti.pixel_fmt==TH_PF_RSVD){
420       fprintf(stderr,"Unknown pixel format: %i\n",ti.pixel_fmt);
421       exit(1);
422     }
423     if(crop){
424       int hdec;
425       int vdec;
426       hdec=!(ti.pixel_fmt&1);
427       vdec=!(ti.pixel_fmt&2);
428       if((ti.pic_x&hdec)||(ti.pic_width&hdec)
429        ||(ti.pic_y&vdec)||(ti.pic_height&vdec)){
430         fprintf(stderr,
431          "Error: Cropped images with odd offsets/sizes and chroma subsampling\n"
432          "cannot be output to YUV4MPEG2. Remove the --crop flag or add the\n"
433          "--raw flag.\n");
434         exit(1);
435       }
436       width=ti.pic_width;
437       height=ti.pic_height;
438     }
439     else{
440       width=ti.frame_width;
441       height=ti.frame_height;
442     }
443     fprintf(outfile,"YUV4MPEG2 C%s W%d H%d F%d:%d I%c A%d:%d\n",
444      CHROMA_TYPES[ti.pixel_fmt],width,height,
445      ti.fps_numerator,ti.fps_denominator,'p',
446      ti.aspect_numerator,ti.aspect_denominator);
447   }
448
449   /* install signal handler */
450   signal (SIGINT, sigint_handler);
451
452   /*Finally the main decode loop.
453
454     It's one Theora packet per frame, so this is pretty straightforward if
455      we're not trying to maintain sync with other multiplexed streams.
456
457     The videobuf_ready flag is used to maintain the input buffer in the libogg
458      stream state.
459     If there's no output frame available at the end of the decode step, we must
460      need more input data.
461     We could simplify this by just using the return code on
462      ogg_page_packetout(), but the flag system extends easily to the case where
463      you care about more than one multiplexed stream (like with audio
464      playback).
465     In that case, just maintain a flag for each decoder you care about, and
466      pull data when any one of them stalls.
467
468     videobuf_time holds the presentation time of the currently buffered video
469      frame.
470     We ignore this value.*/
471
472   stateflag=0; /* playback has not begun */
473   /* queue any remaining pages from data we buffered but that did not
474       contain headers */
475   while(ogg_sync_pageout(&oy,&og)>0){
476     queue_page(&og);
477   }
478
479   if(fps_only){
480     ftime(&start);
481     ftime(&last);
482   }
483
484   while(!got_sigint){
485
486     while(theora_p && !videobuf_ready){
487       /* theora is one in, one out... */
488       if(ogg_stream_packetout(&to,&op)>0){
489
490         if(th_decode_packetin(td,&op,&videobuf_granulepos)>=0){
491           videobuf_time=th_granule_time(td,videobuf_granulepos);
492           videobuf_ready=1;
493           frames++;
494           if(fps_only)
495             ftime(&after);
496         }
497
498       }else
499         break;
500     }
501
502     if(fps_only && (videobuf_ready || fps_only==2)){
503       long ms =
504         after.time*1000.+after.millitm-
505         (last.time*1000.+last.millitm);
506
507       if(ms>500 || fps_only==1 ||
508          (feof(infile) && !videobuf_ready)){
509         float file_fps = (float)ti.fps_numerator/ti.fps_denominator;
510         fps_only=2;
511
512         ms = after.time*1000.+after.millitm-
513           (start.time*1000.+start.millitm);
514
515         fprintf(stderr,"\rframe:%d rate:%.2fx           ",
516                 frames,
517                 frames*1000./(ms*file_fps));
518         memcpy(&last,&after,sizeof(last));
519       }
520     }
521
522     if(!videobuf_ready && feof(infile))break;
523
524     if(!videobuf_ready){
525       /* no data yet for somebody.  Grab another page */
526       buffer_data(infile,&oy);
527       while(ogg_sync_pageout(&oy,&og)>0){
528         queue_page(&og);
529       }
530     }
531     /* dumpvideo frame, and get new one */
532     else if(outfile)video_write();
533
534     videobuf_ready=0;
535   }
536
537   /* end of decoder loop -- close everything */
538
539   if(theora_p){
540     ogg_stream_clear(&to);
541     th_decode_free(td);
542     th_comment_clear(&tc);
543     th_info_clear(&ti);
544   }
545   ogg_sync_clear(&oy);
546
547   if(infile && infile!=stdin)fclose(infile);
548   if(outfile && outfile!=stdout)fclose(outfile);
549
550   fprintf(stderr, "\n\n%d frames\n", frames);
551   fprintf(stderr, "\nDone.\n");
552
553   return(0);
554
555 }