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