Add c64x changes for r17563, as they were accidentally omitted.
[theora.git] / examples / splayer.c
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  * Modified by M. Piacentini http://www.tabuleiro.com
16  * from the original Theora Alpha player_sample files
17  *
18  * Modified to build on Windows and use PortAudio as the audio
19  * and synchronization layer, calculating license.
20  *
21  * With SDL PortAudio it should be easy to compile on other platforms and
22  * sound providers like DirectSound
23  * just include the corresponding .c file (see PortAudio main documentation
24  * for additional information)
25
26  ********************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <math.h>
37 #include <signal.h>
38 #include "theora/theora.h"
39 #include "vorbis/codec.h"
40
41 #ifdef WIN32
42 #include <windows.h>
43 #include <io.h>
44 #endif
45
46 #include <portaudio.h>
47 #include <SDL.h>
48
49 /* for portaudio  */
50 #define FRAMES_PER_BUFFER (256)
51
52 /*start of portaudio helper functions, extracted from pablio directory*/
53
54 /* Pa_streamio routines modified by mauricio at xiph.org
55  * Modified version of Portable Audio Blocking read/write utility.
56  * from the original PABLIO files
57  * Modified to support only playback buffers, direct access
58  * to the underlying stream time and remove blocking operations*/
59
60 /* PortAudio copyright notice follows */
61
62 /*
63  * Author: Phil Burk, http://www.softsynth.com
64  *
65  * This program uses the PortAudio Portable Audio Library.
66  * For more information see: http://www.audiomulch.com/portaudio/
67  * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
68  *
69  * Permission is hereby granted, free of charge, to any person obtaining
70  * a copy of this software and associated documentation files
71  * (the "Software"), to deal in the Software without restriction,
72  * including without limitation the rights to use, copy, modify, merge,
73  * publish, distribute, sublicense, and/or sell copies of the Software,
74  * and to permit persons to whom the Software is furnished to do so,
75  * subject to the following conditions:
76  *
77  * The above copyright notice and this permission notice shall be
78  * included in all copies or substantial portions of the Software.
79  *
80  */
81
82 typedef struct
83 {
84     long   bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */
85 /* These are declared volatile because they are written by a different thread than the reader. */
86     volatile long   writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */
87     volatile long   readIndex;  /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */
88     long   bigMask;    /* Used for wrapping indices with extra bit to distinguish full/empty. */
89     long   smallMask;  /* Used for fitting indices to buffer. */
90     char *buffer;
91 }
92 RingBuffer;
93
94 typedef struct
95 {
96     RingBuffer   outFIFO;
97     PortAudioStream *stream;
98     int          bytesPerFrame;
99     int          samplesPerFrame;
100 }
101 PASTREAMIO_Stream;
102
103 /* Values for flags for OpenAudioStream(). */
104 /* Keep PABLIO ones*/
105
106 #define PASTREAMIO_READ     (1<<0)
107 #define PASTREAMIO_WRITE    (1<<1)
108 #define PASTREAMIO_READ_WRITE    (PABLIO_READ|PABLIO_WRITE)
109 #define PASTREAMIO_MONO     (1<<2)
110 #define PASTREAMIO_STEREO   (1<<3)
111
112 /***************************************************************************
113 ** Helper function added to report stream time. */
114
115 PaTimestamp GetAudioStreamTime( PASTREAMIO_Stream *aStream ){
116   return Pa_StreamTime( aStream->stream ) ;
117 }
118
119  /***************************************************************************
120 ** Clear buffer. Should only be called when buffer is NOT being read. */
121 void RingBuffer_Flush( RingBuffer *rbuf )
122 {
123     rbuf->writeIndex = rbuf->readIndex = 0;
124 }
125
126 /***************************************************************************
127  * Initialize FIFO.
128  * numBytes must be power of 2, returns -1 if not.
129  */
130 long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
131 {
132     if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */
133     rbuf->bufferSize = numBytes;
134     rbuf->buffer = (char *)dataPtr;
135     RingBuffer_Flush( rbuf );
136     rbuf->bigMask = (numBytes*2)-1;
137     rbuf->smallMask = (numBytes)-1;
138     return 0;
139 }
140 /***************************************************************************
141 ** Return number of bytes available for reading. */
142 long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
143 {
144     return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
145 }
146 /***************************************************************************
147 ** Return number of bytes available for writing. */
148 long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
149 {
150     return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
151 }
152
153
154 /***************************************************************************
155 ** Get address of region(s) to which we can write data.
156 ** If the region is contiguous, size2 will be zero.
157 ** If non-contiguous, size2 will be the size of second region.
158 ** Returns room available to be written or numBytes, whichever is smaller.
159 */
160 long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
161                                  void **dataPtr1, long *sizePtr1,
162                                  void **dataPtr2, long *sizePtr2 )
163 {
164     long   index;
165     long   available = RingBuffer_GetWriteAvailable( rbuf );
166     if( numBytes > available ) numBytes = available;
167     /* Check to see if write is not contiguous. */
168     index = rbuf->writeIndex & rbuf->smallMask;
169     if( (index + numBytes) > rbuf->bufferSize )
170     {
171         /* Write data in two blocks that wrap the buffer. */
172         long   firstHalf = rbuf->bufferSize - index;
173         *dataPtr1 = &rbuf->buffer[index];
174         *sizePtr1 = firstHalf;
175         *dataPtr2 = &rbuf->buffer[0];
176         *sizePtr2 = numBytes - firstHalf;
177     }
178     else
179     {
180         *dataPtr1 = &rbuf->buffer[index];
181         *sizePtr1 = numBytes;
182         *dataPtr2 = NULL;
183         *sizePtr2 = 0;
184     }
185     return numBytes;
186 }
187
188
189 /***************************************************************************
190 */
191 long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
192 {
193     return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
194 }
195
196 /***************************************************************************
197 ** Get address of region(s) from which we can read data.
198 ** If the region is contiguous, size2 will be zero.
199 ** If non-contiguous, size2 will be the size of second region.
200 ** Returns room available to be written or numBytes, whichever is smaller.
201 */
202 long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
203                                 void **dataPtr1, long *sizePtr1,
204                                 void **dataPtr2, long *sizePtr2 )
205 {
206     long   index;
207     long   available = RingBuffer_GetReadAvailable( rbuf );
208     if( numBytes > available ) numBytes = available;
209     /* Check to see if read is not contiguous. */
210     index = rbuf->readIndex & rbuf->smallMask;
211     if( (index + numBytes) > rbuf->bufferSize )
212     {
213         /* Write data in two blocks that wrap the buffer. */
214         long firstHalf = rbuf->bufferSize - index;
215         *dataPtr1 = &rbuf->buffer[index];
216         *sizePtr1 = firstHalf;
217         *dataPtr2 = &rbuf->buffer[0];
218         *sizePtr2 = numBytes - firstHalf;
219     }
220     else
221     {
222         *dataPtr1 = &rbuf->buffer[index];
223         *sizePtr1 = numBytes;
224         *dataPtr2 = NULL;
225         *sizePtr2 = 0;
226     }
227     return numBytes;
228 }
229 /***************************************************************************
230 */
231 long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
232 {
233     return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
234 }
235
236 /***************************************************************************
237 ** Return bytes written. */
238 long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
239 {
240     long size1, size2, numWritten;
241     void *data1, *data2;
242     numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
243     if( size2 > 0 )
244     {
245
246         memcpy( data1, data, size1 );
247         data = ((char *)data) + size1;
248         memcpy( data2, data, size2 );
249     }
250     else
251     {
252         memcpy( data1, data, size1 );
253     }
254     RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
255     return numWritten;
256 }
257
258 /***************************************************************************
259 ** Return bytes read. */
260 long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
261 {
262     long size1, size2, numRead;
263     void *data1, *data2;
264     numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
265     if( size2 > 0 )
266     {
267         memcpy( data, data1, size1 );
268         data = ((char *)data) + size1;
269         memcpy( data, data2, size2 );
270     }
271     else
272     {
273         memcpy( data, data1, size1 );
274     }
275     RingBuffer_AdvanceReadIndex( rbuf, numRead );
276     return numRead;
277 }
278
279
280 /************************************************************************/
281 /******** Functions *****************************************************/
282 /************************************************************************/
283
284 /* Called from PortAudio.
285  * Read and write data only if there is room in FIFOs.
286  */
287 static int audioIOCallback( void *inputBuffer, void *outputBuffer,
288                                unsigned long framesPerBuffer,
289                                PaTimestamp outTime, void *userData )
290 {
291     PASTREAMIO_Stream *data = (PASTREAMIO_Stream*)userData;
292     long numBytes = data->bytesPerFrame * framesPerBuffer;
293     (void) outTime;
294     (void) inputBuffer;
295
296     if( outputBuffer != NULL )
297     {
298         int i;
299         int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes );
300         /* Zero out remainder of buffer if we run out of data. */
301         for( i=numRead; i<numBytes; i++ )
302         {
303             ((char *)outputBuffer)[i] = 0;
304         }
305     }
306
307     return 0;
308 }
309
310 /* Allocate buffer. */
311 static PaError PASTREAMIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame )
312 {
313     long numBytes = numFrames * bytesPerFrame;
314     char *buffer = (char *) malloc( numBytes );
315     if( buffer == NULL ) return paInsufficientMemory;
316     memset( buffer, 0, numBytes );
317     return (PaError) RingBuffer_Init( rbuf, numBytes, buffer );
318 }
319
320 /* Free buffer. */
321 static PaError PASTREAMIO_TermFIFO( RingBuffer *rbuf )
322 {
323     if( rbuf->buffer ) free( rbuf->buffer );
324     rbuf->buffer = NULL;
325     return paNoError;
326 }
327
328 /************************************************************
329  * Write data to ring buffer.
330  * Will not return until all the data has been written.
331  */
332 long WriteAudioStream( PASTREAMIO_Stream *aStream, void *data, long numFrames )
333 {
334     long bytesWritten;
335     char *p = (char *) data;
336     long numBytes = aStream->bytesPerFrame * numFrames;
337     while( numBytes > 0)
338     {
339         bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );
340         numBytes -= bytesWritten;
341         p += bytesWritten;
342         if( numBytes > 0) Pa_Sleep(10);
343     }
344     return numFrames;
345 }
346
347
348 /************************************************************
349  * Return the number of frames that could be written to the stream without
350  * having to wait.
351  */
352 long GetAudioStreamWriteable( PASTREAMIO_Stream *aStream )
353 {
354     int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
355     return bytesEmpty / aStream->bytesPerFrame;
356 }
357
358
359
360 /************************************************************/
361 unsigned long RoundUpToNextPowerOf2( unsigned long n )
362 {
363     long numBits = 0;
364     if( ((n-1) & n) == 0) return n; /* Already Power of two. */
365     while( n > 0 )
366     {
367         n= n>>1;
368         numBits++;
369     }
370     return (1<<numBits);
371 }
372
373 /* forward prototype */
374 PaError CloseAudioStream( PASTREAMIO_Stream *aStream );
375
376 /************************************************************
377  * Opens a PortAudio stream with default characteristics.
378  * Allocates PASTREAMIO_Stream structure.
379  *
380  * flags parameter can be an ORed combination of:
381  *    PABLIO_WRITE, 
382  *    and either PABLIO_MONO or PABLIO_STEREO
383  */
384 PaError OpenAudioStream( PASTREAMIO_Stream **rwblPtr, double sampleRate,
385                          PaSampleFormat format, long flags )
386 {
387     long   bytesPerSample;
388     long   doWrite = 0;
389     PaError err;
390     PASTREAMIO_Stream *aStream;
391     long   minNumBuffers;
392     long   numFrames;
393
394     /* Allocate PASTREAMIO_Stream structure for caller. */
395     aStream = (PASTREAMIO_Stream *) malloc( sizeof(PASTREAMIO_Stream) );
396     if( aStream == NULL ) return paInsufficientMemory;
397     memset( aStream, 0, sizeof(PASTREAMIO_Stream) );
398
399     /* Determine size of a sample. */
400     bytesPerSample = Pa_GetSampleSize( format );
401     if( bytesPerSample < 0 )
402     {
403         err = (PaError) bytesPerSample;
404         goto error;
405     }
406     aStream->samplesPerFrame = ((flags&PASTREAMIO_MONO) != 0) ? 1 : 2;
407     aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;
408
409     /* Initialize PortAudio  */
410     err = Pa_Initialize();
411     if( err != paNoError ) goto error;
412
413     /* Warning: numFrames must be larger than amount of data processed per interrupt
414      *    inside PA to prevent glitches. Just to be safe, adjust size upwards.
415      */
416     minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
417     numFrames = minNumBuffers * FRAMES_PER_BUFFER;
418     numFrames = RoundUpToNextPowerOf2( numFrames );
419
420     /* Initialize Ring Buffer */
421     doWrite = ((flags & PASTREAMIO_WRITE) != 0);
422
423     if(doWrite)
424     {
425         err = PASTREAMIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );
426         if( err != paNoError ) goto error;
427         /* Make Write FIFO appear full initially. 
428         numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
429         RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes );*/
430     }
431
432     /* Open a PortAudio stream that we will use to communicate with the underlying
433      * audio drivers. */
434     err = Pa_OpenStream(
435               &aStream->stream,
436               paNoDevice,
437               0 ,
438               format,
439               NULL,
440               Pa_GetDefaultOutputDeviceID() ,
441               aStream->samplesPerFrame ,
442               format,
443               NULL,
444               sampleRate,
445               FRAMES_PER_BUFFER,
446               minNumBuffers,
447               paClipOff,       /* we won't output out of range samples so don't bother clipping them */
448               audioIOCallback,
449               aStream );
450     if( err != paNoError ) goto error;
451
452     *rwblPtr = aStream;
453     return paNoError;
454
455 error:
456     CloseAudioStream( aStream );
457     *rwblPtr = NULL;
458     return err;
459 }
460
461 PaError StartAudioStream( PASTREAMIO_Stream *aStream)
462 {
463         PaError err;
464         err = Pa_StartStream( aStream->stream );
465     if( err != paNoError ) goto error;
466
467     return paNoError;
468 error:
469     CloseAudioStream( aStream );
470     return err;
471 }
472
473
474 /************************************************************/
475 PaError CloseAudioStream( PASTREAMIO_Stream *aStream )
476 {
477     PaError err;
478     int bytesEmpty;
479     int byteSize = aStream->outFIFO.bufferSize;
480
481     /* If we are writing data, make sure we play everything written. */
482     if( byteSize > 0 )
483     {
484         bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
485         while( bytesEmpty < byteSize )
486         {
487             Pa_Sleep( 10 );
488             bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
489         }
490     }
491
492     err = Pa_StopStream( aStream->stream );
493     if( err != paNoError ) goto error;
494     err = Pa_CloseStream( aStream->stream );
495     if( err != paNoError ) goto error;
496     Pa_Terminate();
497
498 error:
499     PASTREAMIO_TermFIFO( &aStream->outFIFO );
500     free( aStream );
501     return err;
502 }
503
504 /* -- end of portaudio specific routines --*/
505
506 /* portaudio related global types  */
507 #define PA_SAMPLE_TYPE  paInt16
508 typedef short SAMPLE;
509 #define SAMPLE_SILENCE  (0)
510
511 PASTREAMIO_Stream  *aOutStream; /* our modified stream buffer*/
512 SAMPLE      *samples; /*local buffer for samples*/
513 double latency_sec = 0;
514
515 /* ticks information to be used if the audio stream is not present */
516 int    currentTicks = -1;
517
518 /* initial state of the audio stream */
519 int isPlaying = 0;
520 PaError err;
521
522 /* Ogg and codec state for demux/decode */
523 ogg_sync_state   oy;
524 ogg_page         og;
525 ogg_stream_state vo;
526 ogg_stream_state to;
527 theora_info      ti;
528 theora_comment   tc;
529 theora_state     td;
530 vorbis_info      vi;
531 vorbis_dsp_state vd;
532 vorbis_block     vb;
533 vorbis_comment   vc;
534
535 int              theora_p=0;
536 int              vorbis_p=0;
537 int              stateflag=0;
538
539 FILE * infile = NULL;
540
541 /* SDL Video playback structures */
542 SDL_Surface *screen;
543 SDL_Overlay *yuv_overlay;
544 SDL_Rect rect;
545
546 /* single frame video buffering */
547 int          videobuf_ready=0;
548 ogg_int64_t  videobuf_granulepos=-1;
549 double       videobuf_time=0;
550
551 int          audiobuf_ready=0;
552 ogg_int64_t  audiobuf_granulepos=0; /* time position of last sample */
553
554 static int open_audio(){
555     /* this will open one circular audio stream */
556     /* build on top of portaudio routines */
557     /* implementation based on file pastreamio.c */
558
559     int numSamples;
560     int numBytes;
561
562     int minNumBuffers;
563     int numFrames;
564
565     minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, vi.rate );
566     numFrames = minNumBuffers * FRAMES_PER_BUFFER;
567     numFrames = RoundUpToNextPowerOf2( numFrames );
568
569     numSamples = numFrames * vi.channels;
570     numBytes = numSamples * sizeof(SAMPLE);
571
572     samples = (SAMPLE *) malloc( numBytes );
573
574     /* store our latency calculation here */
575     latency_sec =  (double) numFrames / vi.rate / vi.channels;
576     printf( "Latency: %.04f\n", latency_sec );
577
578     err = OpenAudioStream( &aOutStream, vi.rate, PA_SAMPLE_TYPE,
579                            (PASTREAMIO_WRITE | PASTREAMIO_STEREO) );
580     if( err != paNoError ) goto error;
581     return err;
582 error:
583     CloseAudioStream( aOutStream );
584     printf( "An error occured while opening the portaudio stream\n" );
585     printf( "Error number: %d\n", err );
586     printf( "Error message: %s\n", Pa_GetErrorText( err ) );
587     return err;
588
589 }
590
591 static int start_audio(){
592     err = StartAudioStream(aOutStream);
593     if( err != paNoError ) goto error;
594
595     return err;
596 error:
597     CloseAudioStream( aOutStream );
598     printf( "An error occured while opening the portaudio stream\n" );
599     printf( "Error number: %d\n", err );
600     printf( "Error message: %s\n", Pa_GetErrorText( err ) );
601     return err;
602 }
603
604 static int audio_close(void){
605     err = CloseAudioStream( aOutStream );
606     if( err != paNoError ) goto error;
607
608     free(samples);
609     return err;
610 error:
611     Pa_Terminate();
612     printf( "An error occured while closing the portaudio stream\n" );
613     printf( "Error number: %d\n", err );
614     printf( "Error message: %s\n", Pa_GetErrorText( err ) );
615     return err;
616 }
617
618
619 double get_time() {
620     static Uint32 startticks = 0;
621     double curtime;
622     if (vorbis_p) {
623       /* not entirely accurate with the WAVE OUT device, but good enough
624          at this stage. Needs to be reworked to account for blank audio
625          data written to the stream... */
626       curtime = (double) (GetAudioStreamTime( aOutStream ) / vi.rate) - latency_sec;
627       if (curtime<0.0) curtime = 0.0;
628     } else {
629       /* initialize timer variable if not set yet */
630       if (startticks==0)
631         startticks = SDL_GetTicks();
632       curtime = 1.0e-3 * (double)(SDL_GetTicks() - startticks);
633     }
634     return curtime;
635 }
636
637
638 static void open_video(void){
639   /* taken from player_sample.c test file for theora alpha */
640
641   if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
642     printf("Unable to initialize SDL: %s\n", SDL_GetError());
643     exit(1);
644   }
645   
646   screen = SDL_SetVideoMode(ti.frame_width, ti.frame_height, 0, SDL_SWSURFACE);
647   if ( screen == NULL ) {
648     printf("Unable to set %dx%d video mode: %s\n",
649            ti.frame_width,ti.frame_height,SDL_GetError());
650     exit(1);
651   }
652
653   yuv_overlay = SDL_CreateYUVOverlay(ti.frame_width, ti.frame_height,
654                                      SDL_YV12_OVERLAY,
655                                      screen);
656   if ( yuv_overlay == NULL ) {
657     printf("SDL: Couldn't create SDL_yuv_overlay: %s\n",
658            SDL_GetError());
659     exit(1);
660   }
661   rect.x = 0;
662   rect.y = 0;
663   rect.w = ti.frame_width;
664   rect.h = ti.frame_height;
665
666   SDL_DisplayYUVOverlay(yuv_overlay, &rect);
667 }
668
669 static void video_write(void){
670   /* taken from player_sample.c test file for theora alpha */
671   int i;
672   yuv_buffer yuv;
673   int crop_offset;
674   theora_decode_YUVout(&td,&yuv);
675
676   /* Lock SDL_yuv_overlay */
677   if ( SDL_MUSTLOCK(screen) ) {
678     if ( SDL_LockSurface(screen) < 0 ) return;
679   }
680   if (SDL_LockYUVOverlay(yuv_overlay) < 0) return;
681
682   /* let's draw the data (*yuv[3]) on a SDL screen (*screen) */
683   /* deal with border stride */
684   /* reverse u and v for SDL */
685   /* and crop input properly, respecting the encoded frame rect */
686   crop_offset=ti.offset_x+yuv.y_stride*ti.offset_y;
687   for(i=0;i<yuv_overlay->h;i++)
688     memcpy(yuv_overlay->pixels[0]+yuv_overlay->pitches[0]*i,
689            yuv.y+crop_offset+yuv.y_stride*i,
690            yuv_overlay->w);
691   crop_offset=(ti.offset_x/2)+(yuv.uv_stride)*(ti.offset_y/2);
692   for(i=0;i<yuv_overlay->h/2;i++){
693     memcpy(yuv_overlay->pixels[1]+yuv_overlay->pitches[1]*i,
694            yuv.v+crop_offset+yuv.uv_stride*i,
695            yuv_overlay->w/2);
696     memcpy(yuv_overlay->pixels[2]+yuv_overlay->pitches[2]*i,
697            yuv.u+crop_offset+yuv.uv_stride*i,
698            yuv_overlay->w/2);
699   }
700
701   /* Unlock SDL_yuv_overlay */
702   SDL_UnlockYUVOverlay(yuv_overlay);
703   if ( SDL_MUSTLOCK(screen) ) {
704     SDL_UnlockSurface(screen);
705   }
706
707   /* Show, baby, show! */
708   SDL_DisplayYUVOverlay(yuv_overlay, &rect);
709 }
710
711 static void usage(void){
712   printf("Usage: splayer <ogg_file>\n"
713 #ifdef WIN32
714     "\n"
715     "or drag and drop an ogg file over the .exe\n\n"
716 #endif
717   );
718 }
719
720 /* dump the theora (or vorbis) comment header */
721 static int dump_comments(theora_comment *tc){
722   int i, len;
723   char *value;
724
725   printf("Encoded by %s\n",tc->vendor);
726   if(tc->comments){
727     printf("theora comment header:\n");
728     for(i=0;i<tc->comments;i++){
729       if(tc->user_comments[i]){
730         len=tc->comment_lengths[i];
731         value=malloc(len+1);
732         memcpy(value,tc->user_comments[i],len);
733         value[len]='\0';
734         printf("\t%s\n", value);
735         free(value);
736       }
737     }
738   }
739   return(0);
740 }
741
742 /* Report the encoder-specified colorspace for the video, if any.
743    We don't actually make use of the information in this example;
744    a real player should attempt to perform color correction for
745    whatever display device it supports. */
746 static void report_colorspace(theora_info *ti)
747 {
748     switch(ti->colorspace){
749       case OC_CS_UNSPECIFIED:
750         /* nothing to report */
751         break;;
752       case OC_CS_ITU_REC_470M:
753         fprintf(stderr,"  encoder specified ITU Rec 470M color.\n");
754         break;;
755       case OC_CS_ITU_REC_470BG:
756         fprintf(stderr,"  encoder specified ITU Rec 470BG color.\n");
757         break;;
758       default:
759         fprintf(stderr,"warning: encoder specified unknown colorspace (%d).\n",
760             ti->colorspace);
761         break;;
762     }
763 }
764
765 /* Helper; just grab some more compressed bitstream and sync it for
766    page extraction */
767 int buffer_data(ogg_sync_state *oy){
768   char *buffer=ogg_sync_buffer(oy,4096);
769   int bytes=fread(buffer,1,4096,infile);
770   ogg_sync_wrote(oy,bytes);
771   return(bytes);
772 }
773
774 /* helper: push a page into the appropriate stream */
775 /* this can be done blindly; a stream won't accept a page
776                 that doesn't belong to it */
777 static int queue_page(ogg_page *page){
778   if(theora_p)ogg_stream_pagein(&to,page);
779   if(vorbis_p)ogg_stream_pagein(&vo,page);
780   return 0;
781 }
782
783 void parseHeaders(){
784   /* extracted from player_sample.c test file for theora alpha */
785   ogg_packet op;
786   /* Parse the headers */
787   /* Only interested in Vorbis/Theora streams */
788   while(!stateflag){
789     int ret=buffer_data(&oy);
790     if(ret==0)break;
791     while(ogg_sync_pageout(&oy,&og)>0){
792       ogg_stream_state test;
793
794       /* is this a mandated initial header? If not, stop parsing */
795       if(!ogg_page_bos(&og)){
796         /* don't leak the page; get it into the appropriate stream */
797         queue_page(&og);
798         stateflag=1;
799         break;
800       }
801
802       ogg_stream_init(&test,ogg_page_serialno(&og));
803       ogg_stream_pagein(&test,&og);
804       ogg_stream_packetout(&test,&op);
805
806       /* identify the codec: try theora */
807       if(!theora_p && theora_decode_header(&ti,&tc,&op)>=0){
808         /* it is theora */
809         memcpy(&to,&test,sizeof(test));
810         theora_p=1;
811       }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){
812         /* it is vorbis */
813         memcpy(&vo,&test,sizeof(test));
814         vorbis_p=1;
815       }else{
816         /* whatever it is, we don't care about it */
817         ogg_stream_clear(&test);
818       }
819     }
820   }
821
822   /* we've now identified all the bitstreams. parse the secondary header packets. */
823   while((theora_p && theora_p<3) || (vorbis_p && vorbis_p<3)){
824     int ret;
825
826     /* look for further theora headers */
827     while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))){
828       if(ret<0){
829         printf("Error parsing Theora stream headers; corrupt stream?\n");
830         exit(1);
831       }
832       if(theora_decode_header(&ti,&tc,&op)){
833         printf("Error parsing Theora stream headers; corrupt stream?\n");
834         exit(1);
835       }
836       theora_p++;
837       if(theora_p==3)break;
838     }
839
840     /* look for more vorbis header packets */
841     while(vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&vo,&op))){
842       if(ret<0){
843         printf("Error parsing Vorbis stream headers; corrupt stream?\n");
844         exit(1);
845       }
846       if(vorbis_synthesis_headerin(&vi,&vc,&op)){
847         printf("Error parsing Vorbis stream headers; corrupt stream?\n");
848         exit(1);
849       }
850       vorbis_p++;
851       if(vorbis_p==3)break;
852     }
853
854     /* The header pages/packets will arrive before anything else we
855        care about, or the stream is not obeying spec */
856
857     if(ogg_sync_pageout(&oy,&og)>0){
858       queue_page(&og); /* demux into the appropriate stream */
859     }else{
860       int ret=buffer_data(&oy);
861       if(ret==0){
862         fprintf(stderr,"End of file while searching for codec headers.\n");
863         exit(1);
864       }
865     }
866   }
867 }
868
869 int main( int argc, char* argv[] ){
870
871   int i,j;
872   ogg_packet op;
873   SDL_Event event;
874   int hasdatatobuffer = 1;
875   int playbackdone = 0;
876   double now, delay, last_frame_time = 0;
877
878   int frameNum=0;
879   int skipNum=0;
880
881   /* takes first argument as file to play */
882   /* this works better on Windows and is more convenient
883      for drag and drop ogg files over the .exe */
884
885   if( argc != 2 )
886   {
887     usage();
888     exit(0);
889   }
890
891   infile  = fopen( argv[1], "rb" );
892
893   /* start up Ogg stream synchronization layer */
894   ogg_sync_init(&oy);
895
896   /* init supporting Vorbis structures needed in header parsing */
897   vorbis_info_init(&vi);
898   vorbis_comment_init(&vc);
899
900   /* init supporting Theora structures needed in header parsing */
901   theora_comment_init(&tc);
902   theora_info_init(&ti);
903
904   parseHeaders();
905
906   /* force audio off */
907   /* vorbis_p = 0; */
908
909   /* initialize decoders */
910   if(theora_p){
911     theora_decode_init(&td,&ti);
912     printf("Ogg logical stream %x is Theora %dx%d %.02f fps video\n"
913            "  Frame content is %dx%d with offset (%d,%d).\n",
914            to.serialno,ti.width,ti.height, (double)ti.fps_numerator/ti.fps_denominator,
915            ti.frame_width, ti.frame_height, ti.offset_x, ti.offset_y);
916     report_colorspace(&ti);
917     dump_comments(&tc);
918   }else{
919     /* tear down the partial theora setup */
920     theora_info_clear(&ti);
921     theora_comment_clear(&tc);
922   }
923   if(vorbis_p){
924     vorbis_synthesis_init(&vd,&vi);
925     vorbis_block_init(&vd,&vb);
926     printf("Ogg logical stream %x is Vorbis %d channel %d Hz audio.\n",
927            vo.serialno,vi.channels,vi.rate);
928   }else{
929     /* tear down the partial vorbis setup */
930     vorbis_info_clear(&vi);
931     vorbis_comment_clear(&vc);
932   }
933   /* open audio */
934   if(vorbis_p)open_audio();
935   /* open video */
936   if(theora_p)open_video();
937
938   /* our main loop */
939   while(!playbackdone){
940
941     /* break out on SDL quit event */
942     if ( SDL_PollEvent ( &event ) )
943     {
944       if ( event.type == SDL_QUIT ) break ;
945     }
946
947     /* get some audio data */
948     while(vorbis_p && !audiobuf_ready){
949       int ret;
950       float **pcm;
951       int count = 0;
952       int maxBytesToWrite;
953
954       /* is there pending audio? does it fit our circular buffer without blocking? */
955       ret=vorbis_synthesis_pcmout(&vd,&pcm);
956       maxBytesToWrite = GetAudioStreamWriteable(aOutStream);
957
958       if (maxBytesToWrite<=FRAMES_PER_BUFFER){
959         /* break out until there is a significant amount of
960            data to avoid a series of small write operations. */
961         break;
962       }
963       /* if there's pending, decoded audio, grab it */
964       if((ret>0)&&(maxBytesToWrite>0)){
965
966         for(i=0;i<ret && i<(maxBytesToWrite/vi.channels);i++)
967           for(j=0;j<vi.channels;j++){
968             int val=(int)(pcm[j][i]*32767.f);
969             if(val>32767)val=32767;
970             if(val<-32768)val=-32768;
971             samples[count]=val;
972             count++;
973           }
974         if(WriteAudioStream( aOutStream, samples, i )) {
975           if(count==maxBytesToWrite){
976             audiobuf_ready=1;
977           }
978         }
979         vorbis_synthesis_read(&vd,i);
980
981         if(vd.granulepos>=0)
982           audiobuf_granulepos=vd.granulepos-ret+i;
983         else
984           audiobuf_granulepos+=i;
985
986       }else{
987
988         /* no pending audio; is there a pending packet to decode? */
989         if(ogg_stream_packetout(&vo,&op)>0){
990           if(vorbis_synthesis(&vb,&op)==0) /* test for success! */
991            vorbis_synthesis_blockin(&vd,&vb);
992         }else   /* we need more data; break out to suck in another page */
993           break;
994       }
995     } /* end audio cycle */
996
997     while(theora_p && !videobuf_ready){
998       /* get one video packet... */
999       if(ogg_stream_packetout(&to,&op)>0){
1000
1001         theora_decode_packetin(&td,&op);
1002
1003           videobuf_granulepos=td.granulepos;
1004           videobuf_time=theora_granule_time(&td,videobuf_granulepos);
1005           /* update the frame counter */
1006           frameNum++;
1007
1008           /* check if this frame time has not passed yet.
1009              If the frame is late we need to decode additonal
1010              ones and keep looping, since theora at this stage
1011              needs to decode all frames */
1012           now=get_time();
1013           delay=videobuf_time-now;
1014           if(delay>=0.0){
1015                 /* got a good frame, not late, ready to break out */
1016                 videobuf_ready=1;
1017           }else if(now-last_frame_time>=1.0){
1018                 /* display at least one frame per second, regardless */
1019                 videobuf_ready=1;
1020           }else{
1021                 fprintf(stderr, "dropping frame %d (%.3fs behind)\n",
1022                         frameNum, -delay);
1023            }
1024       }else{
1025         /* need more data */
1026         break;
1027       }
1028     }
1029
1030     if(!hasdatatobuffer && !videobuf_ready && !audiobuf_ready){
1031       isPlaying = 0;
1032       playbackdone = 1;
1033     }
1034
1035     /* if we're set for the next frame, sleep */
1036     if((!theora_p || videobuf_ready) &&
1037        (!vorbis_p || audiobuf_ready)){
1038         int ticks = 1.0e3*(videobuf_time-get_time());
1039         if(ticks>0)
1040           SDL_Delay(ticks);
1041     }
1042
1043     if(videobuf_ready){
1044       /* time to write our cached frame */
1045       video_write();
1046       videobuf_ready=0;
1047       last_frame_time=get_time();
1048
1049       /* if audio has not started (first frame) then start it */
1050       if ((!isPlaying)&&(vorbis_p)){
1051         start_audio();
1052         isPlaying = 1;
1053       }
1054     }
1055
1056     /* HACK: always look for more audio data */
1057     audiobuf_ready=0;
1058
1059     /* buffer compressed data every loop */
1060     if(hasdatatobuffer){
1061       hasdatatobuffer=buffer_data(&oy);
1062       if(hasdatatobuffer==0){
1063         printf("Ogg buffering stopped, end of file reached.\n");
1064       }
1065     }
1066
1067     if (ogg_sync_pageout(&oy,&og)>0){
1068       queue_page(&og);
1069     }
1070
1071   } /* playbackdone */
1072
1073   /* show number of video frames decoded */
1074   printf( "\n");
1075   printf( "Frames decoded: %d", frameNum );
1076   if(skipNum)
1077     printf( " (only %d shown)", frameNum-skipNum);
1078   printf( "\n" );
1079
1080   /* tear it all down */
1081   fclose( infile );
1082
1083   if(vorbis_p){
1084     audio_close();
1085
1086     ogg_stream_clear(&vo);
1087     vorbis_block_clear(&vb);
1088     vorbis_dsp_clear(&vd);
1089     vorbis_comment_clear(&vc);
1090     vorbis_info_clear(&vi);
1091   }
1092   if(theora_p){
1093     ogg_stream_clear(&to);
1094     theora_clear(&td);
1095     theora_comment_clear(&tc);
1096     theora_info_clear(&ti);
1097   }
1098   ogg_sync_clear(&oy);
1099
1100   printf("\r                                                              "
1101          "\nDone.\n");
1102
1103   SDL_Quit();
1104
1105   return(0);
1106
1107 }