[xiph-cvs] cvs commit: theora/lib toplevel.c

Ralph Giles giles at xiph.org
Sat May 10 15:02:32 PDT 2003



giles       03/05/10 18:02:32

  Modified:    examples encoder_example.c player_example.c
               lib      toplevel.c
  Log:
  Split the codebook portion of the headers into their own packet so we
  don't have a huge indet/info packet that the beginning of the stream.
  
  THIS IS AN INCOMPATIBLE BITSTREAM FORMAT CHANGE.
  
  Also some minor comment cleanup.

Revision  Changes    Path
1.7       +37 -12    theora/examples/encoder_example.c

Index: encoder_example.c
===================================================================
RCS file: /usr/local/cvsroot/theora/examples/encoder_example.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- encoder_example.c	25 Sep 2002 10:01:52 -0000	1.6
+++ encoder_example.c	10 May 2003 22:02:32 -0000	1.7
@@ -12,7 +12,7 @@
 
   function: example encoder application; makes an Ogg Theora/Vorbis 
             file from YUV4MPEG2 and WAV input
-  last mod: $Id: encoder_example.c,v 1.6 2002/09/25 10:01:52 xiphmont Exp $
+  last mod: $Id: encoder_example.c,v 1.7 2003/05/10 22:02:32 giles Exp $
 
  ********************************************************************/
 
@@ -227,7 +227,7 @@
       return;
     }
   }
-  fprintf(stderr,"Input file %s is niether a WAV nor YUV4MPEG2 file.\n",f);
+  fprintf(stderr,"Input file %s is neither a WAV nor YUV4MPEG2 file.\n",f);
   exit(1);
 
  riff_err:
@@ -425,7 +425,7 @@
 
   theora_state     td;
   theora_info      ti;
-  
+
   vorbis_info      vi; /* struct that stores all the static vorbis bitstream
                           settings */
   vorbis_comment   vc; /* struct that stores all the user comments */
@@ -552,18 +552,22 @@
     vorbis_block_init(&vd,&vb);
   }
 
-  /* get the bitstream header pages; one for theora, three for vorbis */
+  /* write the bitstream header packets with proper page interleave */
+
+  /* first packet will get its own page automatically */
   theora_encode_header(&td,&op);
-  ogg_stream_packetin(&to,&op); /* first packet, so it's flushed
-                                   immediately */
+  ogg_stream_packetin(&to,&op);
   if(ogg_stream_pageout(&to,&og)!=1){
-    /*can't get here unless Ogg is borked */
     fprintf(stderr,"Internal Ogg library error.\n");
     exit(1);
-  }
+  }  
   fwrite(og.header,1,og.header_len,stdout);
   fwrite(og.body,1,og.body_len,stdout);
 
