[xiph-cvs] cvs commit: theora/examples splayer.c

Ralph Giles giles at xiph.org
Sat Mar 13 11:53:38 PST 2004



giles       04/03/13 14:53:38

  Modified:    examples splayer.c
  Log:
  Improve playback rate handling in the splayer example. Instead of playing back at fixed delay per
  loop interation plus decode delay, calculate how far ahead we are and sleep for the duration. We
  now play back at the proper frame rate even when there's no audio, play through to the end of the
  data even after reaching end-of-file, and exit after playback completes. There's still no
  monitoring of the draining of the audio playback buffer which could cause problems on
  badly-interleaved streams.
  
  Also code cleanup, reformatting, and a couple of comment corrections.

Revision  Changes    Path
1.14      +114 -101  theora/examples/splayer.c

Index: splayer.c
===================================================================
RCS file: /usr/local/cvsroot/theora/examples/splayer.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -r1.13 -r1.14
--- a/splayer.c	13 Mar 2004 03:53:38 -0000	1.13
+++ b/splayer.c	13 Mar 2004 19:53:38 -0000	1.14
@@ -500,9 +500,10 @@
     free( aStream );
     return err;
 }
-/*end of portaudio specific routines*/
 
-/*portaudio related global types  */
+/* -- end of portaudio specific routines --*/
+
+/* portaudio related global types  */
 #define PA_SAMPLE_TYPE  paInt16
 typedef short SAMPLE;
 #define SAMPLE_SILENCE  (0)
@@ -511,17 +512,13 @@
 SAMPLE      *samples; /*local buffer for samples*/
 double latency_sec = 0;
 
-/*ticks information to be used if the audio stream is not present*/
+/* ticks information to be used if the audio stream is not present */
 int    currentTicks = -1;
 
-/*initial state of the audio stream*/
+/* initial state of the audio stream */
 int isPlaying = 0;
 PaError err;
 
-/* this should go in a header file */
-int theora_decode_tables(theora_info *c, ogg_packet *op);
-
-
 /* Ogg and codec state for demux/decode */
 ogg_sync_state   oy; 
 ogg_page         og;
@@ -551,34 +548,16 @@
 ogg_int64_t  videobuf_granulepos=-1;
 double       videobuf_time=0;
 
-int          audiobuf_ready = 0;
+int          audiobuf_ready=0;
 ogg_int64_t  audiobuf_granulepos=0; /* time position of last sample */
 
-
-short initialticks, endticks;
-
-/* Helper; just grab some more compressed bitstream and sync it for
-   page extraction */
-int buffer_data(ogg_sync_state *oy){
-  char *buffer=ogg_sync_buffer(oy,4096);
-  int bytes=fread(buffer,1,4096,infile);
-  ogg_sync_wrote(oy,bytes);
-  return(bytes);
-}
-
-static void usage(void){
-  printf("Usage: splayer ogg_file\n\n"
-         "or drag and drop an ogg file over the .exe\n\n");
-  exit(1);
-}
-
 static int open_audio(){
-    /* this will open one circular audio stream*/
-    /*build on top of portaudio routines*/
-    /*implementation on fille pastreamio.c*/
+    /* this will open one circular audio stream */
+    /* build on top of portaudio routines */
+    /* implementation based on file pastreamio.c */
 
-    int        numSamples;
-    int        numBytes;
+    int numSamples;
+    int numBytes;
 
     int minNumBuffers;
     int numFrames;
@@ -592,7 +571,7 @@
 
     samples = (SAMPLE *) malloc( numBytes );
 
-    /*store our latency calculation here*/
+    /* store our latency calculation here */
     latency_sec =  (double) numFrames / vi.rate / vi.channels;
     printf( "Latency: %.04f\n", latency_sec );
 
@@ -628,7 +607,6 @@
 
     free(samples);
     return err;
-
 error:
     Pa_Terminate();
     printf( "An error occured while closing the portaudio stream\n" );
@@ -638,36 +616,36 @@
 }
 
 
