[xiph-commits] r9352 - trunk/vorbis-tools/ogginfo

giles at motherfish-iii.xiph.org giles at motherfish-iii.xiph.org
Sat Jun 4 13:15:21 PDT 2005


Author: giles
Date: 2005-06-04 13:15:19 -0700 (Sat, 04 Jun 2005)
New Revision: 9352

Added:
   trunk/vorbis-tools/ogginfo/theora.c
   trunk/vorbis-tools/ogginfo/theora.h
Modified:
   trunk/vorbis-tools/ogginfo/Makefile.am
   trunk/vorbis-tools/ogginfo/ogginfo.1
   trunk/vorbis-tools/ogginfo/ogginfo2.c
Log:
Add rough theora support to ogginfo. This needs further testing.

Implementaiton by Mike Smith, cleanup by Ralph Giles.



Modified: trunk/vorbis-tools/ogginfo/Makefile.am
===================================================================
--- trunk/vorbis-tools/ogginfo/Makefile.am	2005-06-04 17:26:46 UTC (rev 9351)
+++ trunk/vorbis-tools/ogginfo/Makefile.am	2005-06-04 20:15:19 UTC (rev 9352)
@@ -3,7 +3,7 @@
 AUTOMAKE_OPTIONS = foreign
 
 mans = ogginfo.1
-ogginfosources = ogginfo2.c
+ogginfosources = ogginfo2.c theora.c
 
 datadir = @datadir@
 localedir = $(datadir)/locale

Modified: trunk/vorbis-tools/ogginfo/ogginfo.1
===================================================================
--- trunk/vorbis-tools/ogginfo/ogginfo.1	2005-06-04 17:26:46 UTC (rev 9351)
+++ trunk/vorbis-tools/ogginfo/ogginfo.1	2005-06-04 20:15:19 UTC (rev 9352)
@@ -37,6 +37,10 @@
 and number of channels, the bitrate and playback length, and the contents of
 the comment header are printed.
 
+Similarly, for
+.B Theora
+streams, basic information about the video is provided, including frame rate, aspect ratio, bitrate, length, and the comment header.
+
 .SH OPTIONS
 .IP -h
 Show a help and usage message.

Modified: trunk/vorbis-tools/ogginfo/ogginfo2.c
===================================================================
--- trunk/vorbis-tools/ogginfo/ogginfo2.c	2005-06-04 17:26:46 UTC (rev 9351)
+++ trunk/vorbis-tools/ogginfo/ogginfo2.c	2005-06-04 20:15:19 UTC (rev 9352)
@@ -2,7 +2,7 @@
  *
  * A tool to describe ogg file contents and metadata.
  *
- * Copyright 2002 Michael Smith <msmith at xiph.org>
+ * Copyright 2002-2005 Michael Smith <msmith at xiph.org>
  * Licensed under the GNU GPL, distributed with this program.
  */
 
@@ -21,6 +21,8 @@
 #include "utf8.h"
 #include "i18n.h"
 
+#include "theora.h"
+
 #define CHUNK 4500
 
 struct vorbis_release {
@@ -88,6 +90,17 @@
     int doneheaders;
 } misc_vorbis_info;
 
+typedef struct {
+    theora_info ti;
+    theora_comment tc;
+
+    ogg_int64_t bytes;
+    ogg_int64_t lastgranulepos;
+    ogg_int64_t firstgranulepos;
+
+    int doneheaders;
+} misc_theora_info;
+
 static int printinfo = 1;
 static int printwarn = 1;
 static int verbose = 1;
@@ -143,6 +156,296 @@
     va_end(ap);
 }
 
