summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@collabora.co.uk>2010-03-02 11:16:39 +0100
committerWim Taymans <wim.taymans@collabora.co.uk>2010-05-04 12:04:19 +0200
commit838067c9640ba46dcd26e691e472b1b1bb1e6a95 (patch)
tree0ce038f5985cb5241d27ef8203b6f18f62bd95c9
parent52c0a1d815214885e4bc55284726a179731df3b5 (diff)
oggdemux: more index parsing workogg-index
-rw-r--r--ext/ogg/gstoggdemux.c93
-rw-r--r--ext/ogg/gstoggstream.c131
-rw-r--r--ext/ogg/gstoggstream.h23
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, &timestamp, &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