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

Ralph Giles giles at xiph.org
Sun May 11 17:20:06 PDT 2003



giles       03/05/11 20:20:06

  Modified:    examples encoder_example.c player_example.c
               include/theora theora.h
               lib      Makefile.am toplevel.c
  Added:       lib      comment.c
  Log:
  Add a vorbiscomment-style metadata header to the theora bitstream
  format. As in vorbis, it's the second independent but required header
  packet, between the ident and info header and the decoder tables.
  
  The new file comment.c has functions for manipulating the new
  theora_comment structure. These and the comment structure are copied
  directly from the vorbis source; only the names have been changed. Since
  many xiph codecs share this format it's worth thinking about moving to
  libogg (or liboggfile). There remains however a need for codec-specific
  wrappers because all the codecs use a slightly different preamble.
  
  There are several decisions made here. Like in vorbis, the
  theora_comment structure is not associated with the info or codec state.
  This makes for more housekeeping on the player side, but is consistent.
  The justification in the vorbis source is 'so vorbis_info can be
  declared static'. Sure.
  
  Not so nice is that the separate parsing functions for each header
  packet have been retained. This was reasonable for one and two header
  packets, but I do think it makes too much work for the client with
  three and we should move to a single theora_header_in() like vorbis
  uses. This would take care of the parsing in order: the client just
  calls it three times. Note that this is simpler if, contrary to the
  above, the theora_comment is tracked inside the info or codec state
  structures.
  
  Finally, there's a question of endianness. Vorbis, the original ogg,
  and speex are all little endian; theora is big endian. Should the
  theora comment header by little or big endian? I picked little for
  consistency with the other codecs, but it makes for a point of confusion
  in the theora spec.

Revision  Changes    Path
1.9       +5 -1      theora/examples/encoder_example.c

Index: encoder_example.c
===================================================================
RCS file: /usr/local/cvsroot/theora/examples/encoder_example.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- encoder_example.c	11 May 2003 12:48:24 -0000	1.8
+++ encoder_example.c	12 May 2003 00:20:05 -0000	1.9
@@ -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.8 2003/05/11 12:48:24 giles Exp $
+  last mod: $Id: encoder_example.c,v 1.9 2003/05/12 00:20:05 giles Exp $
 
  ********************************************************************/
 
@@ -425,6 +425,7 @@
 
   theora_state     td;
   theora_info      ti;
+  theora_comment   tc;
 
   vorbis_info      vi; /* struct that stores all the static vorbis bitstream
                           settings */
@@ -565,6 +566,9 @@
   fwrite(og.body,1,og.body_len,stdout);
 
   /* create the remaining theora headers */
+  theora_comment_init(&tc);
+  theora_encode_comment(&tc,&op);
+  ogg_stream_packetin(&to,&op);
   theora_encode_tables(&td,&op);
   ogg_stream_packetin(&to,&op); 
   

<p><p>1.11      +44 -8     theora/examples/player_example.c

Index: player_example.c
===================================================================
RCS file: /usr/local/cvsroot/theora/examples/player_example.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- player_example.c	10 May 2003 22:02:32 -0000	1.10
+++ player_example.c	12 May 2003 00:20:05 -0000	1.11
@@ -12,7 +12,7 @@
 
   function: example SDL player application; plays Ogg Theora files (with
             optional Vorbis audio second stream)
-  last mod: $Id: player_example.c,v 1.10 2003/05/10 22:02:32 giles Exp $
+  last mod: $Id: player_example.c,v 1.11 2003/05/12 00:20:05 giles Exp $
 
  ********************************************************************/
 
@@ -68,6 +68,7 @@
 ogg_stream_state vo;
 ogg_stream_state to;
 theora_info      ti;
+theora_comment   tc;
 theora_state     td;
 vorbis_info      vi;
 vorbis_dsp_state vd;
@@ -345,6 +346,28 @@
   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);
+}
 
 /* helper: push a page into the appropriate steam */
 /* this can be done blindly; a stream won't accept a page
@@ -411,21 +434,34 @@
   }
   
   /* we're expecting more header packets. */
