spec: Use %license macro to copy license
[platform/upstream/libtheora.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:rf";
55 struct option options [] = {
56   {"output",required_argument,NULL,'o'},
57   {"raw",no_argument, NULL,'r'}, /*Disable YUV4MPEG2 headers:*/
58   {"fps-only",no_argument, NULL, 'f'}, /* Only interested in fps of decode loop */
59   {NULL,0,NULL,0}
60 };
61
62 /* Helper; just grab some more compressed bitstream and sync it for
63    page extraction */
64 int buffer_data(FILE *in,ogg_sync_state *oy){
65   char *buffer=ogg_sync_buffer(oy,4096);
66   int bytes=fread(buffer,1,4096,in);
67   ogg_sync_wrote(oy,bytes);
68   return(bytes);
69 }
70
71 /* never forget that globals are a one-way ticket to Hell */
72 /* Ogg and codec state for demux/decode */
73 ogg_sync_state    oy;
74 ogg_page          og;
75 ogg_stream_state  vo;
76 ogg_stream_state  to;
77 th_info           ti;
78 th_comment        tc;
79 th_setup_info    *ts;
80 th_dec_ctx       *td;
81
82 int              theora_p=0;
83 int              theora_processing_headers;
84 int              stateflag=0;
85
86 /* single frame video buffering */
87 int          videobuf_ready=0;
88 ogg_int64_t  videobuf_granulepos=-1;
89 double       videobuf_time=0;
90 int          raw=0;
91
92 FILE* outfile = NULL;
93
94 int got_sigint=0;
95 static void sigint_handler (int signal) {
96   got_sigint = 1;
97 }
98
99 static th_ycbcr_buffer ycbcr;
100
101 static void stripe_decoded(th_ycbcr_buffer _dst,th_ycbcr_buffer _src,
102  int _fragy0,int _fragy_end){
103   int pli;
104   for(pli=0;pli<3;pli++){
105     int yshift;
106     int y_end;
107     int y;
108     yshift=pli!=0&&!(ti.pixel_fmt&2);
109     y_end=_fragy_end<<3-yshift;
110     /*An implemention intending to display this data would need to check the
111        crop rectangle before proceeding.*/
112     for(y=_fragy0<<3-yshift;y<y_end;y++){
113       memcpy(_dst[pli].data+y*_dst[pli].stride,
114        _src[pli].data+y*_src[pli].stride,_src[pli].width);
115     }
116   }
117 }
118
119 static void open_video(void){
120   th_stripe_callback cb;
121   int                pli;
122   /*Here we allocate a buffer so we can use the striped decode feature.
123     There's no real reason to do this in this application, because we want to
124      write to the file top-down, but the frame gets decoded bottom up, so we
125      have to buffer it all anyway.
126     But this illustrates how the API works.*/
127   for(pli=0;pli<3;pli++){
128     int xshift;
129     int yshift;
130     xshift=pli!=0&&!(ti.pixel_fmt&1);
131     yshift=pli!=0&&!(ti.pixel_fmt&2);
132     ycbcr[pli].data=(unsigned char *)malloc(
133      (ti.frame_width>>xshift)*(ti.frame_height>>yshift)*sizeof(char));
134     ycbcr[pli].stride=ti.frame_width>>xshift;
135     ycbcr[pli].width=ti.frame_width>>xshift;
136     ycbcr[pli].height=ti.frame_height>>yshift;
137   }
138   /*Similarly, since ycbcr is a global, there's no real reason to pass it as
139      the context.
140     In a more object-oriented decoder, we could pass the "this" pointer
141      instead (though in C++, platform-dependent calling convention differences
142      prevent us from using a real member function pointer).*/
143   cb.ctx=ycbcr;
144   cb.stripe_decoded=(th_stripe_decoded_func)stripe_decoded;
145   th_decode_ctl(td,TH_DECCTL_SET_STRIPE_CB,&cb,sizeof(cb));
146 }
147
148 /*Write out the planar YUV frame, uncropped.*/
149 static void video_write(void){
150   int pli;
151   int i;
152   /*Uncomment the following to do normal, non-striped decoding.
153   th_ycbcr_buffer ycbcr;
154   th_decode_ycbcr_out(td,ycbcr);*/
155   if(outfile){
156     if(!raw)fprintf(outfile, "FRAME\n");
157     for(pli=0;pli<3;pli++){
158       for(i=0;i<ycbcr[pli].height;i++){
159         fwrite(ycbcr[pli].data+ycbcr[pli].stride*i, 1,
160          ycbcr[pli].width, outfile);
161       }
162     }
163   }
164 }
165
166 /* dump the theora comment header */
167 static int dump_comments(th_comment *_tc){
168   int   i;
169   int   len;
170   FILE *out;
171   out=stderr;
172   fprintf(out,"Encoded by %s\n",_tc->vendor);
173   if(_tc->comments){
174     fprintf(out,"theora comment header:\n");
175     for(i=0;i<_tc->comments;i++){
176       if(_tc->user_comments[i]){
177         len=_tc->comment_lengths[i]<INT_MAX?_tc->comment_lengths[i]:INT_MAX;
178         fprintf(out,"\t%.*s\n",len,_tc->user_comments[i]);
179       }
180     }
181   }
182   return 0;
183 }
184
185 /* helper: push a page into the appropriate steam */
186 /* this can be done blindly; a stream won't accept a page
187                 that doesn't belong to it */
188 static int queue_page(ogg_page *page){
189   if(theora_p)ogg_stream_pagein(&to,page);
190   return 0;
191 }
192
193 static void usage(void){
194   fprintf(stderr,
195           "Usage: dumpvid <file.ogv> > outfile\n"
196           "input is read from stdin if no file is passed on the command line\n"
197           "\n"
198   );
199 }
200
201 int main(int argc,char *argv[]){
202
203   ogg_packet op;
204
205   int long_option_index;
206   int c;
207
208   struct timeb start;
209   struct timeb after;
210   struct timeb last;
211   int fps_only=0;
212   int frames = 0;
213
214   FILE *infile = stdin;
215   outfile = stdout;
216
217 #ifdef _WIN32 /* We need to set stdin/stdout to binary mode on windows. */
218   /* Beware the evil ifdef. We avoid these where we can, but this one we
219      cannot. Don't add any more, you'll probably go to hell if you do. */
220   _setmode( _fileno( stdin ), _O_BINARY );
221   _setmode( _fileno( stdout ), _O_BINARY );
222 #endif
223
224   /* Process option arguments. */
225   while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
226     switch(c){
227     case 'o':
228       if(strcmp(optarg,"-")!=0){
229         outfile=fopen(optarg,"wb");
230         if(outfile==NULL){
231           fprintf(stderr,"Unable to open output file '%s'\n", optarg);
232           exit(1);
233         }
234       }else{
235         outfile=stdout;
236       }
237       break;
238
239     case 'r':
240       raw=1;
241       break;
242
243     case 'f':
244       fps_only = 1;
245       outfile = NULL;
246       break;
247
248     default:
249       usage();
250     }
251   }
252   if(optind<argc){
253     infile=fopen(argv[optind],"rb");
254     if(infile==NULL){
255       fprintf(stderr,"Unable to open '%s' for extraction.\n", argv[optind]);
256       exit(1);
257     }
258     if(++optind<argc){
259       usage();
260       exit(1);
261     }
262   }
263   /*Ok, Ogg parsing.
264     The idea here is we have a bitstream that is made up of Ogg pages.
265     The libogg sync layer will find them for us.
266     There may be pages from several logical streams interleaved; we find the
267      first theora stream and ignore any others.
268     Then we pass the pages for our stream to the libogg stream layer which
269      assembles our original set of packets out of them.
270     It's the packets that libtheora actually knows how to handle.*/
271
272   /* start up Ogg stream synchronization layer */
273   ogg_sync_init(&oy);
274
275   /* init supporting Theora structures needed in header parsing */
276   th_comment_init(&tc);
277   th_info_init(&ti);
278
279   /*Ogg file open; parse the headers.
280     Theora (like Vorbis) depends on some initial header packets for decoder
281      setup and initialization.
282     We retrieve these first before entering the main decode loop.*/
283
284   /* Only interested in Theora streams */
285   while(!stateflag){
286     int ret=buffer_data(infile,&oy);
287     if(ret==0)break;
288     while(ogg_sync_pageout(&oy,&og)>0){
289       int got_packet;
290       ogg_stream_state test;
291
292       /* is this a mandated initial header? If not, stop parsing */
293       if(!ogg_page_bos(&og)){
294         /* don't leak the page; get it into the appropriate stream */
295         queue_page(&og);
296         stateflag=1;
297         break;
298       }
299
300       ogg_stream_init(&test,ogg_page_serialno(&og));
301       ogg_stream_pagein(&test,&og);
302       got_packet = ogg_stream_packetpeek(&test,&op);
303
304       /* identify the codec: try theora */
305       if((got_packet==1) && !theora_p && (theora_processing_headers=
306        th_decode_headerin(&ti,&tc,&ts,&op))>=0){
307         /* it is theora -- save this stream state */
308         memcpy(&to,&test,sizeof(test));
309         theora_p=1;
310         /*Advance past the successfully processed header.*/
311         if(theora_processing_headers)ogg_stream_packetout(&to,NULL);
312       }else{
313         /* whatever it is, we don't care about it */
314         ogg_stream_clear(&test);
315       }
316     }
317     /* fall through to non-bos page parsing */
318   }
319
320   /* we're expecting more header packets. */
321   while(theora_p && theora_processing_headers){
322     int ret;
323
324     /* look for further theora headers */
325     while(theora_processing_headers&&(ret=ogg_stream_packetpeek(&to,&op))){
326       if(ret<0)continue;
327       theora_processing_headers=th_decode_headerin(&ti,&tc,&ts,&op);
328       if(theora_processing_headers<0){
329         fprintf(stderr,"Error parsing Theora stream headers; "
330          "corrupt stream?\n");
331         exit(1);
332       }
333       else if(theora_processing_headers>0){
334         /*Advance past the successfully processed header.*/
335         ogg_stream_packetout(&to,NULL);
336       }
337       theora_p++;
338     }
339
340     /*Stop now so we don't fail if there aren't enough pages in a short
341        stream.*/
342     if(!(theora_p && theora_processing_headers))break;
343
344     /* The header pages/packets will arrive before anything else we
345        care about, or the stream is not obeying spec */
346
347     if(ogg_sync_pageout(&oy,&og)>0){
348       queue_page(&og); /* demux into the appropriate stream */
349     }else{
350       int ret=buffer_data(infile,&oy); /* someone needs more data */
351       if(ret==0){
352         fprintf(stderr,"End of file while searching for codec headers.\n");
353         exit(1);
354       }
355     }
356   }
357
358   /* and now we have it all.  initialize decoders */
359   if(theora_p){
360     dump_comments(&tc);
361     td=th_decode_alloc(&ti,ts);
362     fprintf(stderr,"Ogg logical stream %lx is Theora %dx%d %.02f fps video\n"
363      "Encoded frame content is %dx%d with %dx%d offset\n",
364      to.serialno,ti.frame_width,ti.frame_height,
365      (double)ti.fps_numerator/ti.fps_denominator,
366      ti.pic_width,ti.pic_height,ti.pic_x,ti.pic_y);
367   }else{
368     /* tear down the partial theora setup */
369     th_info_clear(&ti);
370     th_comment_clear(&tc);
371   }
372   /*Either way, we're done with the codec setup data.*/
373   th_setup_free(ts);
374
375   /* open video */
376   if(theora_p)open_video();
377
378   if(!raw && outfile){
379     static const char *CHROMA_TYPES[4]={"420jpeg",NULL,"422","444"};
380     if(ti.pixel_fmt>=4||ti.pixel_fmt==TH_PF_RSVD){
381       fprintf(stderr,"Unknown pixel format: %i\n",ti.pixel_fmt);
382       exit(1);
383     }
384     fprintf(outfile,"YUV4MPEG2 C%s W%d H%d F%d:%d I%c A%d:%d\n",
385      CHROMA_TYPES[ti.pixel_fmt],ti.frame_width,ti.frame_height,
386      ti.fps_numerator,ti.fps_denominator,'p',
387      ti.aspect_numerator,ti.aspect_denominator);
388   }
389
390   /* install signal handler */
391   signal (SIGINT, sigint_handler);
392
393   /*Finally the main decode loop.
394
395     It's one Theora packet per frame, so this is pretty straightforward if
396      we're not trying to maintain sync with other multiplexed streams.
397
398     The videobuf_ready flag is used to maintain the input buffer in the libogg
399      stream state.
400     If there's no output frame available at the end of the decode step, we must
401      need more input data.
402     We could simplify this by just using the return code on
403      ogg_page_packetout(), but the flag system extends easily to the case where
404      you care about more than one multiplexed stream (like with audio
405      playback).
406     In that case, just maintain a flag for each decoder you care about, and
407      pull data when any one of them stalls.
408
409     videobuf_time holds the presentation time of the currently buffered video
410      frame.
411     We ignore this value.*/
412
413   stateflag=0; /* playback has not begun */
414   /* queue any remaining pages from data we buffered but that did not
415       contain headers */
416   while(ogg_sync_pageout(&oy,&og)>0){
417     queue_page(&og);
418   }
419
420   if(fps_only){
421     ftime(&start);
422     ftime(&last);
423   }
424
425   while(!got_sigint){
426
427     while(theora_p && !videobuf_ready){
428       /* theora is one in, one out... */
429       if(ogg_stream_packetout(&to,&op)>0){
430
431         if(th_decode_packetin(td,&op,&videobuf_granulepos)>=0){
432           videobuf_time=th_granule_time(td,videobuf_granulepos);
433           videobuf_ready=1;
434           frames++;
435           if(fps_only)
436             ftime(&after);
437         }
438
439       }else
440         break;
441     }
442
443     if(fps_only && (videobuf_ready || fps_only==2)){
444       long ms =
445         after.time*1000.+after.millitm-
446         (last.time*1000.+last.millitm);
447
448       if(ms>500 || fps_only==1 ||
449          (feof(infile) && !videobuf_ready)){
450         float file_fps = (float)ti.fps_numerator/ti.fps_denominator;
451         fps_only=2;
452
453         ms = after.time*1000.+after.millitm-
454           (start.time*1000.+start.millitm);
455
456         fprintf(stderr,"\rframe:%d rate:%.2fx           ",
457                 frames,
458                 frames*1000./(ms*file_fps));
459         memcpy(&last,&after,sizeof(last));
460       }
461     }
462
463     if(!videobuf_ready && feof(infile))break;
464
465     if(!videobuf_ready){
466       /* no data yet for somebody.  Grab another page */
467       buffer_data(infile,&oy);
468       while(ogg_sync_pageout(&oy,&og)>0){
469         queue_page(&og);
470       }
471     }
472     /* dumpvideo frame, and get new one */
473     else if(outfile)video_write();
474
475     videobuf_ready=0;
476   }
477
478   /* end of decoder loop -- close everything */
479
480   if(theora_p){
481     ogg_stream_clear(&to);
482     th_decode_free(td);
483     th_comment_clear(&tc);
484     th_info_clear(&ti);
485   }
486   ogg_sync_clear(&oy);
487
488   if(infile && infile!=stdin)fclose(infile);
489   if(outfile && outfile!=stdout)fclose(outfile);
490
491   fprintf(stderr, "\n\n%d frames\n", frames);
492   fprintf(stderr, "\nDone.\n");
493
494   return(0);
495
496 }