[xiph-commits] r6879 - in experimental/derf/theora-exp: examples
j at dactyl.lonelymoon.com
j
Fri Jun 25 19:09:44 PDT 2004
unix
Message-ID: <20040626020944.C6C0D9AAAB at dactyl.lonelymoon.com>
Author: j
Date: Fri Jun 25 19:09:44 2004
New Revision: 6879
Added:
experimental/derf/theora-exp/examples/player_example.c
Modified:
experimental/derf/theora-exp/unix/Makefile
Log:
- add player_example to experimental/derf/theora-exp
- update unix/Makefile to build the player
Added: experimental/derf/theora-exp/examples/player_example.c
===================================================================
--- experimental/derf/theora-exp/examples/player_example.c 2004-06-25 21:21:49 UTC (rev 6878)
+++ experimental/derf/theora-exp/examples/player_example.c 2004-06-26 02:09:42 UTC (rev 6879)
@@ -0,0 +1,754 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2003 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: example SDL player application; plays Ogg Theora files (with
+ optional Vorbis audio second stream)
+ last mod: $Id: player_example.c,v 1.29 2004/03/08 06:44:26 giles Exp $
+
+ ********************************************************************/
+
+/* far more complex than most Ogg 'example' programs. The complexity
+ of maintaining A/V sync is pretty much unavoidable. It's necessary
+ to actually have audio/video playback to make the hard audio clock
+ sync actually work. If there's audio playback, there might as well
+ be simple video playback as well...
+
+ A simple 'demux and write back streams' would have been easier,
+ it's true. */
+
+#define _GNU_SOURCE
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include "theora/theora.h"
+#include "vorbis/codec.h"
+#include <SDL.h>
+
+/* yes, this makes us OSS-specific for now. None of SDL, libao, libao2
+ give us any way to determine hardware timing, and since the
+ hard/kernel buffer is going to be most of or > a second, that's
+ just a little bit important */
+#if defined(__FreeBSD__)
+#include <machine/soundcard.h>
+#define AUDIO_DEVICE "/dev/audio"
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#include <soundcard.h>
+#define AUDIO_DEVICE "/dev/audio"
+#else
+#include <sys/soundcard.h>
+#define AUDIO_DEVICE "/dev/dsp"
+#endif
+#include <sys/ioctl.h>
+
+/* Helper; just grab some more compressed bitstream and sync it for
+ page extraction */
+int buffer_data(FILE *in,ogg_sync_state *oy){
+ char *buffer=ogg_sync_buffer(oy,4096);
+ int bytes=fread(buffer,1,4096,in);
+ ogg_sync_wrote(oy,bytes);
+ return(bytes);
+}
+
+/* never forget that globals are a one-way ticket to Hell */
+/* Ogg and codec state for demux/decode */
+ogg_sync_state oy;
+ogg_page og;
+ogg_stream_state vo;
+ogg_stream_state to;
+theora_info ti;
+theora_comment tc;
+theora_setup_info *ts;
+theora_dec_ctx *td;
+vorbis_info vi;
+vorbis_dsp_state vd;
+vorbis_block vb;
+vorbis_comment vc;
+
+int theora_p=0;
+int theora_needs_more_headers;
+int vorbis_p=0;
+int stateflag=0;
+
+/* SDL Video playback structures */
+SDL_Surface *screen;
+SDL_Overlay *yuv_overlay;
+SDL_Rect rect;
+
+/* single frame video buffering */
+int videobuf_ready=0;
+ogg_int64_t videobuf_granulepos=-1;
+double videobuf_time=0;
+
+/* single audio fragment audio buffering */
+int audiobuf_fill=0;
+int audiobuf_ready=0;
+ogg_int16_t *audiobuf;
+ogg_int64_t audiobuf_granulepos=0; /* time position of last sample */
+
+/* audio / video synchronization tracking:
+
+Since this will make it to Google at some point and lots of people
+search for how to do this, a quick rundown of a practical A/V sync
+strategy under Linux [the UNIX where Everything Is Hard]. Naturally,
+this works on other platforms using OSS for sound as well.
+
+In OSS, we don't have reliable access to any precise information on
+the exact current playback position (that, of course would have been
+too easy; the kernel folks like to keep us app people working hard
+doing simple things that should have been solved once and abstracted
+long ago). Hopefully ALSA solves this a little better; we'll probably
+use that once ALSA is the standard in the stable kernel.
+
+We can't use the system clock for a/v sync because audio is hard
+synced to its own clock, and both the system and audio clocks suffer
+from wobble, drift, and a lack of accuracy that can be guaranteed to
+add a reliable percent or so of error. After ten seconds, that's
+100ms. We can't drift by half a second every minute.
+
+Although OSS can't generally tell us where the audio playback pointer
+is, we do know that if we work in complete audio fragments and keep
+the kernel buffer full, a blocking select on the audio buffer will
+give us a writable fragment immediately after playback finishes with
+it. We assume at that point that we know the exact number of bytes in
+the kernel buffer that have not been played (total fragments minus
+one) and calculate clock drift between audio and system then (and only
+then). Damp the sync correction fraction, apply, and walla: A
+reliable A/V clock that even works if it's interrupted. */
+
+long audiofd_totalsize=-1;
+int audiofd_fragsize; /* read and write only complete fragments
+ so that SNDCTL_DSP_GETOSPACE is
+ accurate immediately after a bank
+ switch */
+int audiofd=-1;
+ogg_int64_t audiofd_timer_calibrate=-1;
+
+static void open_audio(){
+ audio_buf_info info;
+ int format=AFMT_S16_NE; /* host endian */
+ int channels=vi.channels;
+ int rate=vi.rate;
+ int ret;
+
+ audiofd=open(AUDIO_DEVICE,O_RDWR);
+ if(audiofd<0){
+ fprintf(stderr,"Could not open audio device " AUDIO_DEVICE ".\n");
+ exit(1);
+ }
+
+ ret=ioctl(audiofd,SNDCTL_DSP_SETFMT,&format);
+ if(ret){
+ fprintf(stderr,"Could not set 16 bit host-endian playback\n");
+ exit(1);
+ }
+
+ ret=ioctl(audiofd,SNDCTL_DSP_CHANNELS,&channels);
+ if(ret){
+ fprintf(stderr,"Could not set %d channel playback\n",channels);
+ exit(1);
+ }
+
+ ret=ioctl(audiofd,SNDCTL_DSP_SPEED,&rate);
+ if(ret){
+ fprintf(stderr,"Could not set %d Hz playback\n",rate);
+ exit(1);
+ }
+
+ ioctl(audiofd,SNDCTL_DSP_GETOSPACE,&info);
+ audiofd_fragsize=info.fragsize;
+ audiofd_totalsize=info.fragstotal*info.fragsize;
+
+ audiobuf=malloc(audiofd_fragsize);
+}
+
+static void audio_close(void){
+ if(audiofd>-1){
+ ioctl(audiofd,SNDCTL_DSP_RESET,NULL);
+ close(audiofd);
+ free(audiobuf);
+ }
+}
+
+/* call this only immediately after unblocking from a full kernel
+ having a newly empty fragment or at the point of DMA restart */
+void audio_calibrate_timer(int restart){
+ struct timeval tv;
+ ogg_int64_t current_sample;
+ ogg_int64_t new_time;
+
+ gettimeofday(&tv,0);
+ new_time=tv.tv_sec*1000+tv.tv_usec/1000;
+
+ if(restart){
+ current_sample=audiobuf_granulepos-audiobuf_fill/2/vi.channels;
+ }else
+ current_sample=audiobuf_granulepos-
+ (audiobuf_fill+audiofd_totalsize-audiofd_fragsize)/2/vi.channels;
+
+ new_time-=1000*current_sample/vi.rate;
+
+ audiofd_timer_calibrate=new_time;
+}
+
+/* get relative time since beginning playback, compensating for A/V
+ drift */
+double get_time(){
+ static ogg_int64_t last=0;
+ static ogg_int64_t up=0;
+ ogg_int64_t now;
+ struct timeval tv;
+
+ gettimeofday(&tv,0);
+ now=tv.tv_sec*1000+tv.tv_usec/1000;
+
+ if(audiofd_timer_calibrate==-1)audiofd_timer_calibrate=last=now;
+
+ if(audiofd<0){
+ /* no audio timer to worry about, we can just use the system clock */
+ /* only one complication: If the process is suspended, we should
+ reset timing to account for the gap in play time. Do it the
+ easy/hack way */
+ if(now-last>1000)audiofd_timer_calibrate+=(now-last);
+ last=now;
+ }
+
+ if(now-up>200){
+ double timebase=(now-audiofd_timer_calibrate)*.001;
+ int hundredths=timebase*100-(long)timebase*100;
+ int seconds=(long)timebase%60;
+ int minutes=((long)timebase/60)%60;
+ int hours=(long)timebase/3600;
+
+ fprintf(stderr," Playing: %d:%02d:%02d.%02d \r",
+ hours,minutes,seconds,hundredths);
+ up=now;
+ }
+
+ return (now-audiofd_timer_calibrate)*.001;
+
+}
+
+/* write a fragment to the OSS kernel audio API, but only if we can
+ stuff in a whole fragment without blocking */
+void audio_write_nonblocking(void){
+
+ if(audiobuf_ready){
+ audio_buf_info info;
+ long bytes;
+
+ ioctl(audiofd,SNDCTL_DSP_GETOSPACE,&info);
+ bytes=info.bytes;
+ if(bytes>=audiofd_fragsize){
+ if(bytes==audiofd_totalsize)audio_calibrate_timer(1);
+
+ while(1){
+ bytes=write(audiofd,audiobuf+(audiofd_fragsize-audiobuf_fill),
+ audiofd_fragsize);
+
+ if(bytes>0){
+
+ if(bytes!=audiobuf_fill){
+ /* shouldn't actually be possible... but eh */
+ audiobuf_fill-=bytes;
+ }else
+ break;
+ }
+ }
+
+ audiobuf_fill=0;
+ audiobuf_ready=0;
+
+ }
+ }
+}
+
+/* clean quit on Ctrl-C for SDL and thread shutdown as per SDL example
+ (we don't use any threads, but libSDL does) */
+int got_sigint=0;
+static void sigint_handler (int signal) {
+ got_sigint = 1;
+}
+
+static void open_video(void){
+ if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
+ fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+ screen = SDL_SetVideoMode(ti.frame_width, ti.frame_height, 0, SDL_SWSURFACE);
+ if ( screen == NULL ) {
+ fprintf(stderr, "Unable to set %dx%d video: %s\n",
+ ti.frame_width,ti.frame_height,SDL_GetError());
+ exit(1);
+ }
+
+ yuv_overlay = SDL_CreateYUVOverlay(ti.frame_width, ti.frame_height,
+ SDL_YV12_OVERLAY,
+ screen);
+ if ( yuv_overlay == NULL ) {
+ fprintf(stderr, "SDL: Couldn't create SDL_yuv_overlay: %s\n",
+ SDL_GetError());
+ exit(1);
+ }
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = ti.frame_width;
+ rect.h = ti.frame_height;
+
+ SDL_DisplayYUVOverlay(yuv_overlay, &rect);
+}
+
+static void video_write(void){
+ int pli;
+ int wscale, hscale;
+ int i;
+ theora_ycbcr_buffer ycbcr;
+ int crop_offset;
+ theora_decode_ycbcr_out(td,ycbcr);
+ int planemap[] = {0,2,1};
+
+ /* Lock SDL_yuv_overlay */
+ if ( SDL_MUSTLOCK(screen) ) {
+ if ( SDL_LockSurface(screen) < 0 ) return;
+ }
+ if (SDL_LockYUVOverlay(yuv_overlay) < 0) return;
+
+ /* let's draw the data on a SDL screen (*screen) */
+ /* deal with border stride */
+ /* reverse u and v for SDL */
+ /* and crop input properly, respecting the encoded frame rect */
+ /* problems may exist for odd frame rect for some encodings */
+ for (pli = 0; pli < 3; pli++) {
+ wscale = ycbcr[0].width / ycbcr[pli].width;
+ hscale = ycbcr[0].height / ycbcr[pli].height;
+ crop_offset = (ti.offset_x / wscale)
+ + (ycbcr[pli].ystride)
+ * (ti.offset_y / hscale);
+ for(i=0;i<yuv_overlay->h / hscale;i++)
+ memcpy(yuv_overlay->pixels[planemap[pli]]
+ + yuv_overlay->pitches[planemap[pli]]*i,
+ ycbcr[pli].data+crop_offset+ycbcr[pli].ystride*i,
+ yuv_overlay->w / wscale);
+ }
+
+ /* Unlock SDL_yuv_overlay */
+ if ( SDL_MUSTLOCK(screen) ) {
+ SDL_UnlockSurface(screen);
+ }
+ SDL_UnlockYUVOverlay(yuv_overlay);
+
+
+ /* Show, baby, show! */
+ SDL_DisplayYUVOverlay(yuv_overlay, &rect);
+
+}
+/* dump the theora (or vorbis) comment header */
+static int dump_comments(theora_comment *tc){
+ int i, len;
+ char *value;
+ FILE *out=stdout;
+
+ fprintf(out,"Encoded by %s\n",tc->vendor);
+ if(tc->comments){
+ fprintf(out, "theora comment header:\n");
+ for(i=0;i<tc->comments;i++){
+ if(tc->user_comments[i]){
+ len=tc->comment_lengths[i];
+ value=malloc(len+1);
+ memcpy(value,tc->user_comments[i],len);
+ value[len]='\0';
+ fprintf(out, "\t%s\n", value);
+ free(value);
+ }
+ }
+ }
+ return(0);
+}
+
+/* Report the encoder-specified colorspace for the video, if any.
+ We don't actually make use of the information in this example;
+ a real player should attempt to perform color correction for
+ whatever display device it supports. */
+static void report_colorspace(theora_info *ti)
+{
+ switch(ti->colorspace){
+ case OC_CS_UNSPECIFIED:
+ /* nothing to report */
+ break;;
+ case OC_CS_ITU_REC_470M:
+ fprintf(stderr," encoder specified ITU Rec 470M (NTSC) color.\n");
+ break;;
+ case OC_CS_ITU_REC_470BG:
+ fprintf(stderr," encoder specified ITU Rec 470BG (PAL) color.\n");
+ break;;
+ default:
+ fprintf(stderr,"warning: encoder specified unknown colorspace (%d).\n",
+ ti->colorspace);
+ break;;
+ }
+}
+
+/* helper: push a page into the appropriate steam */
+/* this can be done blindly; a stream won't accept a page
+ that doesn't belong to it */
+static int queue_page(ogg_page *page){
+ if(theora_p)ogg_stream_pagein(&to,page);
+ if(vorbis_p)ogg_stream_pagein(&vo,&og);
+ return 0;
+}
+
+static void usage(void){
+ fprintf(stderr,
+ "Usage: player_example <file.ogg>\n"
+ "input is read from stdin if no file is passed on the command line\n"
+ "\n"
+ );
+}
+
+int main(int argc,char *argv[]){
+
+ int i,j;
+ ogg_packet op;
+
+ FILE *infile = stdin;
+
+#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */
+ /* Beware the evil ifdef. We avoid these where we can, but this one we
+ cannot. Don't add any more, you'll probably go to hell if you do. */
+ _setmode( _fileno( stdin ), _O_BINARY );
+#endif
+
+ /* open the input file if any */
+ if(argc==2){
+ infile=fopen(argv[1],"rb");
+ if(infile==NULL){
+ fprintf(stderr,"Unable to open '%s' for playback.\n", argv[1]);
+ exit(1);
+ }
+ }
+ if(argc>2){
+ usage();
+ exit(1);
+ }
+
+ /* start up Ogg stream synchronization layer */
+ ogg_sync_init(&oy);
+
+ /* init supporting Vorbis structures needed in header parsing */
+ vorbis_info_init(&vi);
+ vorbis_comment_init(&vc);
+
+ /* init supporting Theora structures needed in header parsing */
+ theora_comment_init(&tc);
+ theora_info_init(&ti);
+
+ /* Ogg file open; parse the headers */
+ /* Only interested in Vorbis/Theora streams */
+ while(!stateflag){
+ int ret=buffer_data(infile,&oy);
+ if(ret==0)break;
+ while(ogg_sync_pageout(&oy,&og)>0){
+ ogg_stream_state test;
+
+ /* is this a mandated initial header? If not, stop parsing */
+ if(!ogg_page_bos(&og)){
+ /* don't leak the page; get it into the appropriate stream */
+ queue_page(&og);
+ stateflag=1;
+ break;
+ }
+
+ ogg_stream_init(&test,ogg_page_serialno(&og));
+ ogg_stream_pagein(&test,&og);
+ ogg_stream_packetout(&test,&op);
+
+ /* identify the codec: try theora */
+ if(!theora_p && (theora_needs_more_headers=
+ theora_decode_headerin(&ti,&tc,&ts,&op))>=0){
+ /* it is theora */
+ memcpy(&to,&test,sizeof(test));
+ theora_p=1;
+ }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){
+ /* it is vorbis */
+ memcpy(&vo,&test,sizeof(test));
+ vorbis_p=1;
+ }else{
+ /* whatever it is, we don't care about it */
+ ogg_stream_clear(&test);
+ }
+ }
+ /* fall through to non-bos page parsing */
+ }
+
+ /* we're expecting more header packets. */
+ while((theora_p && theora_needs_more_headers) || (vorbis_p && vorbis_p<3)){
+ int ret;
+
+ /* look for further theora headers */
+ while(theora_needs_more_headers&&(ret=ogg_stream_packetout(&to,&op))){
+ if(ret<0){
+ fprintf(stderr,
+ "Error parsing Theora stream headers; corrupt stream?\n");
+ exit(1);
+ }
+ theora_needs_more_headers=theora_decode_headerin(&ti,&tc,&ts,&op);
+ if(theora_needs_more_headers<0){
+ printf("Error parsing Theora stream headers; corrupt stream?\n");
+ exit(1);
+ }
+ theora_p++;
+ }
+
+ /* look for more vorbis header packets */
+ while(vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&vo,&op))){
+ if(ret<0){
+ fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n");
+ exit(1);
+ }
+ if(vorbis_synthesis_headerin(&vi,&vc,&op)){
+ fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n");
+ exit(1);
+ }
+ vorbis_p++;
+ if(vorbis_p==3)break;
+ }
+
+ /* The header pages/packets will arrive before anything else we
+ care about, or the stream is not obeying spec */
+
+ if(ogg_sync_pageout(&oy,&og)>0){
+ queue_page(&og); /* demux into the appropriate stream */
+ }else{
+ int ret=buffer_data(infile,&oy); /* someone needs more data */
+ if(ret==0){
+ fprintf(stderr,"End of file while searching for codec headers.\n");
+ exit(1);
+ }
+ }
+ }
+
+ /* and now we have it all. initialize decoders */
+ if(theora_p){
+ td=theora_decode_alloc(&ti,ts);
+ printf("Ogg logical stream %x is Theora %dx%d %.02f fps video\n",
+ to.serialno,ti.width,ti.height,
+ (double)ti.fps_numerator/ti.fps_denominator);
+ if(ti.width!=ti.frame_width || ti.height!=ti.frame_height)
+ printf(" Frame content is %dx%d with offset (%d,%d).\n",
+ ti.frame_width, ti.frame_height, ti.offset_x, ti.offset_y);
+ report_colorspace(&ti);
+ dump_comments(&tc);
+ }else{
+ /* tear down the partial theora setup */
+ theora_info_clear(&ti);
+ theora_comment_clear(&tc);
+ theora_setup_free(ts);
+ }
+ if(vorbis_p){
+ vorbis_synthesis_init(&vd,&vi);
+ vorbis_block_init(&vd,&vb);
+ fprintf(stderr,"Ogg logical stream %x is Vorbis %d channel %d Hz audio.\n",
+ vo.serialno,vi.channels,vi.rate);
+ }else{
+ /* tear down the partial vorbis setup */
+ vorbis_info_clear(&vi);
+ vorbis_comment_clear(&vc);
+ }
+
+ /* open audio */
+ if(vorbis_p)open_audio();
+
+ /* open video */
+ if(theora_p)open_video();
+
+ /* install signal handler as SDL clobbered the default */
+ signal (SIGINT, sigint_handler);
+
+ /* on to the main decode loop. We assume in this example that audio
+ and video start roughly together, and don't begin playback until
+ we have a start frame for both. This is not necessarily a valid
+ assumption in Ogg A/V streams! It will always be true of the
+ example_encoder (and most streams) though. */
+
+ stateflag=0; /* playback has not begun */
+ while(!got_sigint){
+
+ /* we want a video and audio frame ready to go at all times. If
+ we have to buffer incoming, buffer the compressed data (ie, let
+ ogg do the buffering) */
+ while(vorbis_p && !audiobuf_ready){
+ int ret;
+ float **pcm;
+
+ /* if there's pending, decoded audio, grab it */
+ if((ret=vorbis_synthesis_pcmout(&vd,&pcm))>0){
+ int count=audiobuf_fill/2;
+ int maxsamples=(audiofd_fragsize-audiobuf_fill)/2/vi.channels;
+ for(i=0;i<ret && i<maxsamples;i++)
+ for(j=0;j<vi.channels;j++){
+ int val=rint(pcm[j][i]*32767.f);
+ if(val>32767)val=32767;
+ if(val<-32768)val=-32768;
+ audiobuf[count++]=val;
+ }
+ vorbis_synthesis_read(&vd,i);
+ audiobuf_fill+=i*vi.channels*2;
+ if(audiobuf_fill==audiofd_fragsize)audiobuf_ready=1;
+ if(vd.granulepos>=0)
+ audiobuf_granulepos=vd.granulepos-ret+i;
+ else
+ audiobuf_granulepos+=i;
+
+ }else{
+
+ /* no pending audio; is there a pending packet to decode? */
+ if(ogg_stream_packetout(&vo,&op)>0){
+ if(vorbis_synthesis(&vb,&op)==0) /* test for success! */
+ vorbis_synthesis_blockin(&vd,&vb);
+ }else /* we need more data; break out to suck in another page */
+ break;
+ }
+ }
+
+ while(theora_p && !videobuf_ready){
+ /* theora is one in, one out... */
+ if(ogg_stream_packetout(&to,&op)>0){
+
+ if(theora_decode_packetin(td,&op,&videobuf_granulepos)>=0){
+ videobuf_time=theora_granule_time(td,videobuf_granulepos);
+
+ /* is it already too old to be useful? This is only actually
+ useful cosmetically after a SIGSTOP. Note that we have to
+ decode the frame even if we don't show it (for now) due to
+ keyframing. Soon enough libtheora will be able to deal
+ with non-keyframe seeks. */
+
+ if(videobuf_time>=get_time())
+ videobuf_ready=1;
+ }
+
+ }else
+ break;
+ }
+
+ if(!videobuf_ready && !audiobuf_ready && feof(infile))break;
+
+ if(!videobuf_ready || !audiobuf_ready){
+ /* no data yet for somebody. Grab another page */
+ int ret=buffer_data(infile,&oy);
+ while(ogg_sync_pageout(&oy,&og)>0){
+ queue_page(&og);
+ }
+ }
+
+ /* If playback has begun, top audio buffer off immediately. */
+ if(stateflag) audio_write_nonblocking();
+
+ /* are we at or past time for this video frame? */
+ if(stateflag && videobuf_ready && videobuf_time<=get_time()){
+ video_write();
+ videobuf_ready=0;
+ }
+
+ if(stateflag &&
+ (audiobuf_ready || !vorbis_p) &&
+ (videobuf_ready || !theora_p) &&
+ !got_sigint){
+ /* we have an audio frame ready (which means the audio buffer is
+ full), it's not time to play video, so wait until one of the
+ audio buffer is ready or it's near time to play video */
+
+ /* set up select wait on the audiobuffer and a timeout for video */
+ struct timeval timeout;
+ fd_set writefs;
+ fd_set empty;
+ int n=0;
+
+ FD_ZERO(&writefs);
+ FD_ZERO(&empty);
+ if(audiofd>=0){
+ FD_SET(audiofd,&writefs);
+ n=audiofd+1;
+ }
+
+ if(theora_p){
+ long milliseconds=(videobuf_time-get_time())*1000-5;
+ if(milliseconds>500)milliseconds=500;
+ if(milliseconds>0){
+ timeout.tv_sec=milliseconds/1000;
+ timeout.tv_usec=(milliseconds%1000)*1000;
+
+ n=select(n,&empty,&writefs,&empty,&timeout);
+ if(n)audio_calibrate_timer(0);
+ }
+ }else{
+ select(n,&empty,&writefs,&empty,NULL);
+ }
+ }
+
+ /* if our buffers either don't exist or are ready to go,
+ we can begin playback */
+ if((!theora_p || videobuf_ready) &&
+ (!vorbis_p || audiobuf_ready))stateflag=1;
+ /* same if we've run out of input */
+ if(feof(infile))stateflag=1;
+
+ }
+
+ /* tear it all down */
+
+ audio_close();
+ SDL_Quit();
+
+ if(vorbis_p){
+ ogg_stream_clear(&vo);
+ vorbis_block_clear(&vb);
+ vorbis_dsp_clear(&vd);
+ vorbis_comment_clear(&vc);
+ vorbis_info_clear(&vi);
+ }
+ if(theora_p){
+ ogg_stream_clear(&to);
+ theora_decode_free(td);
+ theora_setup_free(ts);
+ theora_comment_clear(&tc);
+ theora_info_clear(&ti);
+ }
+ ogg_sync_clear(&oy);
+
+ if(infile && infile!=stdin)fclose(infile);
+
+ fprintf(stderr,
+ "\r "
+ "\nDone.\n");
+ return(0);
+
+}
Modified: experimental/derf/theora-exp/unix/Makefile
===================================================================
--- experimental/derf/theora-exp/unix/Makefile 2004-06-25 21:21:49 UTC (rev 6878)
+++ experimental/derf/theora-exp/unix/Makefile 2004-06-26 02:09:42 UTC (rev 6879)
@@ -5,6 +5,7 @@
# Name of the targets
LIBTHEORA_TARGET = libtheora.a
DUMP_VIDEO_TARGET = dump_video
+PLAYER_EXAMPLE_TARGET = player_example
ENCODER_EXAMPLE_TARGET = encoder_example
# The compiler to use
CC = gcc
@@ -14,17 +15,17 @@
# The location of include files.
# Modify these to point to your Ogg and Vorbis include directories if they are
# not installed in a standard location.
-CINCLUDE =
+CINCLUDE = -I../include -I/usr/include/SDL -D_REENTRANT
# Extra compilation flags.
# You may get speed increases by including flags such as -O2 or -O3 or
# -ffast-math, or additional flags, depending on your system and compiler.
# The -g flag will generally include debugging information.
-CFLAGS =
+CFLAGS = -funroll-loops -pg -fno-inline-functions
# Libraries to link with, and the location of library files.
LIBS = -logg -lvorbis -lvorbisenc
# ANYTHING BELOW THIS LINE PROBABLY DOES NOT NEED EDITING
-CINCLUDE+= -I../include
+CINCLUDE+=
LIBSRCDIR = ../lib
BINSRCDIR = ../examples
WORKDIR = objs
@@ -70,24 +71,28 @@
DUMP_VIDEO_CSOURCES = dump_video.c
ENCODER_EXAMPLE_CSOURCES = encoder_example.c
+PLAYER_EXAMPLE_CSOURCES = player_example.c
# Create object file list.
LIBTHEORA_OBJS:= ${LIBTHEORA_CSOURCES:%.c=${WORKDIR}/%.o}
DUMP_VIDEO_OBJS:= ${DUMP_VIDEO_CSOURCES:%.c=${WORKDIR}/%.o}
ENCODER_EXAMPLE_OBJS:= ${ENCODER_EXAMPLE_CSOURCES:%.c=${WORKDIR}/%.o}
+PLAYER_EXAMPLE_OBJS:= ${PLAYER_EXAMPLE_CSOURCES:%.c=${WORKDIR}/%.o}
# Prepend source path to file names.
LIBTHEORA_CSOURCES:= ${LIBTHEORA_CSOURCES:%=${LIBSRCDIR}/%}
LIBTHEORA_CHEADERS:= ${LIBTHEORA_CHEADERS:%=${LIBSRCDIR}/%}
DUMP_VIDEO_CSOURCES:= ${DUMP_VIDEO_CSOURCES:%=${BINSRCDIR}/%}
ENCODER_EXAMPLE_CSOURCES:= ${ENCODER_EXAMPLE_CSOURCES:%=${BINSRCDIR}/%}
+PLAYER_EXAMPLE_CSOURCES:= ${PLAYER_EXAMPLE_CSOURCES:%=${BINSRCDIR}/%}
# Prepand target path to file names.
LIBTHEORA_TARGET:= ${TARGETLIBDIR}/${LIBTHEORA_TARGET}
DUMP_VIDEO_TARGET:= ${TARGETBINDIR}/${DUMP_VIDEO_TARGET}
ENCODER_EXAMPLE_TARGET:= ${TARGETBINDIR}/${ENCODER_EXAMPLE_TARGET}
+PLAYER_EXAMPLE_TARGET:= ${TARGETBINDIR}/${PLAYER_EXAMPLE_TARGET}
# Targets:
# Everything (default)
-all: ${LIBTHEORA_TARGET} ${DUMP_VIDEO_TARGET} ${ENCODER_EXAMPLE_TARGET}
+all: ${LIBTHEORA_TARGET} ${DUMP_VIDEO_TARGET} ${ENCODER_EXAMPLE_TARGET} ${PLAYER_EXAMPLE_TARGET}
# libtheora
${LIBTHEORA_TARGET}: ${LIBTHEORA_OBJS}
@@ -105,11 +110,18 @@
${CC} ${CFLAGS} -o $@ ${ENCODER_EXAMPLE_OBJS} ${LIBS} \
${LIBTHEORA_TARGET}
+# player_example
+${PLAYER_EXAMPLE_TARGET}: ${PLAYER_EXAMPLE_OBJS} ${LIBTHEORA_TARGET}
+ mkdir -p ${TARGETBINDIR}
+ ${CC} ${CFLAGS} -o $@ ${PLAYER_EXAMPLE_OBJS} ${LIBS} -lSDL -lpthread \
+ ${LIBTHEORA_TARGET}
+
# Remove all targets.
clean:
-rm ${LIBTHEORA_OBJS} ${LIBTHEORA_TARGET} \
${DUMP_VIDEO_OBJS} ${DUMP_VIDEO_TARGET} \
- ${ENCODER_EXAMPLE_OBJS} ${ENCODER_EXAMPLE_TARGET}
+ ${ENCODER_EXAMPLE_OBJS} ${ENCODER_EXAMPLE_TARGET} \
+ ${PLAYER_EXAMPLE_OBJS} ${PLAYER_EXAMPLE_TARGET}
-rmdir ${WORKDIR}
# Make the dependency list.
@@ -118,7 +130,8 @@
# Only tracks dependencies on headers in this project, not libraries.
depend: ${LIBTHEORA_CSOURCES} ${LIBTHEORA_CHEADERS} \
${DUMP_VIDEO_CSOURCES} \
- ${ENCODER_EXAMPLE_CSOURCES}
+ ${ENCODER_EXAMPLE_CSOURCES} \
+ ${PLAYER_EXAMPLE_CSOURCES}
${MAKEDEPEND} ${CINCLUDE} ${CFLAGS} ${LIBTHEORA_CSOURCES} > $@
# Specify which targets are phony for GNU make
More information about the commits
mailing list