+  /* create the remaining theora headers */
+  theora_encode_tables(&td,&op);
+  ogg_stream_packetin(&to,&op); 
+  
   if(audio){
     ogg_packet header;
     ogg_packet header_comm;
@@ -572,12 +576,33 @@
     vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
     ogg_stream_packetin(&vo,&header); /* automatically placed in its own
                                          page */
+    if(ogg_stream_pageout(&vo,&og)!=1){
+      fprintf(stderr,"Internal Ogg library error.\n");
+      exit(1);
+    }
+    fwrite(og.header,1,og.header_len,stdout);
+    fwrite(og.body,1,og.body_len,stdout);
+    
+    /* remaining vorbis header packets */
     ogg_stream_packetin(&vo,&header_comm);
     ogg_stream_packetin(&vo,&header_code);
-    
-    /* This ensures the actual
-     * audio data will start on a new page, as per spec
-     */
+  }
+  
+  /* Flush the rest of our headers. This ensures
+     the actual data in each stream will start 
+     on a new page, as per spec. */
+  while(1){
+    int result = ogg_stream_flush(&to,&og);
+      if(result<0){
+        /* can't get here */
+        fprintf(stderr,"Internal Ogg library error.\n");
+        exit(1);
+      }
+    if(result==0)break;
+    fwrite(og.header,1,og.header_len,stdout);
+    fwrite(og.body,1,og.body_len,stdout);
+  }
+  if(audio){
     while(1){
       int result=ogg_stream_flush(&vo,&og);
       if(result<0){

<p><p>1.10      +46 -30    theora/examples/player_example.c

Index: player_example.c
===================================================================
RCS file: /usr/local/cvsroot/theora/examples/player_example.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- player_example.c	25 Sep 2002 10:01:52 -0000	1.9
+++ player_example.c	10 May 2003 22:02:32 -0000	1.10
@@ -5,14 +5,14 @@
  * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
  * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
  *                                                                  *
- * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002             *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-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.9 2002/09/25 10:01:52 xiphmont Exp $
+  last mod: $Id: player_example.c,v 1.10 2003/05/10 22:02:32 giles Exp $
 
  ********************************************************************/
 
@@ -101,13 +101,12 @@
 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 information that gives us
-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.
+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
@@ -123,7 +122,7 @@
 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 its interrupted. */
+reliable A/V clock that even works if it's interrupted. */
 
 long         audiofd_totalsize=-1;
 int          audiofd_fragsize;      /* read and write only complete fragments
@@ -152,7 +151,7 @@
     exit(1);
   }
   
-  ret=ioctl(audiofd,SNDCTL_DSP_CHANNELS,&vi.channels);
+  ret=ioctl(audiofd,SNDCTL_DSP_CHANNELS,&channels);
   if(ret){
     fprintf(stderr,"Could not set %d channel playback\n",channels);
     exit(1);
@@ -169,7 +168,6 @@
   audiofd_totalsize=info.fragstotal*info.fragsize;
   
   audiobuf=malloc(audiofd_fragsize);
-
 }
 
 static void audio_close(void){
@@ -348,6 +346,14 @@
   
 }
 
+/* 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,&og);
+  if(vorbis_p)ogg_stream_pagein(&vo,&og);
+  return 0;
+}                                   
 
 int main(void){
   
@@ -378,10 +384,7 @@
       /* 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 */
-	/* this can be done blindly; a stream won't accept a page
-             that doesn't bewlong to it */
-	if(theora_p)ogg_stream_pagein(&to,&og);
-	if(vorbis_p)ogg_stream_pagein(&vo,&og);
+	queue_page(&og);
         stateflag=1;
         break;
       }
@@ -398,43 +401,56 @@
       }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 */
         ogg_stream_clear(&test);
       }
     }
+    /* fall through to non-bos page parsing */
   }
   
-  /* we're expecting more vorbis header packets. */
-  while(vorbis_p && vorbis_p<3){
+  /* we're expecting more header packets. */
+  while((theora_p && theora_p<2) || (vorbis_p && vorbis_p<3)){
     int ret;
-    while((ret=ogg_stream_packetout(&vo,&op))){
+    
+    /* look for further theora headers */
+    while(theora_p && (theora_p<2) && (ret=ogg_stream_packetout(&to,&op))){
+      if(ret<0){
+      	fprintf(stderr,"Error parsing Theora stream headers; corrupt stream?\n");
+      	exit(1);
+      }
+      if(theora_decode_tables(&ti,&op)){
+        fprintf(stderr,"Error parsing Theora stream headers; corrupt stream?\n");
+        exit(1);
+      }
+      theora_p++;
+      if(theora_p==2) break;
+    }
+
+    /* 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){
-      ogg_stream_pagein(&vo,&og); /* the vorbis stream will accept
-                                     only its own */
+      queue_page(&og); /* demux into the appropriate stream */
     }else{
-      int ret=buffer_data(&oy);
+      int ret=buffer_data(&oy); /* someone needs more data */
       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);
       }
     }
@@ -541,8 +557,7 @@
       /* no data yet for somebody.  Grab another page */
       int ret=buffer_data(&oy);
       while(ogg_sync_pageout(&oy,&og)>0){
-	if(ogg_stream_pagein(&to,&og))
-	  if(vorbis_p)ogg_stream_pagein(&vo,&og);
+      	queue_page(&og);
       }
     }
 
@@ -591,8 +606,11 @@
       }
     }
 