-  while((theora_p && theora_p<2) || (vorbis_p && vorbis_p<3)){
+  while((theora_p && theora_p<3) || (vorbis_p && vorbis_p<3)){
     int ret;
     
     /* look for further theora headers */
-    while(theora_p && (theora_p<2) && (ret=ogg_stream_packetout(&to,&op))){
+    while(theora_p && (theora_p<3) && (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);
+      if(theora_p==1){
+        if(theora_decode_comment(&tc,&op)){
+          fprintf(stderr,"Error parsing Theora stream headers; corrupt stream?\n");
+          exit(1);
+        }else{
+          dump_comments(&tc);
+          theora_p++;
+          continue;
+        }
+      }
+      if(theora_p==2){
+        if(theora_decode_tables(&ti,&op)){
+          fprintf(stderr,"Error parsing Theora stream headers; corrupt stream?\n");
+          exit(1);
+        }
+        theora_p++;
+        /* fall through */
       }
-      theora_p++;
-      if(theora_p==2) break;
+      if(theora_p==3)break;
     }
 
     /* look for more vorbis header packets */  

<p><p>1.5       +21 -2     theora/include/theora/theora.h

Index: theora.h
===================================================================
RCS file: /usr/local/cvsroot/theora/include/theora/theora.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- theora.h	25 Sep 2002 02:38:10 -0000	1.4
+++ theora.h	12 May 2003 00:20:05 -0000	1.5
@@ -11,7 +11,7 @@
  ********************************************************************
 
   function: 
-  last mod: $Id: theora.h,v 1.4 2002/09/25 02:38:10 xiphmont Exp $
+  last mod: $Id: theora.h,v 1.5 2003/05/12 00:20:05 giles Exp $
 
  ********************************************************************/
 
@@ -71,8 +71,17 @@
 
   void *internal_encode;
   void *internal_decode;
+
 } theora_state;
 
+typedef struct theora_comment{
+  char **user_comments;
+  int   *comment_lengths;
+  int    comments;
+  char  *vendor;                                                          
+
+} theora_comment;
+
 #define OC_FAULT       -1
 #define OC_EINVAL      -10
 #define OC_BADHEADER   -20
@@ -88,14 +97,24 @@
 extern int theora_encode_packetout( theora_state *t, int last_p, 
                                     ogg_packet *op);
 extern int theora_encode_header(theora_state *t, ogg_packet *op);
+extern int theora_encode_comment(theora_comment *tc, ogg_packet *op);
 extern int theora_decode_header(theora_info *c, ogg_packet *op);
+extern int theora_decode_comment(theora_comment *tc, ogg_packet *op); 
 extern int theora_decode_init(theora_state *th, theora_info *c);
 extern int theora_decode_packetin(theora_state *th,ogg_packet *op);
 extern int theora_decode_YUVout(theora_state *th,yuv_buffer *yuv);
 extern double theora_granule_time(theora_state *th,ogg_int64_t granulepos);
 extern void theora_clear(theora_state *t);
 
+extern void theora_comment_init(theora_comment *tc);
+extern void theora_comment_add(theora_comment *tc, char *comment);
+extern void theora_comment_add_tag(theora_comment *tc,
+                                       char *tag, char *value);
+extern char *theora_comment_query(theora_comment *tc, char *tag, int count);
+extern int   theora_comment_query_count(theora_comment *tc, char *tag);
+extern void  theora_comment_clear(theora_comment *tc);
+
 
 
-#endif
+#endif /* _O_THEORA_H_ */
 

<p><p>1.3       +2 -1      theora/lib/Makefile.am

Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/theora/lib/Makefile.am,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- Makefile.am	7 Apr 2003 14:42:59 -0000	1.2
+++ Makefile.am	12 May 2003 00:20:06 -0000	1.3
@@ -8,7 +8,8 @@
         encoder_internal.h idct.c reconstruct.c block_inline.h \
         encoder_lookup.h mcomp.c scan.c blockmap.c misc_common.c \
         dct.c frarray.c pb.c dct_decode.c frinit.c pp.c dct_encode.c \
-	huffman.c pp.h toplevel.c decode.c huffman.h quant.c toplevel_lookup.h
+	huffman.c pp.h toplevel.c decode.c huffman.h quant.c \
+	comment.c toplevel_lookup.h
 
 libtheora_la_LDFLAGS = -version-info @V_LIB_CURRENT@:@V_LIB_REVISION@:@V_LIB_AGE@
 

<p><p>1.16      +106 -13   theora/lib/toplevel.c

Index: toplevel.c
===================================================================
RCS file: /usr/local/cvsroot/theora/lib/toplevel.c,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- toplevel.c	10 May 2003 22:02:32 -0000	1.15
+++ toplevel.c	12 May 2003 00:20:06 -0000	1.16
@@ -11,7 +11,7 @@
  ********************************************************************
 
   function: 
-  last mod: $Id: toplevel.c,v 1.15 2003/05/10 22:02:32 giles Exp $
+  last mod: $Id: toplevel.c,v 1.16 2003/05/12 00:20:06 giles Exp $
 
  ********************************************************************/
 
@@ -1007,18 +1007,29 @@
   return 1;
 }
 
+static void _tp_readbuffer(oggpack_buffer *opb, char *buf, const long len)
+{
+  long i;
+  
+  for (i = 0; i < len; i++)
+    *buf++=oggpack_read(opb,8);
+}
+
+static void _tp_writebuffer(oggpack_buffer *opb, const char *buf, const long len)
+{
+  long i;
+  
+  for (i = 0; i < len; i++)
+    oggpack_write(opb, *buf++, 8);
+}
+ 
 /* 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);
 
   oggpackB_reset(&cpi->oggbuffer);
   oggpackB_write(&cpi->oggbuffer,0x80,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);
+  _tp_writebuffer(&cpi->oggbuffer, "theora", 6);
   
   oggpackB_write(&cpi->oggbuffer,VERSION_MAJOR,8);
   oggpackB_write(&cpi->oggbuffer,VERSION_MINOR,8);
@@ -1050,6 +1061,49 @@
   return(0);
 }
 
+/* build the comment header packet from the passed metadata */
+int theora_encode_comment(theora_comment *tc, ogg_packet *op)
+{
+  const char *vendor = theora_version_string();
+  const int vendor_length = strlen(vendor);
+  oggpack_buffer opb;
+  
+  oggpack_writeinit(&opb);
+  oggpack_write(&opb, 0x81, 8);
+  _tp_writebuffer(&opb, "theora", 6);
+  
+  oggpack_write(&opb, vendor_length, 32);
+  _tp_writebuffer(&opb, vendor, vendor_length);
+  
+  oggpack_write(&opb, tc->comments, 32);
+  if(tc->comments){
+    int i;
+    for(i=0;i<tc->comments;i++){
+      if(tc->user_comments[i]){
+        oggpack_write(&opb,tc->comment_lengths[i],32);
+        _tp_writebuffer(&opb,tc->user_comments[i],tc->comment_lengths[i]);
+      }else{
+        oggpack_write(&opb,0,32);
+      }
+    }
+  }
+  {
+    int bytes=oggpack_bytes(&opb);
+    op->packet=malloc(bytes);
+    memcpy(op->packet, oggpack_get_buffer(&opb), bytes);
+    op->bytes=bytes;
+  }
+  oggpack_writeclear(&opb);
+
+  op->b_o_s=0;
+  op->e_o_s=0;
+  
+  op->packetno=0;
+  op->granulepos=0;
+  
+  return (0);
+}
+
 /* build the final header packet with the tables required
    for decode */
 int theora_encode_tables(theora_state *t, ogg_packet *op){
@@ -1057,12 +1111,7 @@
 
   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);
+  _tp_writebuffer(&cpi->oggbuffer,"theora",6);
   
   write_Qtables(&cpi->oggbuffer);
   write_FrequencyCounts(&cpi->oggbuffer);
@@ -1154,6 +1203,50 @@
   if(ret==-1)return(OC_BADHEADER);
 
   return(0);
+}
+
+int theora_decode_comment(theora_comment *tc, ogg_packet *op){
+  oggpack_buffer opb;
+  oggpack_readinit(&opb,op->packet,op->bytes);
+
+  {
+    char id[6];
+    int typeflag=oggpack_read(&opb,8);
+
+    if(typeflag!=0x81)return(OC_NOTFORMAT);
+    
+    _tp_readbuffer(&opb, id, 6);
+    if(memcmp(id,"theora",6))return(OC_NOTFORMAT);
+  }
+  
+  {
+    int i;
+    int len = oggpack_read(&opb,32);
+    if(len<0)return(OC_BADHEADER);
+    tc->vendor=_ogg_calloc(1,len+1);
+    _tp_readbuffer(&opb,tc->vendor, len);
+    tc->vendor[len]='\0';
+  
+    tc->comments=oggpack_read(&opb,32);
+    if(tc->comments<0)goto parse_err;
+    tc->user_comments=_ogg_calloc(tc->comments,sizeof(*tc->user_comments));
+    tc->comment_lengths=_ogg_calloc(tc->comments,sizeof(*tc->comment_lengths));
+    for(i=0;i<tc->comments;i++){
+      len=oggpack_read(&opb,32);
+      if(len<0)goto parse_err;
+      if(len){
+        tc->user_comments[i]=_ogg_calloc(1,len+1);
+        _tp_readbuffer(&opb,tc->user_comments[i],len);
+        tc->user_comments[i][len]='\0';
+        tc->comment_lengths[i]=len;
+      }
+    }
+  }
+  return(0);
+
+parse_err:
+  theora_comment_clear(tc);
+  return(OC_BADHEADER);
 }
 
 int theora_decode_tables(theora_info *c, ogg_packet *op){

<p><p>1.1                  theora/lib/comment.c

Index: comment.c
===================================================================
/********************************************************************
 *                                                                  *
 * 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 OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002             *
 * by the Xiph.Org Foundation http://www.xiph.org/                  *
 *                                                                  *
 ********************************************************************

  function: read/write and client interface for comment header packet
  last mod: $Id: comment.c,v 1.1 2003/05/12 00:20:06 giles Exp $

 ********************************************************************/

#include <stdlib.h>
#include <ogg/ogg.h>
#include <theora/theora.h>
#include "encoder_internal.h"

#define CommentString "Xiph.Org libTheora I 20020916 3 1 0"

void theora_comment_init(theora_comment *tc){
  memset(tc,0,sizeof(*tc));
}
                                                                                
void theora_comment_add(theora_comment *tc,char *comment){
  tc->user_comments=_ogg_realloc(tc->user_comments,
                            (tc->comments+2)*sizeof(*tc->user_comments));
  tc->comment_lengths=_ogg_realloc(tc->comment_lengths,
                            (tc->comments+2)*sizeof(*tc->comment_lengths));
  tc->comment_lengths[tc->comments]=strlen(comment);
  tc->user_comments[tc->comments]=_ogg_malloc(tc->comment_lengths[tc->comments]+1);
  strcpy(tc->user_comments[tc->comments], comment);
  tc->comments++;
  tc->user_comments[tc->comments]=NULL;
}

void theora_comment_add_tag(theora_comment *tc, char *tag, char *value){
  char *comment=alloca(strlen(tag)+strlen(value)+2); /* +2 for = and \0 */
  strcpy(comment, tag);
  strcat(comment, "=");
  strcat(comment, value);
  theora_comment_add(tc, comment);
}
                                                                                
/* This is more or less the same as strncasecmp - but that doesn't exist
 * everywhere, and this is a fairly trivial function, so we include it */
static int tagcompare(const char *s1, const char *s2, int n){
  int c=0;
  while(c < n){
    if(toupper(s1[c]) != toupper(s2[c]))
      return !0;
    c++;
  }
  return 0;
}

char *theora_comment_query(theora_comment *tc, char *tag, int count){
  long i;
  int found = 0;
  int taglen = strlen(tag)+1; /* +1 for the = we append */
  char *fulltag = alloca(taglen+ 1);
                                                                                
  strcpy(fulltag, tag);
  strcat(fulltag, "=");
                                                                                
  for(i=0;i<tc->comments;i++){
    if(!tagcompare(tc->user_comments[i], fulltag, taglen)){
      if(count == found)
        /* We return a pointer to the data, not a copy */
        return tc->user_comments[i] + taglen;
      else
        found++;
    }
  }
  return NULL; /* didn't find anything */
}

int theora_comment_query_count(theora_comment *tc, char *tag){
  int i,count=0;
  int taglen = strlen(tag)+1; /* +1 for the = we append */
  char *fulltag = alloca(taglen+1);
  strcpy(fulltag,tag);
  strcat(fulltag, "=");
                                                                                
  for(i=0;i<tc->comments;i++){
    if(!tagcompare(tc->user_comments[i], fulltag, taglen))
      count++;
  }
                                                                                
  return count;
}

void theora_comment_clear(theora_comment *tc){
  if(tc){
    long i;
    for(i=0;i<tc->comments;i++)
      if(tc->user_comments[i])_ogg_free(tc->user_comments[i]);
    if(tc->user_comments)_ogg_free(tc->user_comments);
        if(tc->comment_lengths)_ogg_free(tc->comment_lengths);
    if(tc->vendor)_ogg_free(tc->vendor);
  }
  memset(tc,0,sizeof(*tc));
}

<p><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