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-2004 *
9 * by the Xiph.Org Foundation http://www.xiph.org/ *
11 ********************************************************************
13 function: example dumpvid application; dumps Theora streams
16 ********************************************************************/
18 /* By Mauricio Piacentini (mauricio at xiph.org) */
19 /* simply dump decoded YUV data, for verification of theora bitstream */
26 #define _LARGEFILE_SOURCE
27 #define _LARGEFILE64_SOURCE
28 #define _FILE_OFFSET_BITS 64
36 #include <sys/types.h>
38 #include <sys/timeb.h>
49 #include "theora/theora.h"
53 const char *optstring
= "o:rf";
54 struct option options
[] = {
55 {"output",required_argument
,NULL
,'o'},
56 {"raw",no_argument
, NULL
, 'r'}, /* Disable YUV4MPEG2 headers if set */
57 {"fps-only",no_argument
, NULL
, 'f'}, /* Only interested in fps of decode loop */
61 /* Helper; just grab some more compressed bitstream and sync it for
63 int buffer_data(FILE *in
,ogg_sync_state
*oy
){
64 char *buffer
=ogg_sync_buffer(oy
,4096);
65 int bytes
=fread(buffer
,1,4096,in
);
66 ogg_sync_wrote(oy
,bytes
);
70 /* never forget that globals are a one-way ticket to Hell */
71 /* Ogg and codec state for demux/decode */
83 /* single frame video buffering */
85 ogg_int64_t videobuf_granulepos
=-1;
86 double videobuf_time
=0;
92 static void sigint_handler (int signal
) {
96 /* this is a nop in the current implementation. we could
97 open a file here or something if so moved. */
98 static void open_video(void){
102 /* write out the planar YUV frame, uncropped */
103 static void video_write(void){
107 theora_decode_YUVout(&td
,&yuv
);
111 fprintf(outfile
, "FRAME\n");
112 for(i
=0;i
<yuv
.y_height
;i
++)
113 fwrite(yuv
.y
+yuv
.y_stride
*i
, 1, yuv
.y_width
, outfile
);
114 for(i
=0;i
<yuv
.uv_height
;i
++)
115 fwrite(yuv
.u
+yuv
.uv_stride
*i
, 1, yuv
.uv_width
, outfile
);
116 for(i
=0;i
<yuv
.uv_height
;i
++)
117 fwrite(yuv
.v
+yuv
.uv_stride
*i
, 1, yuv
.uv_width
, outfile
);
121 /* dump the theora comment header */
122 static int dump_comments(theora_comment
*tc
){
127 fprintf(out
,"Encoded by %s\n",tc
->vendor
);
129 fprintf(out
, "theora comment header:\n");
130 for(i
=0;i
<tc
->comments
;i
++){
131 if(tc
->user_comments
[i
]){
132 len
=tc
->comment_lengths
[i
];
134 memcpy(value
,tc
->user_comments
[i
],len
);
136 fprintf(out
, "\t%s\n", value
);
144 /* helper: push a page into the steam for packetization */
145 static int queue_page(ogg_page
*page
){
146 if(theora_p
)ogg_stream_pagein(&to
,&og
);
150 static void usage(void){
152 "Usage: dumpvid <file.ogg> > outfile\n"
153 "input is read from stdin if no file is passed on the command line\n"
158 int main(int argc
,char *argv
[]){
162 int long_option_index
;
171 FILE *infile
= stdin
;
174 #ifdef _WIN32 /* We need to set stdin/stdout to binary mode on windows. */
175 /* Beware the evil ifdef. We avoid these where we can, but this one we
176 cannot. Don't add any more, you'll probably go to hell if you do. */
177 _setmode( _fileno( stdin
), _O_BINARY
);
178 _setmode( _fileno( stdout
), _O_BINARY
);
181 /* Process option arguments. */
182 while((c
=getopt_long(argc
,argv
,optstring
,options
,&long_option_index
))!=EOF
){
185 if(!strcmp(optarg
,"-")){
186 outfile
=fopen(optarg
,"wb");
188 fprintf(stderr
,"Unable to open output file '%s'\n", optarg
);
210 infile
=fopen(argv
[optind
],"rb");
212 fprintf(stderr
,"Unable to open '%s' for extraction.\n", argv
[optind
]);
223 Ok, Ogg parsing. The idea here is we have a bitstream
224 that is made up of Ogg pages. The libogg sync layer will
225 find them for us. There may be pages from several logical
226 streams interleaved; we find the first theora stream and
229 Then we pass the pages for our stream to the libogg stream
230 layer which assembles our original set of packets out of
231 them. It's the packets that libtheora actually knows how
235 /* start up Ogg stream synchronization layer */
238 /* init supporting Theora structures needed in header parsing */
239 theora_comment_init(&tc
);
240 theora_info_init(&ti
);
242 /* Ogg file open; parse the headers */
244 /* Vorbis and Theora both depend on some initial header packets
245 for decoder setup and initialization. We retrieve these first
246 before entering the main decode loop. */
248 /* Only interested in Theora streams */
250 int ret
=buffer_data(infile
,&oy
);
252 while(ogg_sync_pageout(&oy
,&og
)>0){
253 ogg_stream_state test
;
255 /* is this a mandated initial header? If not, stop parsing */
256 if(!ogg_page_bos(&og
)){
257 /* don't leak the page; get it into the appropriate stream */
263 ogg_stream_init(&test
,ogg_page_serialno(&og
));
264 ogg_stream_pagein(&test
,&og
);
265 ogg_stream_packetout(&test
,&op
);
267 /* identify the codec: try theora */
268 if(!theora_p
&& theora_decode_header(&ti
,&tc
,&op
)>=0){
269 /* it is theora -- save this stream state */
270 memcpy(&to
,&test
,sizeof(test
));
273 /* whatever it is, we don't care about it */
274 ogg_stream_clear(&test
);
277 /* fall through to non-initial page parsing */
280 /* we're expecting more header packets. */
281 while(theora_p
&& theora_p
<3){
284 /* look for further theora headers */
285 while(theora_p
&& (theora_p
<3) && (ret
=ogg_stream_packetout(&to
,&op
))){
287 fprintf(stderr
,"Error parsing Theora stream headers; corrupt stream?\n");
290 if(theora_decode_header(&ti
,&tc
,&op
)){
291 printf("Error parsing Theora stream headers; corrupt stream?\n");
295 if(theora_p
==3)break;
299 /* The header pages/packets will arrive before anything else we
300 care about, or the stream is not obeying spec */
302 if(ogg_sync_pageout(&oy
,&og
)>0){
303 queue_page(&og
); /* demux into the stream state */
305 int ret
=buffer_data(infile
,&oy
); /* need more data */
307 fprintf(stderr
,"End of file while searching for codec headers.\n");
313 /* Now we have all the required headers. initialize the decoder. */
315 theora_decode_init(&td
,&ti
);
316 fprintf(stderr
,"Ogg logical stream %x is Theora %dx%d %.02f fps video\nEncoded frame content is %dx%d with %dx%d offset\n",
317 (unsigned int)to
.serialno
,ti
.width
,ti
.height
,
318 (double)ti
.fps_numerator
/ti
.fps_denominator
,
319 ti
.frame_width
, ti
.frame_height
, ti
.offset_x
, ti
.offset_y
);
321 /* tear down the partial theora setup */
322 theora_info_clear(&ti
);
323 theora_comment_clear(&tc
);
327 if(theora_p
)open_video();
330 fprintf(outfile
, "YUV4MPEG2 W%d H%d F%d:%d I%c A%d:%d\n",
331 ti
.width
, ti
.height
, ti
.fps_numerator
, ti
.fps_denominator
, 'p',
332 ti
.aspect_numerator
, ti
.aspect_denominator
);
334 /* install signal handler */
335 signal (SIGINT
, sigint_handler
);
337 /* Finally the main decode loop.
339 It's one Theora packet per frame, so this is pretty
340 straightforward if we're not trying to maintain sync
341 with other multiplexed streams.
343 the videobuf_ready flag is used to maintain the input
344 buffer in the libogg stream state. If there's no output
345 frame available at the end of the decode step, we must
346 need more input data. We could simplify this by just
347 using the return code on ogg_page_packetout(), but the
348 flag system extends easily to the case were you care
349 about more than one multiplexed stream (like with audio
350 playback). In that case, just maintain a flag for each
351 decoder you care about, and pull data when any one of
354 videobuf_time holds the presentation time of the currently
355 buffered video frame. We ignore this value.
358 stateflag
=0; /* playback has not begun */
359 /* queue any remaining pages from data we buffered but that did not
361 while(ogg_sync_pageout(&oy
,&og
)>0){
372 while(theora_p
&& !videobuf_ready
){
373 /* theora is one in, one out... */
374 if(ogg_stream_packetout(&to
,&op
)>0){
376 theora_decode_packetin(&td
,&op
);
377 videobuf_granulepos
=td
.granulepos
;
378 videobuf_time
=theora_granule_time(&td
,videobuf_granulepos
);
388 if(fps_only
&& (videobuf_ready
|| fps_only
==2)){
390 after
.time
*1000.+after
.millitm
-
391 (last
.time
*1000.+last
.millitm
);
393 if(ms
>500 || fps_only
==1 ||
394 (feof(infile
) && !videobuf_ready
)){
395 float file_fps
= (float)ti
.fps_numerator
/ti
.fps_denominator
;
398 ms
= after
.time
*1000.+after
.millitm
-
399 (start
.time
*1000.+start
.millitm
);
401 fprintf(stderr
,"\rframe:%d rate:%.2fx ",
403 frames
*1000./(ms
*file_fps
));
404 memcpy(&last
,&after
,sizeof(last
));
409 if(!videobuf_ready
&& feof(infile
))break;
412 /* no data yet for somebody. Grab another page */
413 buffer_data(infile
,&oy
);
414 while(ogg_sync_pageout(&oy
,&og
)>0){
418 /* dumpvideo frame, and get new one */
424 /* end of decoder loop -- close everything */
427 ogg_stream_clear(&to
);
429 theora_comment_clear(&tc
);
430 theora_info_clear(&ti
);
434 if(infile
&& infile
!=stdin
)fclose(infile
);