+static void check_xiph_comment(stream_processor *stream, int i, char *comment)
+{
+    char *sep = strchr(comment, '=');
+    char *decoded;
+    int j;
+    int broken = 0;
+    unsigned char *val;
+    int bytes;
+    int remaining;
+
+    if(sep == NULL) {
+        warn(_("Warning: Comment %d in stream %d has invalid "
+              "format, does not contain '=': \"%s\"\n"), 
+              i, stream->num, comment);
+             return;
+    }
+
+    for(j=0; j < sep-comment; j++) {
+        if(comment[j] < 0x20 || comment[j] > 0x7D) {
+            warn(_("Warning: Invalid comment fieldname in "
+                   "comment %d (stream %d): \"%s\"\n"),
+                   i, stream->num, comment);
+            broken = 1;
+            break;
+        }
+    }
+
+    if(broken)
+	return;
+
+    val = comment;
+
+    j = sep-comment+1;
+    while(j < comment)
+    {
+        remaining = comment - j;
+        if((val[j] & 0x80) == 0)
+            bytes = 1;
+        else if((val[j] & 0x40) == 0x40) {
+            if((val[j] & 0x20) == 0)
+                bytes = 2;
+            else if((val[j] & 0x10) == 0)
+                bytes = 3;
+            else if((val[j] & 0x08) == 0)
+                bytes = 4;
+            else if((val[j] & 0x04) == 0)
+                bytes = 5;
+            else if((val[j] & 0x02) == 0)
+                bytes = 6;
+            else {
+                warn(_("Warning: Illegal UTF-8 sequence in "
+                    "comment %d (stream %d): length marker wrong\n"),
+                    i, stream->num);
+                broken = 1;
+                break;
+            }
+        }
+        else {
+            warn(_("Warning: Illegal UTF-8 sequence in comment "
+                "%d (stream %d): length marker wrong\n"), i, stream->num);
+            broken = 1;
+            break;
+        }
+
+        if(bytes > remaining) {
+            warn(_("Warning: Illegal UTF-8 sequence in comment "
+                "%d (stream %d): too few bytes\n"), i, stream->num);
+            broken = 1;
+            break;
+        }
+
+        switch(bytes) {
+            case 1:
+                /* No more checks needed */
+                break;
+            case 2:
+                if((val[j+1] & 0xC0) != 0x80)
+                    broken = 1;
+                if((val[j] & 0xFE) == 0xC0)
+                    broken = 1;
+                break;
+            case 3:
+                if(!((val[j] == 0xE0 && val[j+1] >= 0xA0 && val[j+1] <= 0xBF && 
+                         (val[j+2] & 0xC0) == 0x80) ||
+                     (val[j] >= 0xE1 && val[j] <= 0xEC && 
+                         (val[j+1] & 0xC0) == 0x80 &&
+                         (val[j+2] & 0xC0) == 0x80) ||
+                     (val[j] == 0xED && val[j+1] >= 0x80 &&
+                         val[j+1] <= 0x9F &&
+                         (val[j+2] & 0xC0) == 0x80) ||
+                     (val[j] >= 0xEE && val[j] <= 0xEF &&
+                         (val[j+1] & 0xC0) == 0x80 &&
+                         (val[j+2] & 0xC0) == 0x80)))
+                     broken = 1;
+                 if(val[j] == 0xE0 && (val[j+1] & 0xE0) == 0x80)
+                     broken = 1;
+                 break;
+            case 4:
+                 if(!((val[j] == 0xF0 && val[j+1] >= 0x90 &&
+                         val[j+1] <= 0xBF &&
+                         (val[j+2] & 0xC0) == 0x80 &&
+                         (val[j+3] & 0xC0) == 0x80) ||
+                     (val[j] >= 0xF1 && val[j] <= 0xF3 &&
+                         (val[j+1] & 0xC0) == 0x80 &&
+                         (val[j+2] & 0xC0) == 0x80 &&
+                         (val[j+3] & 0xC0) == 0x80) ||
+                     (val[j] == 0xF4 && val[j+1] >= 0x80 &&
+                         val[j+1] <= 0x8F &&
+                         (val[j+2] & 0xC0) == 0x80 &&
+                         (val[j+3] & 0xC0) == 0x80)))
+                     broken = 1;
+                 if(val[j] == 0xF0 && (val[j+1] & 0xF0) == 0x80)
+                     broken = 1;
+                 break;
+             /* 5 and 6 aren't actually allowed at this point */
+             case 5:
+                 broken = 1;
+                 break;
+             case 6:
+                 broken = 1;
+                 break;
+         }
+
+         if(broken) {
+             warn(_("Warning: Illegal UTF-8 sequence in comment "
+                   "%d (stream %d): invalid sequence\n"), i, stream->num);
+             broken = 1;
+             break;
+         }
+
+         j += bytes;
+     }
+
+     if(!broken) {
+         if(utf8_decode(sep+1, &decoded) < 0) {
+             warn(_("Warning: Failure in utf8 decoder. This should be impossible\n"));
+             return;
+	 }
+     }
+     
+     *sep = 0;
+     info("\t%s=%s\n", comment, decoded);
+     free(decoded);
+}
+
+static void theora_process(stream_processor *stream, ogg_page *page)
+{
+    ogg_packet packet;
+    misc_theora_info *inf = stream->data;
+    int i, header=0;
+    int k;
+
+    ogg_stream_pagein(&stream->os, page);
+    if(inf->doneheaders < 3)
+        header = 1;
+
+    while(ogg_stream_packetout(&stream->os, &packet) > 0) {
+        if(inf->doneheaders < 3) {
+            if(theora_decode_header(&inf->ti, &inf->tc, &packet) < 0) {
+                warn(_("Warning: Could not decode theora header "
+                       "packet - invalid theora stream (%d)\n"), stream->num);
+                continue;
+            }
+            inf->doneheaders++;
+            if(inf->doneheaders == 3) {
+                if(ogg_page_granulepos(page) != 0 || ogg_stream_packetpeek(&stream->os, NULL) == 1)
+                    warn(_("Warning: Theora stream %d does not have headers "
+                           "correctly framed. Terminal header page contains "
+                           "additional packets or has non-zero granulepos\n"),
+                            stream->num);
+                info(_("Theora headers parsed for stream %d, "
+                       "information follows...\n"), stream->num);
+
+                info(_("Version: %d.%d.%d\n"), inf->ti.version_major, inf->ti.version_minor, inf->ti.version_subminor);
+   
+                info(_("Vendor: %s\n"), inf->tc.vendor);
+                info(_("Width: %d\n"), inf->ti.frame_width);
+                info(_("Height: %d\n"), inf->ti.frame_height);
+		info(_("Total image: %d by %d, offset x %d, offset y %d\n\n"),
+		    inf->ti.width, inf->ti.height, inf->ti.offset_x, inf->ti.offset_y);
+		if(inf->ti.offset_x + inf->ti.frame_width > inf->ti.width)
+		    warn(_("Frame offset/size invalid: width incorrect\n"));
+		if(inf->ti.offset_y + inf->ti.frame_height > inf->ti.height)
+		    warn(_("Frame offset/size invalid: height incorrect\n"));
+
+		if(inf->ti.fps_numerator == 0 || inf->ti.fps_denominator == 0) 
+		   warn(_("Invalid zero framerate\n"));
+		else
+		   info(_("Framerate %d/%d (%f fps)\n"), inf->ti.fps_numerator, inf->ti.fps_denominator, (float)inf->ti.fps_numerator/(float)inf->ti.fps_denominator);
+		
+		if(inf->ti.aspect_numerator == 0 || inf->ti.aspect_denominator == 0) 
+		{
+		    info(_("Aspect ratio undefined\n"));
+		}	
+		else
+		{
+		    float frameaspect = (float)inf->ti.frame_width/(float)inf->ti.frame_height * (float)inf->ti.aspect_numerator/(float)inf->ti.aspect_denominator; 
+		    info(_("Pixel aspect ratio %d:%d (1:%f)\n"), inf->ti.aspect_numerator, inf->ti.aspect_denominator, (float)inf->ti.aspect_numerator/(float)inf->ti.aspect_denominator);
+                    if(abs(frameaspect - 4.0/3.0) < 0.02)
+			info("Frame aspect 4:3\n");
+                    else if(abs(frameaspect - 16.0/9.0) < 0.02)
+			info("Frame aspect 16:9\n");
+		    else
+			info("Frame aspect 1:%d\n", frameaspect);
+		}
+
+		if(inf->ti.colorspace == OC_CS_ITU_REC_470M)
+		    info("Colourspace: Rec. ITU-R BT.470-6 System M (NTSC)\n"); 
+		else if(inf->ti.colorspace == OC_CS_ITU_REC_470BG)
+		    info("Colourspace: Rec. ITU-R BT.470-6 Systems B and G (PAL)\n"); 
+		else
+		    info("Colourspace unspecified\n");
+
+		if(inf->ti.pixelformat == OC_PF_420)
+		    info("Pixel format 4:2:0\n");
+		else if(inf->ti.pixelformat == OC_PF_422)
+		    info("Pixel format 4:2:2\n");
+		else if(inf->ti.pixelformat == OC_PF_444)
+		    info("Pixel format 4:4:4\n");
+		else
+		    warn("Pixel format invalid\n");
+
+		info("Target bitrate: %d kbps\n", inf->ti.target_bitrate/1000);
+		info("Nominal quality setting (0-63): %d\n", inf->ti.quality);
+
+                if(inf->tc.comments > 0)
+                    info(_("User comments section follows...\n"));
+
+                for(i=0; i < inf->tc.comments; i++) {
+                    char *comment = inf->tc.user_comments[i];
+		    check_xiph_comment(stream, i, comment);
+		}
+	    }
+	}
+    }
+
+    if(!header) {
+        ogg_int64_t gp = ogg_page_granulepos(page);
+        if(gp > 0) {
+            if(gp < inf->lastgranulepos)
+#ifdef _WIN32
+                warn(_("Warning: granulepos in stream %d decreases from %I64d to %I64d" ),
+                        stream->num, inf->lastgranulepos, gp);
+#else
+                warn(_("Warning: granulepos in stream %d decreases from %lld to %lld" ),
+                        stream->num, inf->lastgranulepos, gp);
+#endif
+            inf->lastgranulepos = gp;
+        }
+        if(inf->firstgranulepos < 0) { /* Not set yet */
+        }
+        inf->bytes += page->header_len + page->body_len;
+    }
+}
+
+static void theora_end(stream_processor *stream) 
+{
+    misc_theora_info *inf = stream->data;
+    long minutes, seconds, milliseconds;
+    double bitrate, time;
+
+    /* This should be lastgranulepos - startgranulepos, or something like that*/
+    time = (double)inf->lastgranulepos / 
+	((float)inf->ti.fps_numerator/(float)inf->ti.fps_denominator);
+    minutes = (long)time / 60;
+    seconds = (long)time - minutes*60;
+    milliseconds = (long)((time - minutes*60 - seconds)*1000);
+    bitrate = inf->bytes*8 / time / 1000.0;
+
+#ifdef _WIN32
+    info(_("Theora stream %d:\n"
+           "\tTotal data length: %I64d bytes\n"
+           "\tPlayback length: %ldm:%02ld.%03lds\n"
+           "\tAverage bitrate: %f kb/s\n"), 
+            stream->num,inf->bytes, minutes, seconds, milliseconds, bitrate);
+#else
+    info(_("Theora stream %d:\n"
+           "\tTotal data length: %lld bytes\n"
+           "\tPlayback length: %ldm:%02ld.%03lds\n"
+           "\tAverage bitrate: %f kb/s\n"), 
+            stream->num,inf->bytes, minutes, seconds, milliseconds, bitrate);
+#endif
+
+    theora_comment_clear(&inf->tc);
+    theora_info_clear(&inf->ti);
+
+    free(stream->data);
+}
+
+
 static void vorbis_process(stream_processor *stream, ogg_page *page )
 {
     ogg_packet packet;
@@ -208,154 +511,9 @@
                     info(_("User comments section follows...\n"));
 
                 for(i=0; i < inf->vc.comments; i++) {
-                    char *sep = strchr(inf->vc.user_comments[i], '=');
-                    char *decoded;
-                    int j;
-                    int broken = 0;
-                    unsigned char *val;
-                    int bytes;
-                    int remaining;
-
-                    if(sep == NULL) {
-                        warn(_("Warning: Comment %d in stream %d is invalidly "
-                               "formatted, does not contain '=': \"%s\"\n"), 
-                               i, stream->num, inf->vc.user_comments[i]);
-                        continue;
-                    }
-
-                    for(j=0; j < sep-inf->vc.user_comments[i]; j++) {
-                        if(inf->vc.user_comments[i][j] < 0x20 ||
-                           inf->vc.user_comments[i][j] > 0x7D) {
-                            warn(_("Warning: Invalid comment fieldname in "
-                                   "comment %d (stream %d): \"%s\"\n"),
-                                    i, stream->num, inf->vc.user_comments[i]);
-                            broken = 1;
-                            break;
-                        }
-                    }
-
-                    if(broken)
-                        continue;
-
-                    val = inf->vc.user_comments[i];
-
-                    j = sep-inf->vc.user_comments[i]+1;
-                    while(j < inf->vc.comment_lengths[i])
-                    {
-                        remaining = inf->vc.comment_lengths[i] - j;
-                        if((val[j] & 0x80) == 0)
-                            bytes = 1;
-                        else if((val[j] & 0x40) == 0x40) {
-                            if((val[j] & 0x20) == 0)
-                                bytes = 2;
-                            else if((val[j] & 0x10) == 0)
-                                bytes = 3;
-                            else if((val[j] & 0x08) == 0)
-                                bytes = 4;
-                            else if((val[j] & 0x04) == 0)
-                                bytes = 5;
-                            else if((val[j] & 0x02) == 0)
-                                bytes = 6;
-                            else {
-                                warn(_("Warning: Illegal UTF-8 sequence in "
-                                       "comment %d (stream %d): length "
-                                       "marker wrong\n"),
-                                        i, stream->num);
-                                broken = 1;
-                                break;
-                            }
-                        }
-                        else {
-                            warn(_("Warning: Illegal UTF-8 sequence in comment "
-                                   "%d (stream %d): length marker wrong\n"),
-                                    i, stream->num);
-                            broken = 1;
-                            break;
-                        }
-
-                        if(bytes > remaining) {
-                            warn(_("Warning: Illegal UTF-8 sequence in comment "
-                                   "%d (stream %d): too few bytes\n"),
-                                    i, stream->num);
-                            broken = 1;
-                            break;
-                        }
-
-                        switch(bytes) {
-                            case 1:
-                                /* No more checks needed */
-                                break;
-                            case 2:
-                                if((val[j+1] & 0xC0) != 0x80)
-                                    broken = 1;
-                                if((val[j] & 0xFE) == 0xC0)
-                                    broken = 1;
-                                break;
-                            case 3:
-                                if(!((val[j] == 0xE0 && val[j+1] >= 0xA0 && 
-                                        val[j+1] <= 0xBF && 
-                                        (val[j+2] & 0xC0) == 0x80) ||
-                                   (val[j] >= 0xE1 && val[j] <= 0xEC &&
-                                        (val[j+1] & 0xC0) == 0x80 &&
-                                        (val[j+2] & 0xC0) == 0x80) ||
-                                   (val[j] == 0xED && val[j+1] >= 0x80 &&
-                                        val[j+1] <= 0x9F &&
-                                        (val[j+2] & 0xC0) == 0x80) ||
-                                   (val[j] >= 0xEE && val[j] <= 0xEF &&
-                                        (val[j+1] & 0xC0) == 0x80 &&
-                                        (val[j+2] & 0xC0) == 0x80)))
-                                    broken = 1;
-                                if(val[j] == 0xE0 && (val[j+1] & 0xE0) == 0x80)
-                                    broken = 1;
-                                break;
-                            case 4:
-                                if(!((val[j] == 0xF0 && val[j+1] >= 0x90 &&
-                                        val[j+1] <= 0xBF &&
-                                        (val[j+2] & 0xC0) == 0x80 &&
-                                        (val[j+3] & 0xC0) == 0x80) ||
-                                     (val[j] >= 0xF1 && val[j] <= 0xF3 &&
-                                        (val[j+1] & 0xC0) == 0x80 &&
-                                        (val[j+2] & 0xC0) == 0x80 &&
-                                        (val[j+3] & 0xC0) == 0x80) ||
-                                     (val[j] == 0xF4 && val[j+1] >= 0x80 &&
-                                        val[j+1] <= 0x8F &&
-                                        (val[j+2] & 0xC0) == 0x80 &&
-                                        (val[j+3] & 0xC0) == 0x80)))
-                                    broken = 1;
-                                if(val[j] == 0xF0 && (val[j+1] & 0xF0) == 0x80)
-                                    broken = 1;
-                                break;
-                            /* 5 and 6 aren't actually allowed at this point*/  
-                            case 5:
-                                broken = 1;
-                                break;
-                            case 6:
-                                broken = 1;
-                                break;
-                        }
-
-                        if(broken) {
-                            warn(_("Warning: Illegal UTF-8 sequence in comment "
-                                   "%d (stream %d): invalid sequence\n"),
-                                    i, stream->num);
-                            broken = 1;
-                            break;
-                        }
-
-                        j += bytes;
-                    }
-
-                    if(!broken) {
-                        if(utf8_decode(sep+1, &decoded) < 0) {
-                            warn(_("Warning: Failure in utf8 decoder. This "
-                                   "should be impossible\n"));
-                            continue;
-                        }
-                        *sep = 0;
-                        info("\t%s=%s\n", inf->vc.user_comments[i], decoded);
-                        free(decoded);
-                    }
-                }
+                    char *comment = inf->vc.user_comments[i];
+		    check_xiph_comment(stream, i, comment);
+		}
             }
         }
     }
