1 /********************************************************************
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. *
8 * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 *
9 * by the Xiph.Org Foundation http://www.xiph.org/ *
11 ********************************************************************
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 $
16 ********************************************************************/
18 /* By Mauricio Piacentini (mauricio at xiph.org) */
19 /* simply dump decoded YUV data, for verification of theora bitstream */
21 #if !defined(_REENTRANT)
24 #if !defined(_GNU_SOURCE)
27 #if !defined(_LARGEFILE_SOURCE)
28 #define _LARGEFILE_SOURCE
30 #if !defined(_LARGEFILE64_SOURCE)
31 #define _LARGEFILE64_SOURCE
33 #if !defined(_FILE_OFFSET_BITS)
34 #define _FILE_OFFSET_BITS 64
40 #include <sys/timeb.h>
41 #include <sys/types.h>
43 /*Yes, yes, we're going to hell.*/
52 #include "theora/theoradec.h"
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 */
62 /* Helper; just grab some more compressed bitstream and sync it for
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);
71 /* never forget that globals are a one-way ticket to Hell */
72 /* Ogg and codec state for demux/decode */
83 int theora_processing_headers;
86 /* single frame video buffering */
88 ogg_int64_t videobuf_granulepos=-1;
89 double videobuf_time=0;
95 static void sigint_handler (int signal) {
99 static th_ycbcr_buffer ycbcr;
101 static void stripe_decoded(th_ycbcr_buffer _dst,th_ycbcr_buffer _src,
102 int _fragy0,int _fragy_end){
104 for(pli=0;pli<3;pli++){
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);
119 static void open_video(void){
120 th_stripe_callback cb;
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++){
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;
138 /*Similarly, since ycbcr is a global, there's no real reason to pass it as
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).*/
144 cb.stripe_decoded=(th_stripe_decoded_func)stripe_decoded;
145 th_decode_ctl(td,TH_DECCTL_SET_STRIPE_CB,&cb,sizeof(cb));
148 /*Write out the planar YUV frame, uncropped.*/
149 static void video_write(void){
152 /*Uncomment the following to do normal, non-striped decoding.
153 th_ycbcr_buffer ycbcr;
154 th_decode_ycbcr_out(td,ycbcr);*/
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);
166 /* dump the theora comment header */
167 static int dump_comments(th_comment *_tc){
172 fprintf(out,"Encoded by %s\n",_tc->vendor);
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]);
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);
193 static void usage(void){
195 "Usage: dumpvid <file.ogv> > outfile\n"
196 "input is read from stdin if no file is passed on the command line\n"
201 int main(int argc,char *argv[]){
205 int long_option_index;
214 FILE *infile = stdin;
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 );
224 /* Process option arguments. */
225 while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
228 if(strcmp(optarg,"-")!=0){
229 outfile=fopen(optarg,"wb");
231 fprintf(stderr,"Unable to open output file '%s'\n", optarg);
253 infile=fopen(argv[optind],"rb");
255 fprintf(stderr,"Unable to open '%s' for extraction.\n", argv[optind]);
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.*/
272 /* start up Ogg stream synchronization layer */
275 /* init supporting Theora structures needed in header parsing */
276 th_comment_init(&tc);
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.*/
284 /* Only interested in Theora streams */
286 int ret=buffer_data(infile,&oy);
288 while(ogg_sync_pageout(&oy,&og)>0){
290 ogg_stream_state test;
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 */
300 ogg_stream_init(&test,ogg_page_serialno(&og));
301 ogg_stream_pagein(&test,&og);
302 got_packet = ogg_stream_packetpeek(&test,&op);
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));
310 /*Advance past the successfully processed header.*/
311 if(theora_processing_headers)ogg_stream_packetout(&to,NULL);
313 /* whatever it is, we don't care about it */
314 ogg_stream_clear(&test);
317 /* fall through to non-bos page parsing */
320 /* we're expecting more header packets. */
321 while(theora_p && theora_processing_headers){
324 /* look for further theora headers */
325 while(theora_processing_headers&&(ret=ogg_stream_packetpeek(&to,&op))){
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");
333 else if(theora_processing_headers>0){
334 /*Advance past the successfully processed header.*/
335 ogg_stream_packetout(&to,NULL);
340 /*Stop now so we don't fail if there aren't enough pages in a short
342 if(!(theora_p && theora_processing_headers))break;
344 /* The header pages/packets will arrive before anything else we
345 care about, or the stream is not obeying spec */
347 if(ogg_sync_pageout(&oy,&og)>0){
348 queue_page(&og); /* demux into the appropriate stream */
350 int ret=buffer_data(infile,&oy); /* someone needs more data */
352 fprintf(stderr,"End of file while searching for codec headers.\n");
358 /* and now we have it all. initialize decoders */
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);
368 /* tear down the partial theora setup */
370 th_comment_clear(&tc);
372 /*Either way, we're done with the codec setup data.*/
376 if(theora_p)open_video();
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);
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);
390 /* install signal handler */
391 signal (SIGINT, sigint_handler);
393 /*Finally the main decode loop.
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.
398 The videobuf_ready flag is used to maintain the input buffer in the libogg
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
406 In that case, just maintain a flag for each decoder you care about, and
407 pull data when any one of them stalls.
409 videobuf_time holds the presentation time of the currently buffered video
411 We ignore this value.*/
413 stateflag=0; /* playback has not begun */
414 /* queue any remaining pages from data we buffered but that did not
416 while(ogg_sync_pageout(&oy,&og)>0){
427 while(theora_p && !videobuf_ready){
428 /* theora is one in, one out... */
429 if(ogg_stream_packetout(&to,&op)>0){
431 if(th_decode_packetin(td,&op,&videobuf_granulepos)>=0){
432 videobuf_time=th_granule_time(td,videobuf_granulepos);
443 if(fps_only && (videobuf_ready || fps_only==2)){
445 after.time*1000.+after.millitm-
446 (last.time*1000.+last.millitm);
448 if(ms>500 || fps_only==1 ||
449 (feof(infile) && !videobuf_ready)){
450 float file_fps = (float)ti.fps_numerator/ti.fps_denominator;
453 ms = after.time*1000.+after.millitm-
454 (start.time*1000.+start.millitm);
456 fprintf(stderr,"\rframe:%d rate:%.2fx ",
458 frames*1000./(ms*file_fps));
459 memcpy(&last,&after,sizeof(last));
463 if(!videobuf_ready && feof(infile))break;
466 /* no data yet for somebody. Grab another page */
467 buffer_data(infile,&oy);
468 while(ogg_sync_pageout(&oy,&og)>0){
472 /* dumpvideo frame, and get new one */
473 else if(outfile)video_write();
478 /* end of decoder loop -- close everything */
481 ogg_stream_clear(&to);
483 th_comment_clear(&tc);
488 if(infile && infile!=stdin)fclose(infile);
489 if(outfile && outfile!=stdout)fclose(outfile);
491 fprintf(stderr, "\n\n%d frames\n", frames);
492 fprintf(stderr, "\nDone.\n");