Add Russian translation provided by Валерий Крувялис <valkru@mail.ru>
[xiph-mirror.git] / theora-old / examples / splayer.c
blob5fe4513d47ae7a03157758e318acc5da1d235749
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-2003 *
9 * by the Xiph.Org Foundation http://www.xiph.org/ *
10 * *
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 ********************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
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"
41 #ifdef WIN32
42 #include <windows.h>
43 #include <io.h>
44 #endif
46 #include <portaudio.h>
47 #include <SDL.h>
49 /* for portaudio */
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.
82 typedef struct
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;
92 RingBuffer;
94 typedef struct
96 RingBuffer outFIFO;
97 PortAudioStream *stream;
98 int bytesPerFrame;
99 int samplesPerFrame;
101 PASTREAMIO_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 /***************************************************************************
127 * Initialize FIFO.
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;
138 return 0;
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 )
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 )
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;
178 else
180 *dataPtr1 = &rbuf->buffer[index];
181 *sizePtr1 = numBytes;
182 *dataPtr2 = NULL;
183 *sizePtr2 = 0;
185 return 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 )
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 )
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;
220 else
222 *dataPtr1 = &rbuf->buffer[index];
223 *sizePtr1 = numBytes;
224 *dataPtr2 = NULL;
225 *sizePtr2 = 0;
227 return 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;
241 void *data1, *data2;
242 numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
243 if( size2 > 0 )
246 memcpy( data1, data, size1 );
247 data = ((char *)data) + size1;
248 memcpy( data2, data, size2 );
250 else
252 memcpy( data1, data, size1 );
254 RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
255 return numWritten;
258 /***************************************************************************
259 ** Return bytes read. */
260 long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
262 long size1, size2, numRead;
263 void *data1, *data2;
264 numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
265 if( size2 > 0 )
267 memcpy( data, data1, size1 );
268 data = ((char *)data) + size1;
269 memcpy( data, data2, size2 );
271 else
273 memcpy( data, data1, size1 );
275 RingBuffer_AdvanceReadIndex( rbuf, numRead );
276 return 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;
293 (void) outTime;
294 (void) inputBuffer;
296 if( outputBuffer != NULL )
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++ )
303 ((char *)outputBuffer)[i] = 0;
307 return 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 );
320 /* Free buffer. */
321 static PaError PASTREAMIO_TermFIFO( RingBuffer *rbuf )
323 if( rbuf->buffer ) free( rbuf->buffer );
324 rbuf->buffer = NULL;
325 return paNoError;
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 )
334 long bytesWritten;
335 char *p = (char *) data;
336 long numBytes = aStream->bytesPerFrame * numFrames;
337 while( numBytes > 0)
339 bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );
340 numBytes -= bytesWritten;
341 p += bytesWritten;
342 if( numBytes > 0) Pa_Sleep(10);
344 return numFrames;
348 /************************************************************
349 * Return the number of frames that could be written to the stream without
350 * having to wait.
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 )
363 long numBits = 0;
364 if( ((n-1) & n) == 0) return n; /* Already Power of two. */
365 while( n > 0 )
367 n= n>>1;
368 numBits++;
370 return (1<<numBits);
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:
381 * PABLIO_WRITE,
382 * and either PABLIO_MONO or PABLIO_STEREO
384 PaError OpenAudioStream( PASTREAMIO_Stream **rwblPtr, double sampleRate,
385 PaSampleFormat format, long flags )
387 long bytesPerSample;
388 long doWrite = 0;
389 PaError err;
390 PASTREAMIO_Stream *aStream;
391 long minNumBuffers;
392 long numFrames;
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;
404 goto error;
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);
423 if(doWrite)
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
433 * audio drivers. */
434 err = Pa_OpenStream(
435 &aStream->stream,
436 paNoDevice,
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;
452 *rwblPtr = aStream;
453 return paNoError;
455 error:
456 CloseAudioStream( aStream );
457 *rwblPtr = NULL;
458 return err;
461 PaError StartAudioStream( PASTREAMIO_Stream *aStream)
463 PaError err;
464 err = Pa_StartStream( aStream->stream );
465 if( err != paNoError ) goto error;
467 return paNoError;
468 error:
469 CloseAudioStream( aStream );
470 return err;
474 /************************************************************/
475 PaError CloseAudioStream( PASTREAMIO_Stream *aStream )
477 PaError err;
478 int bytesEmpty;
479 int byteSize = aStream->outFIFO.bufferSize;
481 /* If we are writing data, make sure we play everything written. */
482 if( byteSize > 0 )
484 bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
485 while( bytesEmpty < byteSize )
487 Pa_Sleep( 10 );
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;
496 Pa_Terminate();
498 error:
499 PASTREAMIO_TermFIFO( &aStream->outFIFO );
500 free( aStream );
501 return err;
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 */
519 int isPlaying = 0;
520 PaError err;
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;
535 int theora_p=0;
536 int vorbis_p=0;
537 int stateflag=0;
539 FILE * infile = NULL;
541 /* SDL Video playback structures */
542 SDL_Surface *screen;
543 SDL_Overlay *yuv_overlay;
544 SDL_Rect rect;
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 */
559 int numSamples;
560 int numBytes;
562 int minNumBuffers;
563 int numFrames;
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;
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;
591 static int start_audio(){
592 err = StartAudioStream(aOutStream);
593 if( err != paNoError ) goto error;
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;
604 static int audio_close(void){
605 err = CloseAudioStream( aOutStream );
606 if( err != paNoError ) goto error;
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;
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);
634 return curtime;
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());
643 exit(1);
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);
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);
661 rect.x = 0;
662 rect.y = 0;
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 */
671 int i;
672 yuv_buffer yuv;
673 int crop_offset;
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,
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);
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"
713 #ifdef WIN32
714 "\n"
715 "or drag and drop an ogg file over the .exe\n\n"
716 #endif
720 /* dump the theora (or vorbis) comment header */
721 static int dump_comments(theora_comment *tc){
722 int i, len;
723 char *value;
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);
739 return(0);
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 */
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;;
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);
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;
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;
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;
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){
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);
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;
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);
832 if(theora_decode_header(&ti,&tc,&op)){
833 printf("Error parsing Theora stream headers; corrupt stream?\n");
834 exit(1);
836 theora_p++;
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))){
842 if(ret<0){
843 printf("Error parsing Vorbis stream headers; corrupt stream?\n");
844 exit(1);
846 if(vorbis_synthesis_headerin(&vi,&vc,&op)){
847 printf("Error parsing Vorbis stream headers; corrupt stream?\n");
848 exit(1);
850 vorbis_p++;
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 */
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);
869 int main( int argc, char* argv[] ){
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;
878 int frameNum=0;
879 int skipNum=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 */
885 if( argc != 2 )
887 usage();
888 exit(0);
891 infile = fopen( argv[1], "rb" );
893 /* start up Ogg stream synchronization layer */
894 ogg_sync_init(&oy);
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);
904 parseHeaders();
906 /* force audio off */
907 /* vorbis_p = 0; */
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);
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);
933 /* open audio */
934 if(vorbis_p)open_audio();
935 /* open video */
936 if(theora_p)open_video();
938 /* our main loop */
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){
949 int ret;
950 float **pcm;
951 int count = 0;
952 int maxBytesToWrite;
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. */
961 break;
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;
971 samples[count]=val;
972 count++;
974 if(WriteAudioStream( aOutStream, samples, i )) {
975 if(count==maxBytesToWrite){
976 audiobuf_ready=1;
979 vorbis_synthesis_read(&vd,i);
981 if(vd.granulepos>=0)
982 audiobuf_granulepos=vd.granulepos-ret+i;
983 else
984 audiobuf_granulepos+=i;
986 }else{
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;
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 */
1006 frameNum++;
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);
1024 }else{
1025 /* need more data */
1026 break;
1030 if(!hasdatatobuffer && !videobuf_ready && !audiobuf_ready){
1031 isPlaying = 0;
1032 playbackdone = 1;
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);
1043 if(videobuf_ready){
1044 /* time to write our cached frame */
1045 video_write();
1046 videobuf_ready=0;
1047 last_frame_time=get_time();
1049 /* if audio has not started (first frame) then start it */
1050 if ((!isPlaying)&&(vorbis_p)){
1051 start_audio();
1052 isPlaying = 1;
1056 /* HACK: always look for more audio data */
1057 audiobuf_ready=0;
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){
1068 queue_page(&og);
1071 } /* playbackdone */
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" );
1080 /* tear it all down */
1081 fclose( infile );
1083 if(vorbis_p){
1084 audio_close();
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);
1092 if(theora_p){
1093 ogg_stream_clear(&to);
1094 theora_clear(&td);
1095 theora_comment_clear(&tc);
1096 theora_info_clear(&ti);
1098 ogg_sync_clear(&oy);
1100 printf("\r "
1101 "\nDone.\n");
1103 SDL_Quit();
1105 return(0);