@@ -478,6 +636,18 @@
     stream->process_end = NULL;
 }
 
+static void theora_start(stream_processor *stream)
+{
+    misc_theora_info *info;
+
+    stream->type = "theora";
+    stream->process_page = theora_process;
+    stream->process_end = theora_end;
+
+    stream->data = calloc(1, sizeof(misc_theora_info));
+    info = stream->data;
+}
+
 static void vorbis_start(stream_processor *stream)
 {
     misc_vorbis_info *info;
@@ -566,14 +736,14 @@
         }
         else if(packet.bytes >= 7 && memcmp(packet.packet, "\001vorbis", 7)==0)
             vorbis_start(stream);
+        else if(packet.bytes >= 7 && memcmp(packet.packet, "\200theora", 7)==0) 
+            theora_start(stream);
         else if(packet.bytes >= 8 && memcmp(packet.packet, "OggMIDI\0", 8)==0) 
             other_start(stream, "MIDI");
         else if(packet.bytes >= 4 && memcmp(packet.packet, "fLaC", 4)==0) 
             other_start(stream, "FLAC");
         else if(packet.bytes >= 8 && memcmp(packet.packet, "Speex   ", 8)==0) 
             other_start(stream, "speex");
-        else if(packet.bytes >= 7 && memcmp(packet.packet, "\200theora", 7)==0) 
-            other_start(stream, "Theora");
         else
             other_start(stream, NULL);
 
