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 and contributors http://www.xiph.org/ *
10 * *
11 ********************************************************************
12
13 function: example SDL player application; plays Ogg Theora files (with
14 optional Vorbis audio second stream)
15 last mod: $Id: player_example.c 16551 2009-09-09 17:53:13Z gmaxwell $
16
17 ********************************************************************/
18
19 /* far more complex than most Ogg 'example' programs. The complexity
20 of maintaining A/V sync is pretty much unavoidable. It's necessary
21 to actually have audio/video playback to make the hard audio clock
22 sync actually work. If there's audio playback, there might as well
23 be simple video playback as well...
24
25 A simple 'demux and write back streams' would have been easier,
26 it's true. */
27
28 #if !defined(_GNU_SOURCE)
29 #define _GNU_SOURCE
30 #endif
31 #if !defined(_LARGEFILE_SOURCE)
32 #define _LARGEFILE_SOURCE
33 #endif
34 #if !defined(_LARGEFILE64_SOURCE)
35 #define _LARGEFILE64_SOURCE
36 #endif
37 #if !defined(_FILE_OFFSET_BITS)
38 #define _FILE_OFFSET_BITS 64
39 #endif
40
41 #ifdef HAVE_CONFIG_H
42 # include <config.h>
43 #endif
44
45 #ifndef _REENTRANT
46 # define _REENTRANT
47 #endif
48
49 #include <stdio.h>
50 #include <unistd.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <sys/time.h>
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <fcntl.h>
57 #include <math.h>
58 #include <signal.h>
59 #include "theora/theoradec.h"
60 #include "vorbis/codec.h"
61 #include <SDL.h>
62
63 /* yes, this makes us OSS-specific for now. None of SDL, libao, libao2
64 give us any way to determine hardware timing, and since the
65 hard/kernel buffer is going to be most of or > a second, that's
66 just a little bit important */
67 #if defined(__FreeBSD__)
68 #include <machine/soundcard.h>
69 #define AUDIO_DEVICE "/dev/audio"
70 #elif defined(__NetBSD__) || defined(__OpenBSD__)
71 #include <soundcard.h>
72 #define AUDIO_DEVICE "/dev/audio"
73 #else
74 #include <sys/soundcard.h>
75 #define AUDIO_DEVICE "/dev/dsp"
76 #endif
77 #include <sys/ioctl.h>
78
79 /* Helper; just grab some more compressed bitstream and sync it for
80 page extraction */
buffer_data(FILE * in,ogg_sync_state * oy)81 int buffer_data(FILE *in,ogg_sync_state *oy){
82 char *buffer=ogg_sync_buffer(oy,4096);
83 int bytes=fread(buffer,1,4096,in);
84 ogg_sync_wrote(oy,bytes);
85 return(bytes);
86 }
87
88 /* never forget that globals are a one-way ticket to Hell */
89 /* Ogg and codec state for demux/decode */
90 ogg_sync_state oy;
91 ogg_page og;
92 ogg_stream_state vo;
93 ogg_stream_state to;
94 th_info ti;
95 th_comment tc;
96 th_dec_ctx *td;
97 th_setup_info *ts;
98 vorbis_info vi;
99 vorbis_dsp_state vd;
100 vorbis_block vb;
101 vorbis_comment vc;
102 th_pixel_fmt px_fmt;
103
104 int theora_p=0;
105 int vorbis_p=0;
106 int stateflag=0;
107
108 /* SDL Video playback structures */
109 SDL_Surface *screen;
110 SDL_Overlay *yuv_overlay;
111 SDL_Rect rect;
112
113 /* single frame video buffering */
114 int videobuf_ready=0;
115 ogg_int64_t videobuf_granulepos=-1;
116 double videobuf_time=0;
117
118 /* single audio fragment audio buffering */
119 int audiobuf_fill=0;
120 int audiobuf_ready=0;
121 ogg_int16_t *audiobuf;
122 ogg_int64_t audiobuf_granulepos=0; /* time position of last sample */
123
124 /* audio / video synchronization tracking:
125
126 Since this will make it to Google at some point and lots of people
127 search for how to do this, a quick rundown of a practical A/V sync
128 strategy under Linux [the UNIX where Everything Is Hard]. Naturally,
129 this works on other platforms using OSS for sound as well.
130
131 In OSS, we don't have reliable access to any precise information on
132 the exact current playback position (that, of course would have been
133 too easy; the kernel folks like to keep us app people working hard
134 doing simple things that should have been solved once and abstracted
135 long ago). Hopefully ALSA solves this a little better; we'll probably
136 use that once ALSA is the standard in the stable kernel.
137
138 We can't use the system clock for a/v sync because audio is hard
139 synced to its own clock, and both the system and audio clocks suffer
140 from wobble, drift, and a lack of accuracy that can be guaranteed to
141 add a reliable percent or so of error. After ten seconds, that's
142 100ms. We can't drift by half a second every minute.
143
144 Although OSS can't generally tell us where the audio playback pointer
145 is, we do know that if we work in complete audio fragments and keep
146 the kernel buffer full, a blocking select on the audio buffer will
147 give us a writable fragment immediately after playback finishes with
148 it. We assume at that point that we know the exact number of bytes in
149 the kernel buffer that have not been played (total fragments minus
150 one) and calculate clock drift between audio and system then (and only
151 then). Damp the sync correction fraction, apply, and walla: A
152 reliable A/V clock that even works if it's interrupted. */
153
154 long audiofd_totalsize=-1;
155 int audiofd_fragsize; /* read and write only complete fragments
156 so that SNDCTL_DSP_GETOSPACE is
157 accurate immediately after a bank
158 switch */
159 int audiofd=-1;
160 ogg_int64_t audiofd_timer_calibrate=-1;
161
open_audio()162 static void open_audio(){
163 audio_buf_info info;
164 int format=AFMT_S16_NE; /* host endian */
165 int channels=vi.channels;
166 int rate=vi.rate;
167 int ret;
168
169 audiofd=open(AUDIO_DEVICE,O_RDWR);
170 if(audiofd<0){
171 fprintf(stderr,"Could not open audio device " AUDIO_DEVICE ".\n");
172 exit(1);
173 }
174
175 ret=ioctl(audiofd,SNDCTL_DSP_SETFMT,&format);
176 if(ret){
177 fprintf(stderr,"Could not set 16 bit host-endian playback\n");
178 exit(1);
179 }
180
181 ret=ioctl(audiofd,SNDCTL_DSP_CHANNELS,&channels);
182 if(ret){
183 fprintf(stderr,"Could not set %d channel playback\n",channels);
184 exit(1);
185 }
186
187 ret=ioctl(audiofd,SNDCTL_DSP_SPEED,&rate);
188 if(ret){
189 fprintf(stderr,"Could not set %d Hz playback\n",rate);
190 exit(1);
191 }
192
193 ioctl(audiofd,SNDCTL_DSP_GETOSPACE,&info);
194 audiofd_fragsize=info.fragsize;
195 audiofd_totalsize=info.fragstotal*info.fragsize;
196
197 audiobuf=malloc(audiofd_fragsize);
198 }
199
audio_close(void)200 static void audio_close(void){
201 if(audiofd>-1){
202 ioctl(audiofd,SNDCTL_DSP_RESET,NULL);
203 close(audiofd);
204 free(audiobuf);
205 }
206 }
207
208 /* call this only immediately after unblocking from a full kernel
209 having a newly empty fragment or at the point of DMA restart */
audio_calibrate_timer(int restart)210 void audio_calibrate_timer(int restart){
211 struct timeval tv;
212 ogg_int64_t current_sample;
213 ogg_int64_t new_time;
214
215 gettimeofday(&tv,0);
216 new_time=tv.tv_sec*1000+tv.tv_usec/1000;
217
218 if(restart){
219 current_sample=audiobuf_granulepos-audiobuf_fill/2/vi.channels;
220 }else
221 current_sample=audiobuf_granulepos-
222 (audiobuf_fill+audiofd_totalsize-audiofd_fragsize)/2/vi.channels;
223
224 new_time-=1000*current_sample/vi.rate;
225
226 audiofd_timer_calibrate=new_time;
227 }
228
229 /* get relative time since beginning playback, compensating for A/V
230 drift */
get_time()231 double get_time(){
232 static ogg_int64_t last=0;
233 static ogg_int64_t up=0;
234 ogg_int64_t now;
235 struct timeval tv;
236
237 gettimeofday(&tv,0);
238 now=tv.tv_sec*1000+tv.tv_usec/1000;
239
240 if(audiofd_timer_calibrate==-1)audiofd_timer_calibrate=last=now;
241
242 if(audiofd<0){
243 /* no audio timer to worry about, we can just use the system clock */
244 /* only one complication: If the process is suspended, we should
245 reset timing to account for the gap in play time. Do it the
246 easy/hack way */
247 if(now-last>1000)audiofd_timer_calibrate+=(now-last);
248 last=now;
249 }
250
251 if(now-up>200){
252 double timebase=(now-audiofd_timer_calibrate)*.001;
253 int hundredths=timebase*100-(long)timebase*100;
254 int seconds=(long)timebase%60;
255 int minutes=((long)timebase/60)%60;
256 int hours=(long)timebase/3600;
257
258 fprintf(stderr," Playing: %d:%02d:%02d.%02d \r",
259 hours,minutes,seconds,hundredths);
260 up=now;
261 }
262
263 return (now-audiofd_timer_calibrate)*.001;
264
265 }
266
267 /* write a fragment to the OSS kernel audio API, but only if we can
268 stuff in a whole fragment without blocking */
audio_write_nonblocking(void)269 void audio_write_nonblocking(void){
270
271 if(audiobuf_ready){
272 audio_buf_info info;
273 long bytes;
274
275 ioctl(audiofd,SNDCTL_DSP_GETOSPACE,&info);
276 bytes=info.bytes;
277 if(bytes>=audiofd_fragsize){
278 if(bytes==audiofd_totalsize)audio_calibrate_timer(1);
279
280 while(1){
281 bytes=write(audiofd,audiobuf+(audiofd_fragsize-audiobuf_fill),
282 audiofd_fragsize);
283
284 if(bytes>0){
285
286 if(bytes!=audiobuf_fill){
287 /* shouldn't actually be possible... but eh */
288 audiobuf_fill-=bytes;
289 }else
290 break;
291 }
292 }
293
294 audiobuf_fill=0;
295 audiobuf_ready=0;
296
297 }
298 }
299 }
300
301 /* clean quit on Ctrl-C for SDL and thread shutdown as per SDL example
302 (we don't use any threads, but libSDL does) */
303 int got_sigint=0;
sigint_handler(int signal)304 static void sigint_handler (int signal) {
305 got_sigint = 1;
306 }
307
open_video(void)308 static void open_video(void){
309 int w;
310 int h;
311 w=(ti.pic_x+ti.frame_width+1&~1)-(ti.pic_x&~1);
312 h=(ti.pic_y+ti.frame_height+1&~1)-(ti.pic_y&~1);
313 if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
314 fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
315 exit(1);
316 }
317
318 screen = SDL_SetVideoMode(w, h, 0, SDL_SWSURFACE);
319 if ( screen == NULL ) {
320 fprintf(stderr, "Unable to set %dx%d video: %s\n",
321 w,h,SDL_GetError());
322 exit(1);
323 }
324
325 if (px_fmt==TH_PF_422)
326 yuv_overlay = SDL_CreateYUVOverlay(w, h,
327 SDL_YUY2_OVERLAY,
328 screen);
329 else
330 yuv_overlay = SDL_CreateYUVOverlay(w, h,
331 SDL_YV12_OVERLAY,
332 screen);
333
334 if ( yuv_overlay == NULL ) {
335 fprintf(stderr, "SDL: Couldn't create SDL_yuv_overlay: %s\n",
336 SDL_GetError());
337 exit(1);
338 }
339 rect.x = 0;
340 rect.y = 0;
341 rect.w = w;
342 rect.h = h;
343
344 SDL_DisplayYUVOverlay(yuv_overlay, &rect);
345 }
346
video_write(void)347 static void video_write(void){
348 int i;
349 th_ycbcr_buffer yuv;
350 int y_offset, uv_offset;
351 th_decode_ycbcr_out(td,yuv);
352 /* Lock SDL_yuv_overlay */
353 if ( SDL_MUSTLOCK(screen) ) {
354 if ( SDL_LockSurface(screen) < 0 ) return;
355 }
356 if (SDL_LockYUVOverlay(yuv_overlay) < 0) return;
357
358 /* let's draw the data on a SDL screen (*screen) */
359 /* deal with border stride */
360 /* reverse u and v for SDL */
361 /* and crop input properly, respecting the encoded frame rect */
362 /* problems may exist for odd frame rect for some encodings */
363
364 y_offset=(ti.pic_x&~1)+yuv[0].stride*(ti.pic_y&~1);
365
366 if (px_fmt==TH_PF_422) {
367 uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y);
368 /* SDL doesn't have a planar 4:2:2 */
369 for(i=0;i<yuv_overlay->h;i++) {
370 int j;
371 char *in_y = (char *)yuv[0].data+y_offset+yuv[0].stride*i;
372 char *out = (char *)(yuv_overlay->pixels[0]+yuv_overlay->pitches[0]*i);
373 for (j=0;j<yuv_overlay->w;j++)
374 out[j*2] = in_y[j];
375 char *in_u = (char *)yuv[1].data+uv_offset+yuv[1].stride*i;
376 char *in_v = (char *)yuv[2].data+uv_offset+yuv[2].stride*i;
377 for (j=0;j<yuv_overlay->w>>1;j++) {
378 out[j*4+1] = in_u[j];
379 out[j*4+3] = in_v[j];
380 }
381 }
382 } else {
383 uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y/2);
384 for(i=0;i<yuv_overlay->h;i++)
385 memcpy(yuv_overlay->pixels[0]+yuv_overlay->pitches[0]*i,
386 yuv[0].data+y_offset+yuv[0].stride*i,
387 yuv_overlay->w);
388 for(i=0;i<yuv_overlay->h/2;i++){
389 memcpy(yuv_overlay->pixels[1]+yuv_overlay->pitches[1]*i,
390 yuv[2].data+uv_offset+yuv[2].stride*i,
391 yuv_overlay->w/2);
392 memcpy(yuv_overlay->pixels[2]+yuv_overlay->pitches[2]*i,
393 yuv[1].data+uv_offset+yuv[1].stride*i,
394 yuv_overlay->w/2);
395 }
396 }
397
398 /* Unlock SDL_yuv_overlay */
399 if ( SDL_MUSTLOCK(screen) ) {
400 SDL_UnlockSurface(screen);
401 }
402 SDL_UnlockYUVOverlay(yuv_overlay);
403
404
405 /* Show, baby, show! */
406 SDL_DisplayYUVOverlay(yuv_overlay, &rect);
407
408 }
409 /* dump the theora (or vorbis) comment header */
dump_comments(th_comment * tc)410 static int dump_comments(th_comment *tc){
411 int i, len;
412 char *value;
413 FILE *out=stdout;
414
415 fprintf(out,"Encoded by %s\n",tc->vendor);
416 if(tc->comments){
417 fprintf(out, "theora comment header:\n");
418 for(i=0;i<tc->comments;i++){
419 if(tc->user_comments[i]){
420 len=tc->comment_lengths[i];
421 value=malloc(len+1);
422 memcpy(value,tc->user_comments[i],len);
423 value[len]='\0';
424 fprintf(out, "\t%s\n", value);
425 free(value);
426 }
427 }
428 }
429 return(0);
430 }
431
432 /* Report the encoder-specified colorspace for the video, if any.
433 We don't actually make use of the information in this example;
434 a real player should attempt to perform color correction for
435 whatever display device it supports. */
report_colorspace(th_info * ti)436 static void report_colorspace(th_info *ti)
437 {
438 switch(ti->colorspace){
439 case TH_CS_UNSPECIFIED:
440 /* nothing to report */
441 break;;
442 case TH_CS_ITU_REC_470M:
443 fprintf(stderr," encoder specified ITU Rec 470M (NTSC) color.\n");
444 break;;
445 case TH_CS_ITU_REC_470BG:
446 fprintf(stderr," encoder specified ITU Rec 470BG (PAL) color.\n");
447 break;;
448 default:
449 fprintf(stderr,"warning: encoder specified unknown colorspace (%d).\n",
450 ti->colorspace);
451 break;;
452 }
453 }
454
455 /* helper: push a page into the appropriate steam */
456 /* this can be done blindly; a stream won't accept a page
457 that doesn't belong to it */
queue_page(ogg_page * page)458 static int queue_page(ogg_page *page){
459 if(theora_p)ogg_stream_pagein(&to,page);
460 if(vorbis_p)ogg_stream_pagein(&vo,page);
461 return 0;
462 }
463
usage(void)464 static void usage(void){
465 fprintf(stderr,
466 "Usage: player_example <file.ogv>\n"
467 "input is read from stdin if no file is passed on the command line\n"
468 "\n"
469 );
470 }
471
main(int argc,char * const * argv)472 int main(int argc,char *const *argv){
473
474 int pp_level_max;
475 int pp_level;
476 int pp_inc;
477 int i,j;
478 ogg_packet op;
479
480 FILE *infile = stdin;
481
482 int frames = 0;
483 int dropped = 0;
484
485 #ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */
486 /* Beware the evil ifdef. We avoid these where we can, but this one we
487 cannot. Don't add any more, you'll probably go to hell if you do. */
488 _setmode( _fileno( stdin ), _O_BINARY );
489 #endif
490
491 /* open the input file if any */
492 if(argc==2){
493 infile=fopen(argv[1],"rb");
494 if(infile==NULL){
495 fprintf(stderr,"Unable to open '%s' for playback.\n", argv[1]);
496 exit(1);
497 }
498 }
499 if(argc>2){
500 usage();
501 exit(1);
502 }
503
504 /* start up Ogg stream synchronization layer */
505 ogg_sync_init(&oy);
506
507 /* init supporting Vorbis structures needed in header parsing */
508 vorbis_info_init(&vi);
509 vorbis_comment_init(&vc);
510
511 /* init supporting Theora structures needed in header parsing */
512 th_comment_init(&tc);
513 th_info_init(&ti);
514
515 /* Ogg file open; parse the headers */
516 /* Only interested in Vorbis/Theora streams */
517 while(!stateflag){
518 int ret=buffer_data(infile,&oy);
519 if(ret==0)break;
520 while(ogg_sync_pageout(&oy,&og)>0){
521 ogg_stream_state test;
522
523 /* is this a mandated initial header? If not, stop parsing */
524 if(!ogg_page_bos(&og)){
525 /* don't leak the page; get it into the appropriate stream */
526 queue_page(&og);
527 stateflag=1;
528 break;
529 }
530
531 ogg_stream_init(&test,ogg_page_serialno(&og));
532 ogg_stream_pagein(&test,&og);
533 ogg_stream_packetout(&test,&op);
534
535
536 /* identify the codec: try theora */
537 if(!theora_p && th_decode_headerin(&ti,&tc,&ts,&op)>=0){
538 /* it is theora */
539 memcpy(&to,&test,sizeof(test));
540 theora_p=1;
541 }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){
542 /* it is vorbis */
543 memcpy(&vo,&test,sizeof(test));
544 vorbis_p=1;
545 }else{
546 /* whatever it is, we don't care about it */
547 ogg_stream_clear(&test);
548 }
549 }
550 /* fall through to non-bos page parsing */
551 }
552
553 /* we're expecting more header packets. */
554 while((theora_p && theora_p<3) || (vorbis_p && vorbis_p<3)){
555 int ret;
556
557 /* look for further theora headers */
558 while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))){
559 if(ret<0){
560 fprintf(stderr,"Error parsing Theora stream headers; "
561 "corrupt stream?\n");
562 exit(1);
563 }
564 if(!th_decode_headerin(&ti,&tc,&ts,&op)){
565 fprintf(stderr,"Error parsing Theora stream headers; "
566 "corrupt stream?\n");
567 exit(1);
568 }
569 theora_p++;
570 }
571
572 /* look for more vorbis header packets */
573 while(vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&vo,&op))){
574 if(ret<0){
575 fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n");
576 exit(1);
577 }
578 if(vorbis_synthesis_headerin(&vi,&vc,&op)){
579 fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n");
580 exit(1);
581 }
582 vorbis_p++;
583 if(vorbis_p==3)break;
584 }
585
586 /* The header pages/packets will arrive before anything else we
587 care about, or the stream is not obeying spec */
588
589 if(ogg_sync_pageout(&oy,&og)>0){
590 queue_page(&og); /* demux into the appropriate stream */
591 }else{
592 int ret=buffer_data(infile,&oy); /* someone needs more data */
593 if(ret==0){
594 fprintf(stderr,"End of file while searching for codec headers.\n");
595 exit(1);
596 }
597 }
598 }
599
600 /* and now we have it all. initialize decoders */
601 if(theora_p){
602 td=th_decode_alloc(&ti,ts);
603 printf("Ogg logical stream %lx is Theora %dx%d %.02f fps",
604 to.serialno,ti.pic_width,ti.pic_height,
605 (double)ti.fps_numerator/ti.fps_denominator);
606 px_fmt=ti.pixel_fmt;
607 switch(ti.pixel_fmt){
608 case TH_PF_420: printf(" 4:2:0 video\n"); break;
609 case TH_PF_422: printf(" 4:2:2 video\n"); break;
610 case TH_PF_444: printf(" 4:4:4 video\n"); break;
611 case TH_PF_RSVD:
612 default:
613 printf(" video\n (UNKNOWN Chroma sampling!)\n");
614 break;
615 }
616 if(ti.pic_width!=ti.frame_width || ti.pic_height!=ti.frame_height)
617 printf(" Frame content is %dx%d with offset (%d,%d).\n",
618 ti.frame_width, ti.frame_height, ti.pic_x, ti.pic_y);
619 report_colorspace(&ti);
620 dump_comments(&tc);
621 th_decode_ctl(td,TH_DECCTL_GET_PPLEVEL_MAX,&pp_level_max,
622 sizeof(pp_level_max));
623 pp_level=pp_level_max;
624 th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,sizeof(pp_level));
625 pp_inc=0;
626
627 /*{
628 int arg = 0xffff;
629 th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MBMODE,&arg,sizeof(arg));
630 th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MV,&arg,sizeof(arg));
631 th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_QI,&arg,sizeof(arg));
632 arg=10;
633 th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_BITS,&arg,sizeof(arg));
634 }*/
635 }else{
636 /* tear down the partial theora setup */
637 th_info_clear(&ti);
638 th_comment_clear(&tc);
639 }
640
641 th_setup_free(ts);
642
643 if(vorbis_p){
644 vorbis_synthesis_init(&vd,&vi);
645 vorbis_block_init(&vd,&vb);
646 fprintf(stderr,"Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.\n",
647 vo.serialno,vi.channels,vi.rate);
648 }else{
649 /* tear down the partial vorbis setup */
650 vorbis_info_clear(&vi);
651 vorbis_comment_clear(&vc);
652 }
653
654 /* open audio */
655 if(vorbis_p)open_audio();
656
657 /* open video */
658 if(theora_p)open_video();
659
660 /* install signal handler as SDL clobbered the default */
661 signal (SIGINT, sigint_handler);
662
663 /* on to the main decode loop. We assume in this example that audio
664 and video start roughly together, and don't begin playback until
665 we have a start frame for both. This is not necessarily a valid
666 assumption in Ogg A/V streams! It will always be true of the
667 example_encoder (and most streams) though. */
668
669 stateflag=0; /* playback has not begun */
670 while(!got_sigint){
671
672 /* we want a video and audio frame ready to go at all times. If
673 we have to buffer incoming, buffer the compressed data (ie, let
674 ogg do the buffering) */
675 while(vorbis_p && !audiobuf_ready){
676 int ret;
677 float **pcm;
678
679 /* if there's pending, decoded audio, grab it */
680 if((ret=vorbis_synthesis_pcmout(&vd,&pcm))>0){
681 int count=audiobuf_fill/2;
682 int maxsamples=(audiofd_fragsize-audiobuf_fill)/2/vi.channels;
683 for(i=0;i<ret && i<maxsamples;i++)
684 for(j=0;j<vi.channels;j++){
685 int val=rint(pcm[j][i]*32767.f);
686 if(val>32767)val=32767;
687 if(val<-32768)val=-32768;
688 audiobuf[count++]=val;
689 }
690 vorbis_synthesis_read(&vd,i);
691 audiobuf_fill+=i*vi.channels*2;
692 if(audiobuf_fill==audiofd_fragsize)audiobuf_ready=1;
693 if(vd.granulepos>=0)
694 audiobuf_granulepos=vd.granulepos-ret+i;
695 else
696 audiobuf_granulepos+=i;
697
698 }else{
699
700 /* no pending audio; is there a pending packet to decode? */
701 if(ogg_stream_packetout(&vo,&op)>0){
702 if(vorbis_synthesis(&vb,&op)==0) /* test for success! */
703 vorbis_synthesis_blockin(&vd,&vb);
704 }else /* we need more data; break out to suck in another page */
705 break;
706 }
707 }
708
709 while(theora_p && !videobuf_ready){
710 /* theora is one in, one out... */
711 if(ogg_stream_packetout(&to,&op)>0){
712
713 if(pp_inc){
714 pp_level+=pp_inc;
715 th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,
716 sizeof(pp_level));
717 pp_inc=0;
718 }
719 /*HACK: This should be set after a seek or a gap, but we might not have
720 a granulepos for the first packet (we only have them for the last
721 packet on a page), so we just set it as often as we get it.
722 To do this right, we should back-track from the last packet on the
723 page and compute the correct granulepos for the first packet after
724 a seek or a gap.*/
725 if(op.granulepos>=0){
726 th_decode_ctl(td,TH_DECCTL_SET_GRANPOS,&op.granulepos,
727 sizeof(op.granulepos));
728 }
729 if(th_decode_packetin(td,&op,&videobuf_granulepos)==0){
730 videobuf_time=th_granule_time(td,videobuf_granulepos);
731 frames++;
732
733 /* is it already too old to be useful? This is only actually
734 useful cosmetically after a SIGSTOP. Note that we have to
735 decode the frame even if we don't show it (for now) due to
736 keyframing. Soon enough libtheora will be able to deal
737 with non-keyframe seeks. */
738
739 if(videobuf_time>=get_time())
740 videobuf_ready=1;
741 else{
742 /*If we are too slow, reduce the pp level.*/
743 pp_inc=pp_level>0?-1:0;
744 dropped++;
745 }
746 }
747
748 }else
749 break;
750 }
751
752 if(!videobuf_ready && !audiobuf_ready && feof(infile))break;
753
754 if(!videobuf_ready || !audiobuf_ready){
755 /* no data yet for somebody. Grab another page */
756 buffer_data(infile,&oy);
757 while(ogg_sync_pageout(&oy,&og)>0){
758 queue_page(&og);
759 }
760 }
761
762 /* If playback has begun, top audio buffer off immediately. */
763 if(stateflag) audio_write_nonblocking();
764
765 /* are we at or past time for this video frame? */
766 if(stateflag && videobuf_ready && videobuf_time<=get_time()){
767 video_write();
768 videobuf_ready=0;
769 }
770
771 if(stateflag &&
772 (audiobuf_ready || !vorbis_p) &&
773 (videobuf_ready || !theora_p) &&
774 !got_sigint){
775 /* we have an audio frame ready (which means the audio buffer is
776 full), it's not time to play video, so wait until one of the
777 audio buffer is ready or it's near time to play video */
778
779 /* set up select wait on the audiobuffer and a timeout for video */
780 struct timeval timeout;
781 fd_set writefs;
782 fd_set empty;
783 int n=0;
784
785 FD_ZERO(&writefs);
786 FD_ZERO(&empty);
787 if(audiofd>=0){
788 FD_SET(audiofd,&writefs);
789 n=audiofd+1;
790 }
791
792 if(theora_p){
793 double tdiff;
794 long milliseconds;
795 tdiff=videobuf_time-get_time();
796 /*If we have lots of extra time, increase the post-processing level.*/
797 if(tdiff>ti.fps_denominator*0.25/ti.fps_numerator){
798 pp_inc=pp_level<pp_level_max?1:0;
799 }
800 else if(tdiff<ti.fps_denominator*0.05/ti.fps_numerator){
801 pp_inc=pp_level>0?-1:0;
802 }
803 milliseconds=tdiff*1000-5;
804 if(milliseconds>500)milliseconds=500;
805 if(milliseconds>0){
806 timeout.tv_sec=milliseconds/1000;
807 timeout.tv_usec=(milliseconds%1000)*1000;
808
809 n=select(n,&empty,&writefs,&empty,&timeout);
810 if(n)audio_calibrate_timer(0);
811 }
812 }else{
813 select(n,&empty,&writefs,&empty,NULL);
814 }
815 }
816
817 /* if our buffers either don't exist or are ready to go,
818 we can begin playback */
819 if((!theora_p || videobuf_ready) &&
820 (!vorbis_p || audiobuf_ready))stateflag=1;
821 /* same if we've run out of input */
822 if(feof(infile))stateflag=1;
823
824 }
825
826 /* tear it all down */
827
828 audio_close();
829 SDL_Quit();
830
831 if(vorbis_p){
832 ogg_stream_clear(&vo);
833 vorbis_block_clear(&vb);
834 vorbis_dsp_clear(&vd);
835 vorbis_comment_clear(&vc);
836 vorbis_info_clear(&vi);
837 }
838 if(theora_p){
839 ogg_stream_clear(&to);
840 th_decode_free(td);
841 th_comment_clear(&tc);
842 th_info_clear(&ti);
843 }
844 ogg_sync_clear(&oy);
845
846 if(infile && infile!=stdin)fclose(infile);
847
848 fprintf(stderr,
849 "\r \r");
850 fprintf(stderr, "%d frames", frames);
851 if (dropped) fprintf(stderr, " (%d dropped)", dropped);
852 fprintf(stderr, "\n");
853 fprintf(stderr, "\nDone.\n");
854
855 return(0);
856
857 }
858