[cvs-annodex] commit (/annodex):
libannodex/trunk/src/importers/anx_import_ogg.c
conrad
nobody at lists.annodex.net
Fri Feb 11 04:01:20 EST 2005
Update of /annodex (new revision 874)
Modified files:
libannodex/trunk/src/importers/anx_import_ogg.c
Log Message:
add annodex v3 and theora keyframe seeking:
+ scan imported ogg files for granuleshift info
+ build a table of desired keyframes for all tracks
+ deliver the correct recomposition, dropping uneeded intermediate
packets from other tracks
Tested with single and multi-file ogg, anx and cmml sources.
Modified: libannodex/trunk/src/importers/anx_import_ogg.c
===================================================================
--- libannodex/trunk/src/importers/anx_import_ogg.c 2005-02-10 16:50:03 UTC (rev 873)
+++ libannodex/trunk/src/importers/anx_import_ogg.c 2005-02-10 17:01:10 UTC (rev 874)
@@ -39,6 +39,7 @@
#include <string.h>
/*#define DEBUG*/
+/*#define DEBUG_VERBOSE*/
#define MIN(a,b) ((a)<(b)?(a):(b))
@@ -67,39 +68,59 @@
#define SUBSECONDS 1000.0
typedef enum {
+ STATE_HEADERS,
+ STATE_GRANULEINFO,
+ STATE_FILTER,
+ STATE_DATA
+} state_t;
+
+typedef enum {
NEED_SEEK_PENDING = 0,
NEED_SEEK = 1,
NEED_SEEK_DONE = 2
} need_seek_t;
-typedef struct _AnxOggMedia AnxOggMedia;
+typedef struct _AnxOggPacket AnxOggPacket;
+typedef struct _AnxOggTrack AnxOggTrack;
typedef struct _AnxOggData AnxOggData;
-struct _AnxOggMedia {
+struct _AnxOggPacket {
long length;
unsigned char * data;
long granulepos;
- AnxSourceTrack * media_track;
+ AnxSourceTrack * source_track;
double current_time;
int eos;
};
+struct _AnxOggTrack {
+ AnxSourceTrack source_track;
+ int need_keygranule;
+ ogg_int64_t keygranule;
+ double keygranule_time;
+};
+
struct _AnxOggData {
OGGZ * oggz;
- AnxSource * anx_media;
+ AnxSource * anx_source;
+ state_t state;
+
char * id;
int ignore_media;
-
int got_non_bos;
long nr_headers_remaining;
long headers_unread;
+ int use_granule_seek;
+ double min_granule_seek;
+
need_seek_t need_seek;
int got_end;
+ /* Maintain a list of AnxOggTracks */
OggzTable * logicals;
AnxList * media_packets;
@@ -168,13 +189,38 @@
}
static double
-anxogg_seek_update (AnxSource * media)
+gp_to_time (OGGZ * oggz, long serialno, ogg_int64_t granulepos)
{
- AnxOggData * aod = (AnxOggData *)media->custom_data;
+ int granuleshift;
+ ogg_int64_t iframe, pframe;
+ ogg_int64_t gr_n, gr_d;
+
+ granuleshift = oggz_get_granuleshift (oggz, serialno);
+
+ if (oggz_get_granulerate (oggz, serialno, &gr_n, &gr_d) != 0) return -1.0;
+
+ iframe = granulepos >> granuleshift;
+ pframe = granulepos - (iframe << granuleshift);
+ return (double)((iframe + pframe) * gr_d) / ((double)gr_n * SUBSECONDS);
+}
+
+static double
+anxogg_seek_update (AnxSource * source)
+{
+ AnxOggData * aod = (AnxOggData *)source->custom_data;
+ double seek_offset;
ogg_int64_t units, units_at;
double offset;
- units = (ogg_int64_t)(SUBSECONDS * media->start_time);
+ if (aod->use_granule_seek) {
+ seek_offset = aod->min_granule_seek;
+ } else {
+ seek_offset = source->start_time;
+ }
+ seek_offset -= 1.0;
+ if (seek_offset < 0.0) seek_offset = 0.0;
+
+ units = (ogg_int64_t)(SUBSECONDS * seek_offset);
units_at = oggz_seek_units (aod->oggz, units, SEEK_SET);
if (units_at == -1) {
@@ -190,10 +236,10 @@
#ifdef DEBUG
printf ("anxogg: seek_update to %lld (wanted %lld (%f))\n", units_at,
- units, media->start_time);
+ units, seek_offset);
#endif
- if (media->end_time != -1.0 && offset >= media->end_time) {
+ if (source->end_time != -1.0 && offset >= source->end_time) {
aod->got_end = 1;
}
@@ -201,15 +247,18 @@
}
static int
-read_packet (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
+read_packet_headers (OGGZ * oggz, ogg_packet * op, long serialno,
+ void * user_data)
{
unsigned char * header = op->packet;
AnxOggData * aod = (AnxOggData *)user_data;
- AnxSource * m = aod->anx_media;
+ AnxOggTrack * aot = NULL;
+ AnxOggPacket * aop;
+ AnxSource * m = aod->anx_source;
AnxSourceTrack * track = NULL;
- AnxOggMedia * aam;
if (op->b_o_s) {
+
if (!strncmp ((char *)header, "CMML", 5)) {
ogg_int64_t gr_n, gr_d;
@@ -219,8 +268,10 @@
gr_d = INT64_LE_AT(&header[20]);
aod->cmml_granulerate = gr_n / gr_d;
} else {
- track = (AnxSourceTrack *) anx_malloc (sizeof (AnxSourceTrack));
- memset (track, 0, sizeof(AnxSourceTrack));
+ aot = (AnxOggTrack *) anx_malloc (sizeof (AnxOggTrack));
+ memset (aot, 0, sizeof(AnxOggTrack));
+
+ track = &(aot->source_track);
track->eos = 0;
if (!strncmp ((char *)&header[1], "vorbis", 6)) {
@@ -279,10 +330,15 @@
aod->nr_headers_remaining += track->nr_header_packets;
aod->headers_unread += track->nr_header_packets;
+ if (track->granuleshift > 0) {
+ aod->use_granule_seek = 1;
+ aot->need_keygranule = 1;
+ }
+
oggz_table_insert (aod->logicals, serialno, track);
#ifdef DEBUG
- printf ("anxogg::read_packet: Added track for (%ld): %ld/%ld, +%d headers\n", serialno,
+ printf ("anxogg::read_packet: Added track for (%010ld): %ld/%ld, +%d headers\n", serialno,
(long)track->granule_rate_n, (long)track->granule_rate_d,
(int)track->nr_header_packets);
#endif
@@ -310,14 +366,84 @@
m->tracks = anx_list_append (m->tracks, track);
}
} else {
+ aod->got_non_bos = 1;
+ }
+
+ if (aod->nr_headers_remaining > 0) {
+ aod->nr_headers_remaining--;
+ }
+
+ if (aod->got_non_bos && aod->nr_headers_remaining == 0) {
+ if (m->start_time != 0.0) {
+ if (oggz_seek_units (oggz, 0, SEEK_CUR) >= 0) {
+ oggz_set_data_start (oggz, oggz_tell (oggz));
+ if (aod->use_granule_seek) aod->state = STATE_GRANULEINFO;
+ }
+ } else {
+ aod->state = STATE_DATA;
+ }
+ }
+
+ return OGGZ_STOP_OK;
+}
+
+static int
+filter (AnxOggData * aod, AnxOggTrack * aot, long serialno,
+ ogg_int64_t granulepos)
+{
+ AnxSourceTrack * track = &(aot->source_track);
+ double timestamp;
+
+ /* TRUE if we're not in state FILTER */
+ if (aod->state != STATE_FILTER) return 1;
+
+ /* TRUE if we haven't done the seek yet */
+ if (aod->need_seek == NEED_SEEK_PENDING) return 1;
+
+ timestamp =
+ gp_to_time (aod->oggz, serialno, granulepos);
+
+ /* TRUE if we're beyond the start time */
+ if (timestamp >= aod->anx_source->start_time) {
+ aod->state = STATE_DATA;
+ return 1;
+ }
+
+ /* from here, we know we're filtering */
+
+ /* FALSE if we're not using granule_seek at all */
+ if (!aod->use_granule_seek) return 0;
+
+ /* FALSE if this track doesn't use granuleshift */
+ if (track->granuleshift == 0) return 0;
+
+ /* FALSE if this track uses keygranules, and we're before its time */
+ if (granulepos != -1 && timestamp < aot->keygranule_time) return 0;
+
+ /* TRUE otherwise */
+ return 1;
+}
+
+static int
+read_packet_data (OGGZ * oggz, ogg_packet * op, long serialno,
+ void * user_data)
+{
+ unsigned char * header = op->packet;
+ AnxOggData * aod = (AnxOggData *)user_data;
+ AnxOggTrack * aot = NULL;
+ AnxOggPacket * aop;
+ AnxSource * m = aod->anx_source;
+ AnxSourceTrack * track = NULL;
+
+ if (!op->b_o_s) {
if (aod->cmml_serialno != -1 && serialno == aod->cmml_serialno) {
double at_time;
-
+
if (op->granulepos == -1) {
return OGGZ_STOP_ERR;
}
at_time = op->granulepos / aod->cmml_granulerate;
-
+
#ifdef DEBUG
printf ("anxogg::read_packet: got CMML <%c%c%c%c> at %f\n",
header[1], header[2], header[3], header[4], at_time);
@@ -328,10 +454,12 @@
return OGGZ_CONTINUE;
} else {
aod->got_non_bos = 1;
- track = (AnxSourceTrack *) oggz_table_lookup (aod->logicals, serialno);
}
}
+ aot = (AnxOggTrack *) oggz_table_lookup (aod->logicals, serialno);
+ track = &(aot->source_track);
+
if (!track) {
return OGGZ_STOP_ERR;
}
@@ -342,24 +470,26 @@
aod->got_end = 1;
}
- if (!aod->ignore_media && !aod->got_end) {
- aam = anx_malloc (sizeof (AnxOggMedia));
- aam->length = op->bytes;
- aam->data = anx_malloc (op->bytes);
- aam->granulepos = op->granulepos;
- aam->current_time = ((double)oggz_tell_units (oggz)) / SUBSECONDS;
- aam->media_track = track;
- aam->eos = op->e_o_s;
+ if (!aod->ignore_media && !aod->got_end &&
+ (aod->state != STATE_FILTER ||
+ filter (aod, aot, serialno, op->granulepos))) {
+ aop = anx_malloc (sizeof (AnxOggPacket));
+ aop->length = op->bytes;
+ aop->data = anx_malloc (op->bytes);
+ aop->granulepos = op->granulepos;
+ aop->current_time = ((double)oggz_tell_units (oggz)) / SUBSECONDS;
+ aop->source_track = track;
+ aop->eos = op->e_o_s;
#if 0
- if (aod->nr_headers_remaining == 0 && aam->granulepos != -1) {
- aam->granulepos -= track->start_granule;
+ if (aod->nr_headers_remaining == 0 && aop->granulepos != -1) {
+ aop->granulepos -= track->start_granule;
}
#endif
- memcpy (aam->data, op->packet, op->bytes);
+ memcpy (aop->data, op->packet, op->bytes);
- aod->media_packets = anx_list_append (aod->media_packets, aam);
+ aod->media_packets = anx_list_append (aod->media_packets, aop);
if (aod->nr_headers_remaining > 0) {
aod->nr_headers_remaining--;
@@ -377,7 +507,6 @@
#ifdef DEBUG
printf ("anxogg::read_packet NEED seek\n");
#endif
- oggz_set_data_start (oggz, oggz_tell (oggz));
aod->need_seek = NEED_SEEK;
return OGGZ_CONTINUE;
}
@@ -387,6 +516,123 @@
return OGGZ_STOP_OK;
}
+static int
+granuleinfo_update_state (AnxOggData * aod)
+{
+ AnxOggTrack * aot;
+ int i, n;
+
+ n = oggz_table_size (aod->logicals);
+
+ for (i = 0; i < n; i++) {
+ aot = (AnxOggTrack *)oggz_table_nth (aod->logicals, i, NULL);
+ if (aot->need_keygranule) return 0;
+ }
+
+ aod->state = STATE_FILTER;
+
+ return 0;
+}
+
+static int
+read_page_granuleinfo (OGGZ * oggz, const ogg_page * og, long serialno,
+ void * user_data)
+{
+ AnxOggData * aod = (AnxOggData *)user_data;
+ AnxOggTrack * aot = NULL;
+ AnxSourceTrack * track = NULL;
+ ogg_int64_t granulepos, iframe, pframe;
+ double offset;
+
+#ifdef DEBUG
+ printf ("read_page_granuleinfo: track %010ld\n", serialno);
+#endif
+
+ aot = (AnxOggTrack *) oggz_table_lookup (aod->logicals, serialno);
+ if (aot == NULL) {
+ /* If this track is not in the table, ignore it. */
+ return OGGZ_STOP_OK;
+ }
+
+ track = &(aot->source_track);
+
+ granulepos = ogg_page_granulepos ((ogg_page *)og);
+
+ /* Slurp in granuleinfo */
+ if (aot->need_keygranule && granulepos != -1) {
+ iframe = granulepos >> track->granuleshift;
+ pframe = granulepos - (iframe << track->granuleshift);
+ granulepos = (iframe + pframe);
+ aot->keygranule = iframe << track->granuleshift;
+ aot->need_keygranule = 0;
+
+ offset = gp_to_time (aod->oggz, serialno, aot->keygranule);
+ aot->keygranule_time = offset;
+ if (aod->min_granule_seek == 0.0 || offset < aod->min_granule_seek)
+ aod->min_granule_seek = offset;
+
+#ifdef DEBUG
+ printf ("read_page_granuleinfo: ^^^ has keygranule %llx (%f seconds) (granuleshift %d)\n",
+ aot->keygranule, offset, track->granuleshift);
+#endif
+ }
+
+ /* Update state */
+ granuleinfo_update_state (aod);
+
+ return OGGZ_STOP_OK;
+}
+
+static int
+anxogg_setup (AnxOggData * aod)
+{
+ long n;
+ double start_time = 0.0;
+ ogg_int64_t units, units_at;
+ double offset;
+
+ /* Slurp headers */
+ oggz_set_read_callback (aod->oggz, -1, read_packet_headers, aod);
+ while (aod->state == STATE_HEADERS &&
+ (n = oggz_read (aod->oggz, 1024)) != 0);
+
+#ifdef DEBUG
+ printf ("anxogg: slurped headers:\n"
+ "\toggz_read returned %ld\n"
+ "\taod->got_non_bos == %d\n"
+ "\taod->nr_headers_remaining = %d\n",
+ n, aod->got_non_bos, aod->nr_headers_remaining);
+#endif
+
+ /* Find granule_seek info */
+ if (aod->anx_source) start_time = aod->anx_source->start_time;
+
+ if (aod->use_granule_seek && start_time > 0.0) {
+ oggz_set_read_callback (aod->oggz, -1, NULL, NULL);
+
+ units = (ogg_int64_t)(SUBSECONDS * aod->anx_source->start_time);
+ units_at = oggz_seek_units (aod->oggz, units, SEEK_SET);
+
+ if (units_at == -1) {
+#ifdef DEBUG
+ printf ("anxogg_seek_update: oggz_seek_units FAIL\n");
+#endif
+ return -1;
+ }
+
+ oggz_set_read_page (aod->oggz, -1, read_page_granuleinfo, aod);
+ while (aod->state == STATE_GRANULEINFO &&
+ (n = oggz_read (aod->oggz, 1024)) != 0);
+ oggz_set_read_page (aod->oggz, -1, NULL, NULL);
+ }
+
+ /* Reset to beginning */
+ oggz_seek (aod->oggz, 0, SEEK_SET);
+ aod->nr_headers_remaining = aod->headers_unread;
+
+ return 0;
+}
+
static AnxSource *
anxogg_open (const char * path, const char * id, int ignore_media,
double start_time, double end_time,
@@ -420,15 +666,17 @@
return NULL;
aod->oggz = oggz;
- aod->anx_media = m;
-
+ aod->anx_source = m;
+ aod->state = STATE_HEADERS;
aod->id = (char *)id;
-
aod->ignore_media = ignore_media;
aod->media_packets = NULL;
aod->current_offset = 0;
+ aod->use_granule_seek = 0;
+ aod->min_granule_seek = 0;
+
aod->got_non_bos = 0;
aod->need_seek = NEED_SEEK_PENDING;
aod->got_end = 0;
@@ -440,22 +688,15 @@
aod->import_user_data = import_callbacks->import_user_data;
aod->cmml_serialno = -1;
+ anxogg_setup (aod);
+
+ /* Set normal data reading callback */
+ oggz_set_read_callback (oggz, -1, read_packet_data, aod);
+
if (ignore_media) {
- oggz_set_read_callback (oggz, -1, read_packet, aod);
+ /* Slurp CMML */
while ((n = oggz_read (oggz, 1024)) != 0) {
}
- } else {
- oggz_set_read_callback (oggz, -1, read_packet, aod);
- while ((!(aod->got_non_bos && aod->nr_headers_remaining == 0)) &&
- (n = oggz_read (oggz, 1024)) != 0) {
- }
-#ifdef DEBUG
- printf ("anxogg: slurped headers:\n"
- "\toggz_read returned %ld\n"
- "\taod->got_non_bos == %d\n"
- "\taod->nr_headers_remaining = %d\n",
- n, aod->got_non_bos, aod->nr_headers_remaining);
-#endif
}
m->custom_data = aod;
@@ -484,7 +725,7 @@
anxogg_read (AnxSource * media, char * buf, long n, long bound)
{
AnxOggData * aod = (AnxOggData *)media->custom_data;
- AnxOggMedia * aam;
+ AnxOggPacket * aop;
long bytes_to_read;
if (aod->ignore_media) return -1;
@@ -496,47 +737,47 @@
return 0;
}
- aam = (AnxOggMedia *)aod->media_packets->data;
- bytes_to_read = MIN (n, aam->length - aod->current_offset);
+ aop = (AnxOggPacket *)aod->media_packets->data;
+ bytes_to_read = MIN (n, aop->length - aod->current_offset);
- memcpy (buf, &aam->data[aod->current_offset], bytes_to_read);
+ memcpy (buf, &aop->data[aod->current_offset], bytes_to_read);
aod->current_offset += bytes_to_read;
if (aod->headers_unread > 0) aod->headers_unread--;
if (aod->headers_unread == 0) media->written_secondaries = 1;
- media->current_track = aam->media_track;
+ media->current_track = aop->source_track;
-#ifdef DEBUG
+#ifdef DEBUG_VERBOSE
printf ("anxogg: reading from stream %s\n",
media->current_track->content_type);
#endif
- media->current_track->current_granule = aam->granulepos;
- media->current_track->eos = aam->eos;
+ media->current_track->current_granule = aop->granulepos;
+ media->current_track->eos = aop->eos;
/* If that's finished this media packet, advance to the next one */
- if (aod->current_offset >= aam->length) {
+ if (aod->current_offset >= aop->length) {
aod->media_packets =
anx_list_remove (aod->media_packets, aod->media_packets);
- anx_free (aam->data);
- anx_free (aam);
- aam = NULL;
+ anx_free (aop->data);
+ anx_free (aop);
+ aop = NULL;
aod->current_offset = 0;
anxogg_read_update (media);
if (aod->media_packets != NULL)
- aam = (AnxOggMedia *)aod->media_packets->data;
+ aop = (AnxOggPacket *)aod->media_packets->data;
}
/* Set current time to the current time of the next packet that would
* be served */
- if (aam && aam->current_time != -1)
- media->current_time = aam->current_time;
+ if (aop && aop->current_time != -1)
+ media->current_time = aop->current_time;
return bytes_to_read;
}
@@ -545,7 +786,7 @@
anxogg_sizeof_next_read (AnxSource * media, long bound)
{
AnxOggData * aod = (AnxOggData *)media->custom_data;
- AnxOggMedia * aam;
+ AnxOggPacket * aop;
long bytes_to_read;
if (aod->ignore_media) return -1;
@@ -557,21 +798,29 @@
return 0;
}
- aam = (AnxOggMedia *)aod->media_packets->data;
- bytes_to_read = aam->length - aod->current_offset;
+ aop = (AnxOggPacket *)aod->media_packets->data;
+ bytes_to_read = aop->length - aod->current_offset;
return bytes_to_read;
}
static int
-anxogg_close (AnxSource * media)
+anxogg_close (AnxSource * source)
{
- AnxOggData * aod = (AnxOggData *)media->custom_data;
+ AnxOggData * aod = (AnxOggData *)source->custom_data;
+ AnxOggTrack * aot;
+ int i, n;
oggz_close (aod->oggz);
- anx_free (media);
+ n = oggz_table_size (aod->logicals);
+ for (i = 0; i < n; i++) {
+ aot = (AnxOggTrack *)oggz_table_nth (aod->logicals, i, NULL);
+ if (aot) free (aot);
+ }
+ anx_free (source);
+
return 0;
}
--
conrad
More information about the cvs-annodex
mailing list