@@ -728,8 +898,8 @@
 }
 
 static void usage(void) {
-    printf(_("ogginfo 1.0.1\n"
-             "(c) 2003 Michael Smith <msmith at xiph.org>\n"
+    printf(_("ogginfo 1.1.0\n"
+             "(c) 2003-2005 Michael Smith <msmith at xiph.org>\n"
              "\n"
              "Usage: ogginfo [flags] file1.ogg [file2.ogg ... fileN.ogg]\n"
              "Flags supported:\n"
@@ -793,4 +963,3 @@
 
     return ret;
 }
-

Added: trunk/vorbis-tools/ogginfo/theora.c
===================================================================
--- trunk/vorbis-tools/ogginfo/theora.c	2005-06-04 17:26:46 UTC (rev 9351)
+++ trunk/vorbis-tools/ogginfo/theora.c	2005-06-04 20:15:19 UTC (rev 9352)
@@ -0,0 +1,230 @@
+/********************************************************************
+ *                                                                  *
+ * 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:
+  last mod: $Id: toplevel.c,v 1.39 2004/03/18 02:00:30 giles Exp $
+
+ ********************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include "theora.h"
+
+#define theora_read(x,y,z) ( *z = oggpackB_read(x,y) )
+
+static int _ilog(unsigned int v){
+  int ret=0;
+  while(v){
+    ret++;
+    v>>=1;
+  }
+  return(ret);
+}
+
+static void _tp_readbuffer(oggpack_buffer *opb, char *buf, const long len)
+{
+  long i;
+  long ret;
+
+  for (i = 0; i < len; i++) {
+    theora_read(opb, 8, &ret);
+    *buf++=(char)ret;
+  }
+}
+
+static void _tp_readlsbint(oggpack_buffer *opb, long *value)
+{
+  int i;
+  long ret[4];
+
+  for (i = 0; i < 4; i++) {
+    theora_read(opb,8,&ret[i]);
+  }
+  *value = ret[0]|ret[1]<<8|ret[2]<<16|ret[3]<<24;
+}
+
+void theora_info_clear(theora_info *c) {
+  memset(c,0,sizeof(*c));
+}
+
+static int _theora_unpack_info(theora_info *ci, oggpack_buffer *opb){
+  long ret;
+
+  theora_read(opb,8,&ret);
+  ci->version_major=(unsigned char)ret;
+  theora_read(opb,8,&ret);
+  ci->version_minor=(unsigned char)ret;
+  theora_read(opb,8,&ret);
+  ci->version_subminor=(unsigned char)ret;
+
+  theora_read(opb,16,&ret);
+  ci->width=ret<<4;
+  theora_read(opb,16,&ret);
+  ci->height=ret<<4;
+  theora_read(opb,24,&ret);
+  ci->frame_width=ret;
+  theora_read(opb,24,&ret);
+  ci->frame_height=ret;
+  theora_read(opb,8,&ret);
+  ci->offset_x=ret;
+  theora_read(opb,8,&ret);
+  ci->offset_y=ret;
+
+  theora_read(opb,32,&ret);
+  ci->fps_numerator=ret;
+  theora_read(opb,32,&ret);
+  ci->fps_denominator=ret;
+  theora_read(opb,24,&ret);
+  ci->aspect_numerator=ret;
+  theora_read(opb,24,&ret);
+  ci->aspect_denominator=ret;
+
+  theora_read(opb,8,&ret);
+  ci->colorspace=ret;
+  theora_read(opb,24,&ret);
+  ci->target_bitrate=ret;
+  theora_read(opb,6,&ret);
+  ci->quality=ret;
+
+  theora_read(opb,5,&ret);
+  ci->keyframe_frequency_force=1<<ret;
+
+  theora_read(opb,2,&ret);
+  ci->pixelformat=ret;
+
+  /* spare configuration bits */
+  if ( theora_read(opb,3,&ret) == -1 )
+    return (OC_BADHEADER);
+
+  return(0);
+}
+
+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));
+}
+
+static int _theora_unpack_comment(theora_comment *tc, oggpack_buffer *opb){
+  int i;
+  int len;
+
+   _tp_readlsbint(opb,(long *) &len);
+  if(len<0)return(OC_BADHEADER);
+  tc->vendor=_ogg_calloc(1,len+1);
+  _tp_readbuffer(opb,tc->vendor, len);
+  tc->vendor[len]='\0';
+
+  _tp_readlsbint(opb,(long *) &tc->comments);
+  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++){
+    _tp_readlsbint(opb,(long *)&len);
+    if(len<0)goto parse_err;
+    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);
+}
+
+static int _theora_unpack_tables(theora_info *c, oggpack_buffer *opb){
+  /* NOP: ogginfo doesn't use this information */
+}
+
+int theora_decode_header(theora_info *ci, theora_comment *cc, ogg_packet *op){
+  long ret;
+  oggpack_buffer *opb;
+  
+  if(!op)return OC_BADHEADER;
+  
+  opb = _ogg_malloc(sizeof(oggpack_buffer));
+  oggpackB_readinit(opb,op->packet,op->bytes);
+  {
+    char id[6];
+    int typeflag;
+    
+    theora_read(opb,8,&ret);
+    typeflag = ret;
+    if(!(typeflag&0x80)) {
+      free(opb);
+      return(OC_NOTFORMAT);
+    }
+
+    _tp_readbuffer(opb,id,6);
+    if(memcmp(id,"theora",6)) {
+      free(opb);
+      return(OC_NOTFORMAT);
+    }
+
+    switch(typeflag){
+    case 0x80:
+      if(!op->b_o_s){
+        /* Not the initial packet */
+        free(opb);
+        return(OC_BADHEADER);
+      }
+      if(ci->version_major!=0){
+        /* previously initialized info header */
+        free(opb);
+        return OC_BADHEADER;
+      }
+
+      ret = _theora_unpack_info(ci,opb);
+      free(opb);
+      return(ret);
+
+    case 0x81:
+      if(ci->version_major==0){
+        /* um... we didn't get the initial header */
+        free(opb);
+        return(OC_BADHEADER);
+      }
+
+      ret = _theora_unpack_comment(cc,opb);
+      free(opb);
+      return(ret);
+
+    case 0x82:
+      if(ci->version_major==0 || cc->vendor==NULL){
+        /* um... we didn't get the initial header or comments yet */
+        free(opb);
+        return(OC_BADHEADER);
+      }
+
+      ret = _theora_unpack_tables(ci,opb);
+      free(opb);
+      return(ret);
+    
+    default:
+      free(opb);
+      /* ignore any trailing header packets for forward compatibility */
+      return(OC_NEWPACKET);
+    }
+  }
+  /* I don't think it's possible to get this far, but better safe.. */
+  free(opb);
+  return(OC_BADHEADER);
+}
+

