diff options
author | Wim Taymans <wim.taymans@collabora.co.uk> | 2010-03-02 11:16:39 +0100 |
---|---|---|
committer | Wim Taymans <wim.taymans@collabora.co.uk> | 2010-05-04 12:04:19 +0200 |
commit | 838067c9640ba46dcd26e691e472b1b1bb1e6a95 (patch) | |
tree | 0ce038f5985cb5241d27ef8203b6f18f62bd95c9 | |
parent | 52c0a1d815214885e4bc55284726a179731df3b5 (diff) |
oggdemux: more index parsing workogg-index
-rw-r--r-- | ext/ogg/gstoggdemux.c | 93 | ||||
-rw-r--r-- | ext/ogg/gstoggstream.c | 131 | ||||
-rw-r--r-- | ext/ogg/gstoggstream.h | 23 |
3 files changed, 226 insertions, 21 deletions
diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index 8cc6ffca9..0646f9d64 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -180,6 +180,9 @@ gst_ogg_pad_dispose (GObject * object) g_list_free (pad->map.queued); pad->map.queued = NULL; + g_free (pad->map.index); + pad->map.index = NULL; + /* clear continued pages */ g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL); g_list_free (pad->continued); @@ -701,20 +704,35 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet) if (pad->map.is_skeleton) { guint32 serialno; - GstOggPad *fisbone_pad; + GstOggPad *skel_pad; + GstOggSkeleton type; /* try to parse the serialno first */ if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes, - &serialno)) { - fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno); - if (fisbone_pad) { - /* parse the remainder of the fisbone in the pad with the serialno */ - gst_ogg_map_add_fisbone (&fisbone_pad->map, packet->packet, - packet->bytes, &fisbone_pad->start_time); + &serialno, &type)) { + + GST_WARNING_OBJECT (pad->ogg, + "got skeleton packet for stream %08lx", serialno); + + skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno); + if (skel_pad) { + switch (type) { + case GST_OGG_SKELETON_FISBONE: + /* parse the remainder of the fisbone in the pad with the serialno */ + gst_ogg_map_add_fisbone (&skel_pad->map, packet->packet, + packet->bytes, &skel_pad->start_time); + break; + case GST_OGG_SKELETON_INDEX: + gst_ogg_map_add_index (&skel_pad->map, packet->packet, + packet->bytes); + break; + default: + break; + } + } else { GST_WARNING_OBJECT (pad->ogg, - "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT, - serialno); + "found skeleton fisbone for an unknown stream %08lx", serialno); } } } @@ -1880,6 +1898,45 @@ seek_error: } } +static gboolean +do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin, + gint64 end, gint64 begintime, gint64 endtime, gint64 target, + gint64 * p_offset, gint64 * p_timestamp) +{ + guint i; + guint64 timestamp, offset; + guint64 r_timestamp, r_offset; + gboolean result = FALSE; + + target -= begintime; + + r_offset = -1; + r_timestamp = -1; + + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); + + timestamp = target; + if (gst_ogg_map_search_index (&pad->map, TRUE, ×tamp, &offset)) { + GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT, + timestamp, offset); + + if (r_offset == -1 || offset < r_offset) { + r_offset = offset; + r_timestamp = timestamp; + } + result |= TRUE; + } + } + + if (p_timestamp) + *p_timestamp = r_timestamp; + if (p_offset) + *p_offset = r_offset; + + return result; +} + /* * do seek to time @position, return FALSE or chain and TRUE */ @@ -1892,7 +1949,7 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment, gint64 begin, end; gint64 begintime, endtime; gint64 target, keytarget; - gint64 best; + gint64 best, best_time; gint64 total; gint64 result = 0; GstFlowReturn ret; @@ -1919,6 +1976,22 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment, endtime = begintime + chain->total_time; target = position - total + begintime; + if (do_index_search (ogg, chain, begin, end, begintime, endtime, target, + &best, &best_time)) { + /* the index gave some result */ + GST_DEBUG_OBJECT (ogg, + "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT, best, + best_time); + +#if 1 + keytarget = best_time + begintime; + best += begin; + + gst_ogg_demux_seek (ogg, best); + goto done; +#endif + } + if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target, &best)) goto seek_error; diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index e2dac40c7..8f8df2dea 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -725,24 +725,34 @@ setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet) gboolean gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size, - guint32 * serialno) + guint32 * serialno, GstOggSkeleton * type) { + GstOggSkeleton stype; + guint serial_offset; + if (size < SKELETON_FISBONE_MIN_SIZE) { GST_WARNING ("small fisbone packet of size %d, ignoring", size); return FALSE; } - if (memcmp (data, "fisbone\0", 8) != 0) { - GST_WARNING ("unknown skeleton packet %10.10s", data); - return FALSE; - } - if (pad->have_fisbone) { - GST_DEBUG ("already have fisbone, ignoring second one"); + if (memcmp (data, "fisbone\0", 8) == 0) { + GST_INFO ("got fisbone packet"); + stype = GST_OGG_SKELETON_FISBONE; + serial_offset = 12; + } else if (memcmp (data, "index\0", 6) == 0) { + GST_INFO ("got index packet"); + stype = GST_OGG_SKELETON_INDEX; + serial_offset = 6; + } else { + GST_WARNING ("unknown skeleton packet %10.10s", data); return FALSE; } if (serialno) - *serialno = GST_READ_UINT32_LE (data + 12); + *serialno = GST_READ_UINT32_LE (data + serial_offset); + + if (type) + *type = stype; return TRUE; } @@ -754,6 +764,10 @@ gst_ogg_map_add_fisbone (GstOggStream * pad, GstClockTime start_time; gint64 start_granule; + if (pad->have_fisbone) { + GST_DEBUG ("already have fisbone, ignoring second one"); + return FALSE; + } /* skip "fisbone\0" + headers offset + serialno + num headers */ data += 8 + 4 + 4 + 4; @@ -783,6 +797,107 @@ gst_ogg_map_add_fisbone (GstOggStream * pad, return TRUE; } +static guint64 +read_vlc (const guint8 ** data, guint * size) +{ + gint shift = 0; + guint64 result = 0; + gint64 byte; + + do { + byte = **data; + result |= ((byte & 0x7f) << shift); + shift += 7; + (*data)++; + } while ((byte & 0x80) != 0x80); + + return result; +} + +gboolean +gst_ogg_map_add_index (GstOggStream * pad, const guint8 * data, guint size) +{ + guint64 n_keypoints; + guint i; + guint64 offset, timestamp; + + /* skip "index\0" + serialno */ + data += 6 + 4; + size -= 6 + 4; + + if (pad->index) { + GST_DEBUG ("already have index, ignoring second one"); + return TRUE; + } + + n_keypoints = GST_READ_UINT64_LE (data); + pad->kp_denom = GST_READ_UINT64_LE (data + 8); + data += 16; + size -= 16; + + GST_INFO ("skeleton index has %" G_GUINT64_FORMAT " keypoints, denom: %" + G_GINT64_FORMAT, n_keypoints, pad->kp_denom); + + pad->index = g_try_new (GstOggIndex, n_keypoints); + if (!pad->index) + return FALSE; + + pad->n_index = n_keypoints; + + offset = 0; + timestamp = 0; + + for (i = 0; i < n_keypoints; i++) { + offset += read_vlc (&data, &size); + timestamp += read_vlc (&data, &size); + + pad->index[i].offset = offset; + pad->index[i].timestamp = timestamp; + + GST_INFO ("offset %" G_GUINT64_FORMAT " time %" G_GUINT64_FORMAT, offset, + timestamp); + } + + return TRUE; +} + +gboolean +gst_ogg_map_search_index (GstOggStream * pad, gboolean before, + guint64 * timestamp, guint64 * offset) +{ + guint64 n_index; + guint i, best; + guint64 ts; + + n_index = pad->n_index; + if (n_index == 0 || pad->index == NULL) + return FALSE; + + ts = gst_util_uint64_scale (*timestamp, pad->kp_denom, GST_SECOND); + GST_INFO ("timestamp %" G_GUINT64_FORMAT, ts); + + best = -1; + for (i = 0; i < n_index; i++) { + if (pad->index[i].timestamp <= ts) + best = i; + else if (pad->index[i].timestamp > ts) + break; + } + if (best == -1) + return FALSE; + + GST_INFO ("found at index %u", best); + + if (offset) + *offset = pad->index[best].offset; + if (timestamp) + *timestamp = + gst_util_uint64_scale (pad->index[best].timestamp, GST_SECOND, + pad->kp_denom); + + return TRUE; +} + /* Do we need these for something? * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]); * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]); diff --git a/ext/ogg/gstoggstream.h b/ext/ogg/gstoggstream.h index 40ca621f9..6bcd89391 100644 --- a/ext/ogg/gstoggstream.h +++ b/ext/ogg/gstoggstream.h @@ -28,6 +28,16 @@ G_BEGIN_DECLS +typedef enum { + GST_OGG_SKELETON_FISBONE, + GST_OGG_SKELETON_INDEX, +} GstOggSkeleton; + +typedef struct { + guint64 offset; + guint64 timestamp; +} GstOggIndex; + typedef struct _GstOggStream GstOggStream; struct _GstOggStream @@ -57,7 +67,7 @@ struct _GstOggStream gint bitrate; GstCaps *caps; - + /* vorbis stuff */ int nln_increments[4]; int nsn_increment; @@ -74,7 +84,10 @@ struct _GstOggStream /* fishead stuff */ gint64 prestime; gint64 basetime; - + /* index */ + guint n_index; + GstOggIndex *index; + guint64 kp_denom; }; @@ -95,9 +108,13 @@ gboolean gst_ogg_stream_packet_is_header (GstOggStream *pad, ogg_packet *packet) gint64 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet *packet); gboolean gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size, - guint32 * serialno); + guint32 * serialno, GstOggSkeleton *type); gboolean gst_ogg_map_add_fisbone (GstOggStream * pad, const guint8 * data, guint size, GstClockTime * p_start_time); +gboolean gst_ogg_map_add_index (GstOggStream * pad, const guint8 * data, guint size); +gboolean gst_ogg_map_search_index (GstOggStream * pad, gboolean before, guint64 *timestamp, guint64 *offset); + + G_END_DECLS |