-double get_time(){
+double get_time() {
     static Uint32 startticks = 0;
     double curtime;
-    if (vorbis_p){
+    if (vorbis_p) {
       /* not entirely accurate with the WAVE OUT device, but good enough
          at this stage. Needs to be reworked to account for blank audio
          data written to the stream... */
-      curtime = (double) (GetAudioStreamTime( aOutStream ) / vi.rate) -  latency_sec;
-      if (curtime<0) curtime = 0;
+      curtime = (double) (GetAudioStreamTime( aOutStream ) / vi.rate) - latency_sec;
+      if (curtime<0.0) curtime = 0.0;
     } else {
       /* initialize timer variable if not set yet */
       if (startticks==0)
         startticks = SDL_GetTicks();
-        curtime = 1.0e-3 * (double)(SDL_GetTicks() - startticks);
+      curtime = 1.0e-3 * (double)(SDL_GetTicks() - startticks);
     }
     return curtime;
 }
 
 
 static void open_video(void){
-  /*taken from player_sample.c test file for theora alpha*/
+  /* taken from player_sample.c test file for theora alpha */
 
   if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
-    printf("Unable to init SDL: %s\n", SDL_GetError());
+    printf("Unable to initialize SDL: %s\n", SDL_GetError());
     exit(1);
   }
   
   screen = SDL_SetVideoMode(ti.frame_width, ti.frame_height, 0, SDL_SWSURFACE);
   if ( screen == NULL ) {
-    printf("Unable to set %dx%d video: %s\n", 
+    printf("Unable to set %dx%d video mode: %s\n", 
            ti.frame_width,ti.frame_height,SDL_GetError());
     exit(1);
   }
@@ -689,7 +667,7 @@
 }
 
 static void video_write(void){
-  /*taken from player_sample.c test file for theora alpha*/
+  /* taken from player_sample.c test file for theora alpha */
   int i;
   yuv_buffer yuv;
   int crop_offset;
@@ -721,15 +699,22 @@
   }
 
   /* Unlock SDL_yuv_overlay */
+  SDL_UnlockYUVOverlay(yuv_overlay);
   if ( SDL_MUSTLOCK(screen) ) {
     SDL_UnlockSurface(screen);
   }
-  SDL_UnlockYUVOverlay(yuv_overlay);
-
 
   /* Show, baby, show! */
-  SDL_DisplayYUVOverlay(yuv_overlay, &rect);
-  
+  SDL_DisplayYUVOverlay(yuv_overlay, &rect);  
+}
+
+static void usage(void){
+  printf("Usage: splayer <ogg_file>\n"
+#ifdef WIN32  
+    "\n"
+    "or drag and drop an ogg file over the .exe\n\n"
+#endif
+  );
 }
 
 /* dump the theora (or vorbis) comment header */
@@ -777,18 +762,26 @@
     }
 }
 
-/* helper: push a page into the appropriate steam */
+/* Helper; just grab some more compressed bitstream and sync it for
+   page extraction */
+int buffer_data(ogg_sync_state *oy){
+  char *buffer=ogg_sync_buffer(oy,4096);
+  int bytes=fread(buffer,1,4096,infile);
+  ogg_sync_wrote(oy,bytes);
+  return(bytes);
+}
+
+/* helper: push a page into the appropriate stream */
 /* 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,&og);
-  if(vorbis_p)ogg_stream_pagein(&vo,&og);
+  if(theora_p)ogg_stream_pagein(&to,page);
+  if(vorbis_p)ogg_stream_pagein(&vo,page);
   return 0;
 }                                   
 
-
 void parseHeaders(){
-  /*extracted from player_sample.c test file for theora alpha*/
+  /* extracted from player_sample.c test file for theora alpha */
   ogg_packet op;
   /* Parse the headers */
   /* Only interested in Vorbis/Theora streams */
@@ -818,7 +811,6 @@
       }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){
         /* it is vorbis */
         memcpy(&vo,&test,sizeof(test));
-	/* there will be more vorbis headers later... */
         vorbis_p=1;
       }else{
         /* whatever it is, we don't care about it */
@@ -827,7 +819,7 @@
     }
   }
 
-  /* we're expecting more header packets. */
+  /* we've now identified all the bitstreams. parse the secondary header packets. */
   while((theora_p && theora_p<3) || (vorbis_p && vorbis_p<3)){
     int ret;
 
@@ -867,12 +859,11 @@
     }else{
       int ret=buffer_data(&oy);
       if(ret==0){
-	fprintf(stderr,"End of file while searching for Vorbis headers.\n");
+	fprintf(stderr,"End of file while searching for codec headers.\n");
         exit(1);
       }
     }
   }