Added: trunk/vorbis-tools/ogginfo/theora.h
===================================================================
--- trunk/vorbis-tools/ogginfo/theora.h	2005-06-04 17:26:46 UTC (rev 9351)
+++ trunk/vorbis-tools/ogginfo/theora.h	2005-06-04 20:15:19 UTC (rev 9352)
@@ -0,0 +1,183 @@
+/********************************************************************
+ *                                                                  *
+ * 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:
+  last mod: $Id: theora.h,v 1.17 2003/12/06 18:06:19 arc Exp $
+
+ ********************************************************************/
+
+#include <ogg/ogg.h>
+
+/**
+ * A Colorspace.
+ */
+typedef enum {
+  OC_CS_UNSPECIFIED,	/**< the colorspace is unknown or unspecified */
+  OC_CS_ITU_REC_470M,	/**< best option for 'NTSC' content */
+  OC_CS_ITU_REC_470BG,	/**< best option for 'PAL' content */
+  OC_CS_NSPACES		/* mark the end of the defined colorspaces */
+} theora_colorspace;
+
+/**
+ * A Chroma subsampling
+ *
+ * These enumerate the available chroma subsampling options supported
+ * by the theora format. See Section 4.4 of the specification for
+ * exact definitions.
+ */
+typedef enum {
+  OC_PF_420,	/**< Chroma subsampling by 2 in each direction (4:2:0) */
+  OC_PF_RSVD,	/**< reserved value */
+  OC_PF_422,	/**< Horizonatal chroma subsampling by 2 (4:2:2) */
+  OC_PF_444,	/**< No chroma subsampling at all (4:4:4) */
+} theora_pixelformat;
+
+/**
+ * Theora bitstream info.
+ * Contains the basic playback parameters for a stream,
+ * corresponds to the initial 'info' header packet.
+ * 
+ * Encoded theora frames must be a multiple of 16 is size;
+ * this is what the width and height members represent. To
+ * handle other sizes, a crop rectangle is specified in 
+ * frame_height and frame_width, offset_x and offset_y. The
+ * offset and size should still be a power of 2 to avoid
+ * chroma sampling shifts.
+ *
+ * Frame rate, in frames per second is stored as a rational
+ * fraction. So is the aspect ratio. Note that this refers
+ * to the aspect ratio of the frame pixels, not of the
+ * overall frame itself.
+ * 
+ * see the example code for use of the other parameters and
+ * good default settings for the encoder parameters.
+ */
+typedef struct {
+  ogg_uint32_t  width;
+  ogg_uint32_t  height;
+  ogg_uint32_t  frame_width;
+  ogg_uint32_t  frame_height;
+  ogg_uint32_t  offset_x;
+  ogg_uint32_t  offset_y;
+  ogg_uint32_t  fps_numerator;
+  ogg_uint32_t  fps_denominator;
+  ogg_uint32_t  aspect_numerator;
+  ogg_uint32_t  aspect_denominator;
+  theora_colorspace colorspace;
+  int           target_bitrate;
+  int           quality;  /**< nominal quality setting, 0-63 */
+  int           quick_p;  /**< quick encode/decode */
+
+  /* decode only */
+  unsigned char version_major;
+  unsigned char version_minor;
+  unsigned char version_subminor;
+
+  void *codec_setup;
+
+  /* encode only */
+  int           dropframes_p;
+  int           keyframe_auto_p;
+  ogg_uint32_t  keyframe_frequency;
+  ogg_uint32_t  keyframe_frequency_force;  /* also used for decode init to
+                                              get granpos shift correct */
+  ogg_uint32_t  keyframe_data_target_bitrate;
+  ogg_int32_t   keyframe_auto_threshold;
+  ogg_uint32_t  keyframe_mindistance;
+  ogg_int32_t   noise_sensitivity;
+  ogg_int32_t   sharpness;
+
+  theora_pixelformat pixelformat;
+
+} theora_info;
+
+/** 
+ * Comment header metadata.
+ *
+ * This structure holds the in-stream metadata corresponding to
+ * the 'comment' header packet.
+ *
+ * Meta data is stored as a series of (tag, value) pairs, in
+ * length-encoded string vectors. The first occurence of the 
+ * '=' character delimits the tag and value. A particular tag
+ * may occur more than once. The character set encoding for
+ * the strings is always utf-8, but the tag names are limited
+ * to case-insensitive ascii. See the spec for details.
+ *
+ * In filling in this structure, theora_decode_header() will
+ * null-terminate the user_comment strings for safety. However,
+ * the bitstream format itself treats them as 8-bit clean,
+ * and so the length array should be treated as authoritative
+ * for their length.
+ */
+typedef struct theora_comment{
+  char **user_comments;		/**< an array of comment string vectors */
+  int   *comment_lengths;	/**< an array of corresponding string vector lengths in bytes */
+  int    comments;		/**< the total number of comment string vectors */
+  char  *vendor;		/**< the vendor string identifying the encoder, null terminated */
+
+} theora_comment;
+
+#define OC_FAULT       -1	/**< general failure */
+#define OC_EINVAL      -10	/**< library encountered invalid internal data */
+#define OC_DISABLED    -11	/**< requested action is disabled */
+#define OC_BADHEADER   -20	/**< header packet was corrupt/invalid */
+#define OC_NOTFORMAT   -21	/**< packet is not a theora packet */
+#define OC_VERSION     -22	/**< bitstream version is not handled */
+#define OC_IMPL        -23	/**< feature or action not implemented */
+#define OC_BADPACKET   -24	/**< packet is corrupt */
+#define OC_NEWPACKET   -25	/**< packet is an (ignorable) unhandled extension */
+
+/**
+ * Decode an Ogg packet, with the expectation that the packet contains
+ * an initial header, comment data or codebook tables.
+ *
+ * \param ci A theora_info structure to fill. This must have been previously
+ *           initialized with theora_info_init(). If \a op contains an initial
+ *           header, theora_decode_header() will fill \a ci with the
+ *           parsed header values. If \a op contains codebook tables,
+ *           theora_decode_header() will parse these and attach an internal
+ *           representation to \a ci->codec_setup.
+ * \param cc A theora_comment structure to fill. If \a op contains comment
+ *           data, theora_decode_header() will fill \a cc with the parsed
+ *           comments.
+ * \param op An ogg_packet structure which you expect contains an initial
+ *           header, comment data or codebook tables.
+ *
+ * \retval OC_BADHEADER \a op is NULL; OR the first byte of \a op->packet
+ *                      has the signature of an initial packet, but op is
+ *                      not a b_o_s packet; OR this packet has the signature
+ *                      of an initial header packet, but an initial header
+ *                      packet has already been seen; OR this packet has the
+ *                      signature of a comment packet, but the initial header
+ *                      has not yet been seen; OR this packet has the signature
+ *                      of a comment packet, but contains invalid data; OR
+ *                      this packet has the signature of codebook tables,
+ *                      but the initial header or comments have not yet
+ *                      been seen; OR this packet has the signature of codebook
+ *                      tables, but contains invalid data;
+ *                      OR the stream being decoded has a compatible version
+ *                      but this packet does not have the signature of a
+ *                      theora initial header, comments, or codebook packet
+ * \retval OC_VERSION   The packet data of \a op is an initial header with
+ *                      a version which is incompatible with this version of
+ *                      libtheora.
+ * \retval OC_NEWPACKET the stream being decoded has an incompatible (future)
+ *                      version and contains an unknown signature.
+ * \retval 0            Success
+ *
+ * \note The normal usage is that theora_decode_header() be called on the
+ *       first three packets of a theora logical bitstream in succession.
+ */
+extern int theora_decode_header(theora_info *ci, theora_comment *cc,
+                                ogg_packet *op);
+



More information about the commits mailing list