Add Russian translation provided by Валерий Крувялис <valkru@mail.ru>
[xiph-mirror.git] / theora-old / examples / dump_video.c
bloba77012c5770694170cb5755189743b88b6f58fd6
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-2004 *
9 * by the Xiph.Org Foundation http://www.xiph.org/ *
10 * *
11 ********************************************************************
13 function: example dumpvid application; dumps Theora streams
14 last mod: $Id$
16 ********************************************************************/
18 /* By Mauricio Piacentini (mauricio at xiph.org) */
19 /* simply dump decoded YUV data, for verification of theora bitstream */
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
25 #define _GNU_SOURCE
26 #define _LARGEFILE_SOURCE
27 #define _LARGEFILE64_SOURCE
28 #define _FILE_OFFSET_BITS 64
30 #include <stdio.h>
31 #ifndef WIN32
32 #include <unistd.h>
33 #endif
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/timeb.h>
40 #if defined(_WIN32)
41 #include <io.h>
42 #endif
44 #include <fcntl.h>
45 #include <math.h>
46 #include <signal.h>
48 #include "getopt.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 */
58 {NULL,0,NULL,0}
61 /* Helper; just grab some more compressed bitstream and sync it for
62 page extraction */
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);
67 return(bytes);
70 /* never forget that globals are a one-way ticket to Hell */
71 /* Ogg and codec state for demux/decode */
72 ogg_sync_state oy;
73 ogg_page og;
74 ogg_stream_state vo;
75 ogg_stream_state to;
76 theora_info ti;
77 theora_comment tc;
78 theora_state td;
80 int theora_p=0;
81 int stateflag=0;
83 /* single frame video buffering */
84 int videobuf_ready=0;
85 ogg_int64_t videobuf_granulepos=-1;
86 double videobuf_time=0;
87 int raw = 0;
89 FILE* outfile = NULL;
91 int got_sigint=0;
92 static void sigint_handler (int signal) {
93 got_sigint = 1;
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){
99 return;
102 /* write out the planar YUV frame, uncropped */
103 static void video_write(void){
104 int i;
106 yuv_buffer yuv;
107 theora_decode_YUVout(&td,&yuv);
109 if(outfile){
110 if(!raw)
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){
123 int i, len;
124 char *value;
125 FILE *out=stdout;
127 fprintf(out,"Encoded by %s\n",tc->vendor);
128 if(tc->comments){
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];
133 value=malloc(len+1);
134 memcpy(value,tc->user_comments[i],len);
135 value[len]='\0';
136 fprintf(out, "\t%s\n", value);
137 free(value);
141 return(0);
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);
147 return 0;
150 static void usage(void){
151 fprintf(stderr,
152 "Usage: dumpvid <file.ogg> > outfile\n"
153 "input is read from stdin if no file is passed on the command line\n"
154 "\n"
158 int main(int argc,char *argv[]){
160 ogg_packet op;
162 int long_option_index;
163 int c;
165 struct timeb start;
166 struct timeb after;
167 struct timeb last;
168 int fps_only=0;
169 int frames=0;
171 FILE *infile = stdin;
172 outfile = stdout;
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 );
179 #endif
181 /* Process option arguments. */
182 while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
183 switch(c){
184 case 'o':
185 if(!strcmp(optarg,"-")){
186 outfile=fopen(optarg,"wb");
187 if(outfile==NULL){
188 fprintf(stderr,"Unable to open output file '%s'\n", optarg);
189 exit(1);
191 }else{
192 outfile=stdout;
194 break;
196 case 'r':
197 raw = 1;
198 break;
200 case 'f':
201 fps_only = 1;
202 outfile=NULL;
203 break;
205 default:
206 usage();
209 if(optind<argc){
210 infile=fopen(argv[optind],"rb");
211 if(infile==NULL){
212 fprintf(stderr,"Unable to open '%s' for extraction.\n", argv[optind]);
213 exit(1);
215 if(++optind<argc){
216 usage();
217 exit(1);
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
227 ignore any others.
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
232 to handle.
235 /* start up Ogg stream synchronization layer */
236 ogg_sync_init(&oy);
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 */
249 while(!stateflag){
250 int ret=buffer_data(infile,&oy);
251 if(ret==0)break;
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 */
258 queue_page(&og);
259 stateflag=1;
260 break;
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));
271 theora_p=1;
272 }else{
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){
282 int ret;
284 /* look for further theora headers */
285 while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))){
286 if(ret<0){
287 fprintf(stderr,"Error parsing Theora stream headers; corrupt stream?\n");
288 exit(1);
290 if(theora_decode_header(&ti,&tc,&op)){
291 printf("Error parsing Theora stream headers; corrupt stream?\n");
292 exit(1);
294 theora_p++;
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 */
304 }else{
305 int ret=buffer_data(infile,&oy); /* need more data */
306 if(ret==0){
307 fprintf(stderr,"End of file while searching for codec headers.\n");
308 exit(1);
313 /* Now we have all the required headers. initialize the decoder. */
314 if(theora_p){
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);
320 }else{
321 /* tear down the partial theora setup */
322 theora_info_clear(&ti);
323 theora_comment_clear(&tc);
326 /* open video */
327 if(theora_p)open_video();
329 if(!raw && outfile)
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
352 them stalls.
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
360 contain headers */
361 while(ogg_sync_pageout(&oy,&og)>0){
362 queue_page(&og);
365 if(fps_only){
366 ftime(&start);
367 ftime(&last);
370 while(!got_sigint){
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);
379 videobuf_ready=1;
380 frames++;
381 if(fps_only)
382 ftime(&after);
384 }else
385 break;
388 if(fps_only && (videobuf_ready || fps_only==2)){
389 long ms =
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;
396 fps_only=2;
398 ms = after.time*1000.+after.millitm-
399 (start.time*1000.+start.millitm);
401 fprintf(stderr,"\rframe:%d rate:%.2fx ",
402 frames,
403 frames*1000./(ms*file_fps));
404 memcpy(&last,&after,sizeof(last));
409 if(!videobuf_ready && feof(infile))break;
411 if(!videobuf_ready){
412 /* no data yet for somebody. Grab another page */
413 buffer_data(infile,&oy);
414 while(ogg_sync_pageout(&oy,&og)>0){
415 queue_page(&og);
418 /* dumpvideo frame, and get new one */
419 else video_write();
421 videobuf_ready=0;
424 /* end of decoder loop -- close everything */
426 if(theora_p){
427 ogg_stream_clear(&to);
428 theora_clear(&td);
429 theora_comment_clear(&tc);
430 theora_info_clear(&ti);
432 ogg_sync_clear(&oy);
434 if(infile && infile!=stdin)fclose(infile);
436 fprintf(stderr,
437 "\n "
438 "\nDone.\n");
439 return(0);