[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