oggdemux: more index parsing work

This commit is contained in:
Wim Taymans 2010-03-02 11:16:39 +01:00
parent 97319a6276
commit ff4479f00a
3 changed files with 226 additions and 21 deletions

View file

@ -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) {
&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 (&fisbone_pad->map, packet->packet,
packet->bytes, &fisbone_pad->start_time);
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;

View file

@ -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) {
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 (pad->have_fisbone) {
GST_DEBUG ("already have fisbone, ignoring second one");
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]);

View file

@ -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
@ -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