-
 }
 
 int main( int argc, char* argv[] ){
@@ -882,13 +873,14 @@
   SDL_Event event;
   int hasdatatobuffer = 1;
   int playbackdone = 0;
-  double delay, lastframetime = 0;
+  double delay, last_frame_time = 0;
 
   int frameNum=0;
+  int skipNum=0;
 
-  /*takes first argument as file to play*/
-  /*this works better on Windows and is more convenient
-  for drag and drop ogg files over the .exe*/
+  /* takes first argument as file to play */
+  /* this works better on Windows and is more convenient
+     for drag and drop ogg files over the .exe */
 
   if( argc != 2 )
   {
@@ -911,6 +903,9 @@
 
   parseHeaders();
 
+  /* force audio off */
+  /* vorbis_p = 0; */
+
   /* initialize decoders */
   if(theora_p){
     theora_decode_init(&td,&ti);
@@ -939,38 +934,31 @@
   if(vorbis_p)open_audio();
   /* open video */
   if(theora_p)open_video();
+  
+  /* our main loop */
+  while(!playbackdone){
 
-  /*our main loop*/
-  while(hasdatatobuffer){
-
-    SDL_Delay(5);
-
-    if ( playbackdone == 1 ) break;
-
-    /*break out on SDL quit event*/
+    /* break out on SDL quit event */
     if ( SDL_PollEvent ( &event ) )
     {
       if ( event.type == SDL_QUIT ) break ;
     }
 
-    /*get some audio data*/
+    /* get some audio data */
     while(vorbis_p && !audiobuf_ready){
       int ret;
       float **pcm;
       int count = 0;
       int maxBytesToWrite;
 
-      /* is there pending audio? does it fit 
-         out circular buffer without blocking? */
+      /* is there pending audio? does it fit our circular buffer without blocking? */
       ret=vorbis_synthesis_pcmout(&vd,&pcm);
       maxBytesToWrite = GetAudioStreamWriteable(aOutStream);
 
-      if (maxBytesToWrite<=FRAMES_PER_BUFFER)
-      {
-	/*break out until there is a significant amount of
-	data to avoid a series of small write operations*/
-	audiobuf_ready = 0;
-	break;
+      if (maxBytesToWrite<=FRAMES_PER_BUFFER){
+        /* break out until there is a significant amount of
+           data to avoid a series of small write operations. */
+        break;
       }
       /* if there's pending, decoded audio, grab it */
       if((ret>0)&&(maxBytesToWrite>0)){
@@ -1004,12 +992,13 @@
         }else	/* we need more data; break out to suck in another page */
           break;
       }
-    }/*end audio cycle*/
+    } /* end audio cycle */
 
     while(theora_p && !videobuf_ready){
       /* get one video packet... */
       if(ogg_stream_packetout(&to,&op)>0){
-	  theora_decode_packetin(&td,&op);
+      
+        theora_decode_packetin(&td,&op);
 
           videobuf_granulepos=td.granulepos;
           videobuf_time=theora_granule_time(&td,videobuf_granulepos);
@@ -1024,7 +1013,7 @@
           if(delay>=0.0){
                 /* got a good frame, not late, ready to break out */
                 videobuf_ready=1;
-	  }else if(videobuf_time-lastframetime>=1.0){
+	  }else if(videobuf_time-last_frame_time>=1.0){
                 /* display at least one frame per second, regardless */
                 videobuf_ready=1;
           }else{
@@ -1032,48 +1021,69 @@
                         frameNum, -delay);
            }
       }else{
-	/* already have a good frame in the buffer */
+	/* need more data */
         break;
       }
     }
+
+    if(!hasdatatobuffer && !videobuf_ready && !audiobuf_ready){
+      isPlaying = 0;
+      playbackdone = 1;
+    }
+
+    /* if we're set for the next frame, sleep */
+    if((!theora_p || videobuf_ready) && 
+       (!vorbis_p || audiobuf_ready)){
+	double now = get_time();
+        int ticks = 1.0e3*(videobuf_time-now);
+	fprintf(stderr, "delay ticks %d from %f - %f = %f\n",
+		ticks, videobuf_time, now, videobuf_time-now);
+	if(ticks>0){
+	  fprintf(stderr, "  Calling SDL_Delay(%d)\n", ticks);
+          SDL_Delay(ticks);
+        }
+    }
  
-    if(stateflag && audiobuf_ready && videobuf_ready){
-      /*time to write our cached frame*/
+    if(videobuf_ready){
+      /* time to write our cached frame */
       video_write();
       videobuf_ready=0;
-      lastframetime=get_time();
+      last_frame_time=videobuf_time;
 
-      /*if audio has not started (first frame) then start it*/
+      /* if audio has not started (first frame) then start it */
       if ((!isPlaying)&&(vorbis_p)){
-	start_audio();
-	isPlaying = 1;
+        start_audio();
+        isPlaying = 1;
       }
-    } else if (hasdatatobuffer)
-    {
+    }
+
+    /* HACK: always look for more audio data */
+    audiobuf_ready=0;
+
+    /* buffer compressed data every loop */
+    if(hasdatatobuffer){
       hasdatatobuffer=buffer_data(&oy);
       if(hasdatatobuffer==0){
-	printf("Ogg buffering stopped, end of file reached.\n");
+        printf("Ogg buffering stopped, end of file reached.\n");
       }
     }
     
-    /* HACK: always look for more audio data */
-    audiobuf_ready=0;
-
-    if (ogg_sync_pageout(&oy,&og)>0)
+    if (ogg_sync_pageout(&oy,&og)>0){
       queue_page(&og);
+    }
 
-  }
+  } /* playbackdone */
 
-  /*show number of video frames decoded*/
-  printf( "Frames decoded: %d\n", frameNum );
-  /*printf( "Milliseconds: %d\n", endticks-initialticks);
-  printf("Average fps (%.02f)\n", ((float) frameNum)/((endticks-initialticks)/1000.0f));*/
+  /* show number of video frames decoded */
+  printf( "\n");
+  printf( "Frames decoded: %d", frameNum );
+  if(skipNum)
+    printf( " (only %d shown)", frameNum-skipNum);
+  printf( "\n" );
 
   /* tear it all down */
   fclose( infile );
 
-  SDL_Quit();
-
   if(vorbis_p){
     audio_close();
 
@@ -1093,6 +1103,9 @@
 
   printf("\r                                                              "
          "\nDone.\n");
+	 
+  SDL_Quit();
+
   return(0);
 
 }

<p><p>--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list