+    /* 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(stdin))stateflag=1; 
 
   }
@@ -621,5 +639,3 @@
   return(0);
 
 }
-
-

<p><p>1.15      +54 -7     theora/lib/toplevel.c

Index: toplevel.c
===================================================================
RCS file: /usr/local/cvsroot/theora/lib/toplevel.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -r1.14 -r1.15
--- toplevel.c	26 Feb 2003 21:16:58 -0000	1.14
+++ toplevel.c	10 May 2003 22:02:32 -0000	1.15
@@ -11,7 +11,7 @@
  ********************************************************************
 
   function: 
-  last mod: $Id: toplevel.c,v 1.14 2003/02/26 21:16:58 giles Exp $
+  last mod: $Id: toplevel.c,v 1.15 2003/05/10 22:02:32 giles Exp $
 
  ********************************************************************/
 
@@ -1007,6 +1007,7 @@
   return 1;
 }
 
+/* build the initial short header for stream recognition and format */
 int theora_encode_header(theora_state *t, ogg_packet *op){
   CP_INSTANCE *cpi=(CP_INSTANCE *)(t->internal_encode);
 
@@ -1035,15 +1036,41 @@
   oggpackB_write(&cpi->oggbuffer,cpi->pb.info.target_bitrate,24);
   oggpackB_write(&cpi->oggbuffer,cpi->pb.info.quality,6);
 
-  /* dbm -- added functions to write important data (qtables + huff stuff) into header
-     TODO: split this into a separate packet */
+  op->packet=oggpackB_get_buffer(&cpi->oggbuffer);
+  op->bytes=oggpackB_bytes(&cpi->oggbuffer);
+
+  op->b_o_s=1;
+  op->e_o_s=0;
+  
+  op->packetno=0;
+  
+  op->granulepos=0;
+  cpi->packetflag=0;
+
+  return(0);
+}
+
+/* build the final header packet with the tables required
+   for decode */
+int theora_encode_tables(theora_state *t, ogg_packet *op){
+  CP_INSTANCE *cpi=(CP_INSTANCE *)(t->internal_encode);
+
+  oggpackB_reset(&cpi->oggbuffer);
+  oggpackB_write(&cpi->oggbuffer,0x82,8);
+  oggpackB_write(&cpi->oggbuffer,'t',8);
+  oggpackB_write(&cpi->oggbuffer,'h',8);
+  oggpackB_write(&cpi->oggbuffer,'e',8);
+  oggpackB_write(&cpi->oggbuffer,'o',8);
+  oggpackB_write(&cpi->oggbuffer,'r',8);
+  oggpackB_write(&cpi->oggbuffer,'a',8);
+  
   write_Qtables(&cpi->oggbuffer);
   write_FrequencyCounts(&cpi->oggbuffer);
 
   op->packet=oggpackB_get_buffer(&cpi->oggbuffer);
   op->bytes=oggpackB_bytes(&cpi->oggbuffer);
 
-  op->b_o_s=1;
+  op->b_o_s=0;
   op->e_o_s=0;
   
   op->packetno=0;
@@ -1124,11 +1151,31 @@
   c->target_bitrate=oggpackB_read(&opb,24);
   c->quality=ret=oggpackB_read(&opb,6);
 
-  /* dbm -- read important stuff from the stream header: */
+  if(ret==-1)return(OC_BADHEADER);
+
+  return(0);
+}
+
+int theora_decode_tables(theora_info *c, ogg_packet *op){
+  oggpack_buffer opb;
+  oggpackB_readinit(&opb,op->packet,op->bytes);
+
+  {  
+    char id[6];
+    int i;
+    int typeflag=oggpackB_read(&opb,8);
+
+    if(typeflag!=0x82)return(OC_NOTFORMAT);
+    
+    for(i=0;i<6;i++)
+      id[i]=(char)oggpackB_read(&opb,8);
+    
+    if(memcmp(id,"theora",6))return(OC_NOTFORMAT);
+  }
+  
+  /* todo: check for error */
   read_Qtables(&opb);
   read_FrequencyCounts(&opb);
-
-  if(ret==-1)return(OC_BADHEADER);
 
   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