1 /********************************************************************
3 * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
5 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
6 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
8 * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2003 *
9 * by the Xiph.Org Foundation http://www.xiph.org/ *
11 ********************************************************************
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
18 * Modified to build on Windows and use PortAudio as the audio
19 * and synchronization layer, calculating license.
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)
26 ********************************************************************/
38 #include "theora/theora.h"
39 #include "vorbis/codec.h"
46 #include <portaudio.h>
50 #define FRAMES_PER_BUFFER (256)
52 /*start of portaudio helper functions, extracted from pablio directory*/
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*/
60 /* PortAudio copyright notice follows */
63 * Author: Phil Burk, http://www.softsynth.com
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
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:
77 * The above copyright notice and this permission notice shall be
78 * included in all copies or substantial portions of the Software.
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. */
97 PortAudioStream
*stream
;
103 /* Values for flags for OpenAudioStream(). */
104 /* Keep PABLIO ones*/
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)
112 /***************************************************************************
113 ** Helper function added to report stream time. */
115 PaTimestamp
GetAudioStreamTime( PASTREAMIO_Stream
*aStream
){
116 return Pa_StreamTime( aStream
->stream
) ;
119 /***************************************************************************
120 ** Clear buffer. Should only be called when buffer is NOT being read. */
121 void RingBuffer_Flush( RingBuffer
*rbuf
)
123 rbuf
->writeIndex
= rbuf
->readIndex
= 0;
126 /***************************************************************************
128 * numBytes must be power of 2, returns -1 if not.
130 long RingBuffer_Init( RingBuffer
*rbuf
, long numBytes
, void *dataPtr
)
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;
140 /***************************************************************************
141 ** Return number of bytes available for reading. */
142 long RingBuffer_GetReadAvailable( RingBuffer
*rbuf
)
144 return ( (rbuf
->writeIndex
- rbuf
->readIndex
) & rbuf
->bigMask
);
146 /***************************************************************************
147 ** Return number of bytes available for writing. */
148 long RingBuffer_GetWriteAvailable( RingBuffer
*rbuf
)
150 return ( rbuf
->bufferSize
- RingBuffer_GetReadAvailable(rbuf
));
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.
160 long RingBuffer_GetWriteRegions( RingBuffer
*rbuf
, long numBytes
,
161 void **dataPtr1
, long *sizePtr1
,
162 void **dataPtr2
, long *sizePtr2
)
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
)
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
;
180 *dataPtr1
= &rbuf
->buffer
[index
];
181 *sizePtr1
= numBytes
;
189 /***************************************************************************
191 long RingBuffer_AdvanceWriteIndex( RingBuffer
*rbuf
, long numBytes
)
193 return rbuf
->writeIndex
= (rbuf
->writeIndex
+ numBytes
) & rbuf
->bigMask
;
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.
202 long RingBuffer_GetReadRegions( RingBuffer
*rbuf
, long numBytes
,
203 void **dataPtr1
, long *sizePtr1
,
204 void **dataPtr2
, long *sizePtr2
)
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
)
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
;
222 *dataPtr1
= &rbuf
->buffer
[index
];
223 *sizePtr1
= numBytes
;
229 /***************************************************************************
231 long RingBuffer_AdvanceReadIndex( RingBuffer
*rbuf
, long numBytes
)
233 return rbuf
->readIndex
= (rbuf
->readIndex
+ numBytes
) & rbuf
->bigMask
;
236 /***************************************************************************
237 ** Return bytes written. */
238 long RingBuffer_Write( RingBuffer
*rbuf
, void *data
, long numBytes
)
240 long size1
, size2
, numWritten
;
242 numWritten
= RingBuffer_GetWriteRegions( rbuf
, numBytes
, &data1
, &size1
, &data2
, &size2
);
246 memcpy( data1
, data
, size1
);
247 data
= ((char *)data
) + size1
;
248 memcpy( data2
, data
, size2
);
252 memcpy( data1
, data
, size1
);
254 RingBuffer_AdvanceWriteIndex( rbuf
, numWritten
);
258 /***************************************************************************
259 ** Return bytes read. */
260 long RingBuffer_Read( RingBuffer
*rbuf
, void *data
, long numBytes
)
262 long size1
, size2
, numRead
;
264 numRead
= RingBuffer_GetReadRegions( rbuf
, numBytes
, &data1
, &size1
, &data2
, &size2
);
267 memcpy( data
, data1
, size1
);
268 data
= ((char *)data
) + size1
;
269 memcpy( data
, data2
, size2
);
273 memcpy( data
, data1
, size1
);
275 RingBuffer_AdvanceReadIndex( rbuf
, numRead
);
280 /************************************************************************/
281 /******** Functions *****************************************************/
282 /************************************************************************/
284 /* Called from PortAudio.
285 * Read and write data only if there is room in FIFOs.
287 static int audioIOCallback( void *inputBuffer
, void *outputBuffer
,
288 unsigned long framesPerBuffer
,
289 PaTimestamp outTime
, void *userData
)
291 PASTREAMIO_Stream
*data
= (PASTREAMIO_Stream
*)userData
;
292 long numBytes
= data
->bytesPerFrame
* framesPerBuffer
;
296 if( outputBuffer
!= NULL
)
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
++ )
303 ((char *)outputBuffer
)[i
] = 0;
310 /* Allocate buffer. */
311 static PaError
PASTREAMIO_InitFIFO( RingBuffer
*rbuf
, long numFrames
, long bytesPerFrame
)
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
);
321 static PaError
PASTREAMIO_TermFIFO( RingBuffer
*rbuf
)
323 if( rbuf
->buffer
) free( rbuf
->buffer
);
328 /************************************************************
329 * Write data to ring buffer.
330 * Will not return until all the data has been written.
332 long WriteAudioStream( PASTREAMIO_Stream
*aStream
, void *data
, long numFrames
)
335 char *p
= (char *) data
;
336 long numBytes
= aStream
->bytesPerFrame
* numFrames
;
339 bytesWritten
= RingBuffer_Write( &aStream
->outFIFO
, p
, numBytes
);
340 numBytes
-= bytesWritten
;
342 if( numBytes
> 0) Pa_Sleep(10);
348 /************************************************************
349 * Return the number of frames that could be written to the stream without
352 long GetAudioStreamWriteable( PASTREAMIO_Stream
*aStream
)
354 int bytesEmpty
= RingBuffer_GetWriteAvailable( &aStream
->outFIFO
);
355 return bytesEmpty
/ aStream
->bytesPerFrame
;
360 /************************************************************/
361 unsigned long RoundUpToNextPowerOf2( unsigned long n
)
364 if( ((n
-1) & n
) == 0) return n
; /* Already Power of two. */
373 /* forward prototype */
374 PaError
CloseAudioStream( PASTREAMIO_Stream
*aStream
);
376 /************************************************************
377 * Opens a PortAudio stream with default characteristics.
378 * Allocates PASTREAMIO_Stream structure.
380 * flags parameter can be an ORed combination of:
382 * and either PABLIO_MONO or PABLIO_STEREO
384 PaError
OpenAudioStream( PASTREAMIO_Stream
**rwblPtr
, double sampleRate
,
385 PaSampleFormat format
, long flags
)
390 PASTREAMIO_Stream
*aStream
;
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
) );
399 /* Determine size of a sample. */
400 bytesPerSample
= Pa_GetSampleSize( format
);
401 if( bytesPerSample
< 0 )
403 err
= (PaError
) bytesPerSample
;
406 aStream
->samplesPerFrame
= ((flags
&PASTREAMIO_MONO
) != 0) ? 1 : 2;
407 aStream
->bytesPerFrame
= bytesPerSample
* aStream
->samplesPerFrame
;
409 /* Initialize PortAudio */
410 err
= Pa_Initialize();
411 if( err
!= paNoError
) goto error
;
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.
416 minNumBuffers
= 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER
, sampleRate
);
417 numFrames
= minNumBuffers
* FRAMES_PER_BUFFER
;
418 numFrames
= RoundUpToNextPowerOf2( numFrames
);
420 /* Initialize Ring Buffer */
421 doWrite
= ((flags
& PASTREAMIO_WRITE
) != 0);
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 );*/
432 /* Open a PortAudio stream that we will use to communicate with the underlying
440 Pa_GetDefaultOutputDeviceID() ,
441 aStream
->samplesPerFrame
,
447 paClipOff
, /* we won't output out of range samples so don't bother clipping them */
450 if( err
!= paNoError
) goto error
;
456 CloseAudioStream( aStream
);
461 PaError
StartAudioStream( PASTREAMIO_Stream
*aStream
)
464 err
= Pa_StartStream( aStream
->stream
);
465 if( err
!= paNoError
) goto error
;
469 CloseAudioStream( aStream
);
474 /************************************************************/
475 PaError
CloseAudioStream( PASTREAMIO_Stream
*aStream
)
479 int byteSize
= aStream
->outFIFO
.bufferSize
;
481 /* If we are writing data, make sure we play everything written. */
484 bytesEmpty
= RingBuffer_GetWriteAvailable( &aStream
->outFIFO
);
485 while( bytesEmpty
< byteSize
)
488 bytesEmpty
= RingBuffer_GetWriteAvailable( &aStream
->outFIFO
);
492 err
= Pa_StopStream( aStream
->stream
);
493 if( err
!= paNoError
) goto error
;
494 err
= Pa_CloseStream( aStream
->stream
);
495 if( err
!= paNoError
) goto error
;
499 PASTREAMIO_TermFIFO( &aStream
->outFIFO
);
504 /* -- end of portaudio specific routines --*/
506 /* portaudio related global types */
507 #define PA_SAMPLE_TYPE paInt16
508 typedef short SAMPLE
;
509 #define SAMPLE_SILENCE (0)
511 PASTREAMIO_Stream
*aOutStream
; /* our modified stream buffer*/
512 SAMPLE
*samples
; /*local buffer for samples*/
513 double latency_sec
= 0;
515 /* ticks information to be used if the audio stream is not present */
516 int currentTicks
= -1;
518 /* initial state of the audio stream */
522 /* Ogg and codec state for demux/decode */
539 FILE * infile
= NULL
;
541 /* SDL Video playback structures */
543 SDL_Overlay
*yuv_overlay
;
546 /* single frame video buffering */
547 int videobuf_ready
=0;
548 ogg_int64_t videobuf_granulepos
=-1;
549 double videobuf_time
=0;
551 int audiobuf_ready
=0;
552 ogg_int64_t audiobuf_granulepos
=0; /* time position of last sample */
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 */
565 minNumBuffers
= 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER
, vi
.rate
);
566 numFrames
= minNumBuffers
* FRAMES_PER_BUFFER
;
567 numFrames
= RoundUpToNextPowerOf2( numFrames
);
569 numSamples
= numFrames
* vi
.channels
;
570 numBytes
= numSamples
* sizeof(SAMPLE
);
572 samples
= (SAMPLE
*) malloc( numBytes
);
574 /* store our latency calculation here */
575 latency_sec
= (double) numFrames
/ vi
.rate
/ vi
.channels
;
576 printf( "Latency: %.04f\n", latency_sec
);
578 err
= OpenAudioStream( &aOutStream
, vi
.rate
, PA_SAMPLE_TYPE
,
579 (PASTREAMIO_WRITE
| PASTREAMIO_STEREO
) );
580 if( err
!= paNoError
) goto 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
) );
591 static int start_audio(){
592 err
= StartAudioStream(aOutStream
);
593 if( err
!= paNoError
) goto 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
) );
604 static int audio_close(void){
605 err
= CloseAudioStream( aOutStream
);
606 if( err
!= paNoError
) goto error
;
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
) );
620 static Uint32 startticks
= 0;
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;
629 /* initialize timer variable if not set yet */
631 startticks
= SDL_GetTicks();
632 curtime
= 1.0e-3 * (double)(SDL_GetTicks() - startticks
);
638 static void open_video(void){
639 /* taken from player_sample.c test file for theora alpha */
641 if ( SDL_Init(SDL_INIT_VIDEO
) < 0 ) {
642 printf("Unable to initialize SDL: %s\n", SDL_GetError());
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());
653 yuv_overlay
= SDL_CreateYUVOverlay(ti
.frame_width
, ti
.frame_height
,
656 if ( yuv_overlay
== NULL
) {
657 printf("SDL: Couldn't create SDL_yuv_overlay: %s\n",
663 rect
.w
= ti
.frame_width
;
664 rect
.h
= ti
.frame_height
;
666 SDL_DisplayYUVOverlay(yuv_overlay
, &rect
);
669 static void video_write(void){
670 /* taken from player_sample.c test file for theora alpha */
674 theora_decode_YUVout(&td
,&yuv
);
676 /* Lock SDL_yuv_overlay */
677 if ( SDL_MUSTLOCK(screen
) ) {
678 if ( SDL_LockSurface(screen
) < 0 ) return;
680 if (SDL_LockYUVOverlay(yuv_overlay
) < 0) return;
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
,
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
,
696 memcpy(yuv_overlay
->pixels
[2]+yuv_overlay
->pitches
[2]*i
,
697 yuv
.u
+crop_offset
+yuv
.uv_stride
*i
,
701 /* Unlock SDL_yuv_overlay */
702 SDL_UnlockYUVOverlay(yuv_overlay
);
703 if ( SDL_MUSTLOCK(screen
) ) {
704 SDL_UnlockSurface(screen
);
707 /* Show, baby, show! */
708 SDL_DisplayYUVOverlay(yuv_overlay
, &rect
);
711 static void usage(void){
712 printf("Usage: splayer <ogg_file>\n"
715 "or drag and drop an ogg file over the .exe\n\n"
720 /* dump the theora (or vorbis) comment header */
721 static int dump_comments(theora_comment
*tc
){
725 printf("Encoded by %s\n",tc
->vendor
);
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
];
732 memcpy(value
,tc
->user_comments
[i
],len
);
734 printf("\t%s\n", value
);
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
)
748 switch(ti
->colorspace
){
749 case OC_CS_UNSPECIFIED
:
750 /* nothing to report */
752 case OC_CS_ITU_REC_470M
:
753 fprintf(stderr
," encoder specified ITU Rec 470M color.\n");
755 case OC_CS_ITU_REC_470BG
:
756 fprintf(stderr
," encoder specified ITU Rec 470BG color.\n");
759 fprintf(stderr
,"warning: encoder specified unknown colorspace (%d).\n",
765 /* Helper; just grab some more compressed bitstream and sync it for
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
);
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
);
784 /* extracted from player_sample.c test file for theora alpha */
786 /* Parse the headers */
787 /* Only interested in Vorbis/Theora streams */
789 int ret
=buffer_data(&oy
);
791 while(ogg_sync_pageout(&oy
,&og
)>0){
792 ogg_stream_state test
;
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 */
802 ogg_stream_init(&test
,ogg_page_serialno(&og
));
803 ogg_stream_pagein(&test
,&og
);
804 ogg_stream_packetout(&test
,&op
);
806 /* identify the codec: try theora */
807 if(!theora_p
&& theora_decode_header(&ti
,&tc
,&op
)>=0){
809 memcpy(&to
,&test
,sizeof(test
));
811 }else if(!vorbis_p
&& vorbis_synthesis_headerin(&vi
,&vc
,&op
)>=0){
813 memcpy(&vo
,&test
,sizeof(test
));
816 /* whatever it is, we don't care about it */
817 ogg_stream_clear(&test
);
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)){
826 /* look for further theora headers */
827 while(theora_p
&& (theora_p
<3) && (ret
=ogg_stream_packetout(&to
,&op
))){
829 printf("Error parsing Theora stream headers; corrupt stream?\n");
832 if(theora_decode_header(&ti
,&tc
,&op
)){
833 printf("Error parsing Theora stream headers; corrupt stream?\n");
837 if(theora_p
==3)break;
840 /* look for more vorbis header packets */
841 while(vorbis_p
&& (vorbis_p
<3) && (ret
=ogg_stream_packetout(&vo
,&op
))){
843 printf("Error parsing Vorbis stream headers; corrupt stream?\n");
846 if(vorbis_synthesis_headerin(&vi
,&vc
,&op
)){
847 printf("Error parsing Vorbis stream headers; corrupt stream?\n");
851 if(vorbis_p
==3)break;
854 /* The header pages/packets will arrive before anything else we
855 care about, or the stream is not obeying spec */
857 if(ogg_sync_pageout(&oy
,&og
)>0){
858 queue_page(&og
); /* demux into the appropriate stream */
860 int ret
=buffer_data(&oy
);
862 fprintf(stderr
,"End of file while searching for codec headers.\n");
869 int main( int argc
, char* argv
[] ){
874 int hasdatatobuffer
= 1;
875 int playbackdone
= 0;
876 double now
, delay
, last_frame_time
= 0;
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 */
891 infile
= fopen( argv
[1], "rb" );
893 /* start up Ogg stream synchronization layer */
896 /* init supporting Vorbis structures needed in header parsing */
897 vorbis_info_init(&vi
);
898 vorbis_comment_init(&vc
);
900 /* init supporting Theora structures needed in header parsing */
901 theora_comment_init(&tc
);
902 theora_info_init(&ti
);
906 /* force audio off */
909 /* initialize decoders */
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
);
919 /* tear down the partial theora setup */
920 theora_info_clear(&ti
);
921 theora_comment_clear(&tc
);
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
);
929 /* tear down the partial vorbis setup */
930 vorbis_info_clear(&vi
);
931 vorbis_comment_clear(&vc
);
934 if(vorbis_p
)open_audio();
936 if(theora_p
)open_video();
939 while(!playbackdone
){
941 /* break out on SDL quit event */
942 if ( SDL_PollEvent ( &event
) )
944 if ( event
.type
== SDL_QUIT
) break ;
947 /* get some audio data */
948 while(vorbis_p
&& !audiobuf_ready
){
954 /* is there pending audio? does it fit our circular buffer without blocking? */
955 ret
=vorbis_synthesis_pcmout(&vd
,&pcm
);
956 maxBytesToWrite
= GetAudioStreamWriteable(aOutStream
);
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. */
963 /* if there's pending, decoded audio, grab it */
964 if((ret
>0)&&(maxBytesToWrite
>0)){
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;
974 if(WriteAudioStream( aOutStream
, samples
, i
)) {
975 if(count
==maxBytesToWrite
){
979 vorbis_synthesis_read(&vd
,i
);
982 audiobuf_granulepos
=vd
.granulepos
-ret
+i
;
984 audiobuf_granulepos
+=i
;
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 */
995 } /* end audio cycle */
997 while(theora_p
&& !videobuf_ready
){
998 /* get one video packet... */
999 if(ogg_stream_packetout(&to
,&op
)>0){
1001 theora_decode_packetin(&td
,&op
);
1003 videobuf_granulepos
=td
.granulepos
;
1004 videobuf_time
=theora_granule_time(&td
,videobuf_granulepos
);
1005 /* update the frame counter */
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 */
1013 delay
=videobuf_time
-now
;
1015 /* got a good frame, not late, ready to break out */
1017 }else if(now
-last_frame_time
>=1.0){
1018 /* display at least one frame per second, regardless */
1021 fprintf(stderr
, "dropping frame %d (%.3fs behind)\n",
1025 /* need more data */
1030 if(!hasdatatobuffer
&& !videobuf_ready
&& !audiobuf_ready
){
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());
1044 /* time to write our cached frame */
1047 last_frame_time
=get_time();
1049 /* if audio has not started (first frame) then start it */
1050 if ((!isPlaying
)&&(vorbis_p
)){
1056 /* HACK: always look for more audio data */
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");
1067 if (ogg_sync_pageout(&oy
,&og
)>0){
1071 } /* playbackdone */
1073 /* show number of video frames decoded */
1075 printf( "Frames decoded: %d", frameNum
);
1077 printf( " (only %d shown)", frameNum
-skipNum
);
1080 /* tear it all down */
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
);
1093 ogg_stream_clear(&to
);
1095 theora_comment_clear(&tc
);
1096 theora_info_clear(&ti
);
1098 ogg_sync_clear(&oy
);