From 8d9d369e8391f192fd266479799c8c0bc4156c00 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 21 Jul 2004 16:48:20 +0000 Subject: [PATCH] ext/: More seeking fixes, oggdemux now supports seeking to time and uses the downstream element to convert granulepos... Original commit message from CVS: * ext/ogg/gstoggdemux.c: (gst_ogg_demux_init), (gst_ogg_demux_get_formats), (gst_ogg_demux_src_query), (gst_ogg_demux_src_event), (gst_ogg_demux_src_convert), (gst_ogg_demux_handle_event), (gst_ogg_demux_seek_before), (_find_chain_get_unknown_part), (_find_streams_check), (gst_ogg_demux_push), (gst_ogg_pad_push): * ext/theora/theoradec.c: (theora_get_formats), (theora_dec_src_convert), (theora_dec_sink_convert), (theora_dec_src_query), (theora_dec_src_event), (theora_dec_event), (theora_dec_chain): * ext/vorbis/vorbisdec.c: (vorbis_dec_get_formats), (vorbis_dec_convert), (vorbis_dec_src_query), (vorbis_dec_src_event), (vorbis_dec_event): More seeking fixes, oggdemux now supports seeking to time and uses the downstream element to convert granulepos to time. Seeking in theora-only ogg files now works. --- ChangeLog | 19 +++++ ext/ogg/gstoggdemux.c | 155 ++++++++++++++++++++++++++++++++++++----- ext/theora/theoradec.c | 50 +++++++------ ext/vorbis/vorbisdec.c | 2 +- 4 files changed, 187 insertions(+), 39 deletions(-) diff --git a/ChangeLog b/ChangeLog index 583056e6fc..b88d8e45ed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2004-07-21 Wim Taymans + + * ext/ogg/gstoggdemux.c: (gst_ogg_demux_init), + (gst_ogg_demux_get_formats), (gst_ogg_demux_src_query), + (gst_ogg_demux_src_event), (gst_ogg_demux_src_convert), + (gst_ogg_demux_handle_event), (gst_ogg_demux_seek_before), + (_find_chain_get_unknown_part), (_find_streams_check), + (gst_ogg_demux_push), (gst_ogg_pad_push): + * ext/theora/theoradec.c: (theora_get_formats), + (theora_dec_src_convert), (theora_dec_sink_convert), + (theora_dec_src_query), (theora_dec_src_event), (theora_dec_event), + (theora_dec_chain): + * ext/vorbis/vorbisdec.c: (vorbis_dec_get_formats), + (vorbis_dec_convert), (vorbis_dec_src_query), + (vorbis_dec_src_event), (vorbis_dec_event): + More seeking fixes, oggdemux now supports seeking to time and + uses the downstream element to convert granulepos to time. + Seeking in theora-only ogg files now works. + 2004-07-21 Wim Taymans * ext/theora/theoradec.c: (gst_theora_dec_init), diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index 597c836bb1..0a726cbe2f 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -153,6 +153,7 @@ struct _GstOggDemux GstOggPad *seek_pad; gint64 seek_to; gint64 seek_skipped; + GstFormat seek_format; }; struct _GstOggDemuxClass @@ -234,6 +235,7 @@ static void gst_ogg_demux_finalize (GObject * object); static gboolean gst_ogg_demux_src_event (GstPad * pad, GstEvent * event); static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad); static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad); +static const GstFormat *gst_ogg_demux_get_formats (GstPad * pad); static gboolean gst_ogg_demux_src_query (GstPad * pad, GstQueryType type, GstFormat * format, gint64 * value); @@ -302,6 +304,8 @@ gst_ogg_demux_init (GstOggDemux * ogg) (&ogg_demux_sink_template_factory), "sink")); gst_file_pad_set_iterate_function (ogg->sinkpad, gst_ogg_demux_iterate); gst_file_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_handle_event); + gst_pad_set_formats_function (GST_PAD (ogg->sinkpad), + gst_ogg_demux_get_formats); gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD (ogg->sinkpad)); /* initalize variables */ @@ -323,6 +327,24 @@ gst_ogg_demux_finalize (GObject * object) g_array_free (ogg->chains, TRUE); } +static const GstFormat * +gst_ogg_demux_get_formats (GstPad * pad) +{ + static GstFormat src_formats[] = { + GST_FORMAT_BYTES, + GST_FORMAT_DEFAULT, /* granulepos */ + GST_FORMAT_TIME, + 0 + }; + static GstFormat sink_formats[] = { + GST_FORMAT_BYTES, + GST_FORMAT_DEFAULT, /* bytes */ + 0 + }; + + return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats); +} + static const GstEventMask * gst_ogg_demux_get_event_masks (GstPad * pad) { @@ -363,34 +385,55 @@ gst_ogg_get_pad_by_pad (GstOggDemux * ogg, GstPad * pad) return NULL; } +/* the query function on the src pad only knows about granulepos + * values but we can use the peer plugins to convert the granulepos + * (which is supposed to be the default format) to any other format + */ static gboolean gst_ogg_demux_src_query (GstPad * pad, GstQueryType type, GstFormat * format, gint64 * value) { gboolean res = FALSE; - GstOggDemux *ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); - GstOggPad *cur = gst_ogg_get_pad_by_pad (ogg, pad); + GstOggDemux *ogg; + GstOggPad *cur; + guint64 granulepos; + ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); + + cur = gst_ogg_get_pad_by_pad (ogg, pad); if (!cur) return FALSE; switch (type) { case GST_QUERY_TOTAL:{ - if (*format == GST_FORMAT_DEFAULT) { - *value = cur->length; - res = TRUE; - } + granulepos = cur->length; + res = TRUE; break; } case GST_QUERY_POSITION: - if (*format == GST_FORMAT_DEFAULT && cur->length != 0) { - *value = cur->known_offset; + if (cur->length != 0) { + granulepos = cur->known_offset; res = TRUE; } break; default: break; } + if (res) { + /* still ok, got a granulepos then */ + switch (*format) { + case GST_FORMAT_DEFAULT: + /* fine, result should be granulepos */ + *value = granulepos; + res = TRUE; + break; + default: + /* something we have to ask our peer */ + res = gst_pad_convert (GST_PAD_PEER (pad), + GST_FORMAT_DEFAULT, granulepos, format, value); + break; + } + } return res; } @@ -399,6 +442,12 @@ gst_ogg_demux_src_query (GstPad * pad, GstQueryType type, * - when seeking backwards, seek to beginning and seek forward from there * Anyone is free to improve this algorithm as it is quite stupid and probably * really slow. + * + * The seeking position can be specified as the granulepos in case a decoder + * plugin can give us a correct granulepos, or in timestamps. + * In the case of a time seek, we repeadedly ask the peer element to + * convert the granulepos in the page to a timestamp. We go back to playing + * when the timestamp is the requested one (or close enough to it). */ static gboolean gst_ogg_demux_src_event (GstPad * pad, GstEvent * event) @@ -408,6 +457,7 @@ gst_ogg_demux_src_event (GstPad * pad, GstEvent * event) ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); cur = gst_ogg_get_pad_by_pad (ogg, pad); + /* FIXME: optimize this so events from inactive chains work? * in theory there shouldn't be an exisiting pad for inactive chains */ if (cur == NULL) @@ -416,31 +466,62 @@ gst_ogg_demux_src_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { - gint64 offset; + gint64 offset, position; + GstFormat format, my_format; + gboolean res; - if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_DEFAULT) - goto error; + format = GST_EVENT_SEEK_FORMAT (event); offset = GST_EVENT_SEEK_OFFSET (event); + + my_format = format; + + /* get position, we'll need it later to decide what direction + * we need to seek in */ + res = gst_ogg_demux_src_query (pad, + GST_QUERY_POSITION, &my_format, &position); + if (!res) + goto error; + switch (GST_EVENT_SEEK_METHOD (event)) { case GST_SEEK_METHOD_END: - if (cur->length == 0 || offset > 0) + { + gint64 value; + + /* invalid offset */ + if (offset > 0) goto error; - offset = cur->length + offset; + + /* calculate total length first */ + res = gst_ogg_demux_src_query (pad, + GST_QUERY_TOTAL, &my_format, &value); + if (!res) + goto error; + + /* requested position is end + offset */ + offset = value + offset; break; + } case GST_SEEK_METHOD_CUR: - offset += cur->known_offset; + { + /* add current position to offset */ + offset = position + offset; break; + } case GST_SEEK_METHOD_SET: + /* offset and format are fine here */ break; default: g_warning ("invalid seek method in seek event"); goto error; } - if (offset < cur->known_offset) { + + if (offset < position) { + /* seek backwards, move to beginning of file */ if (gst_file_pad_seek (ogg->sinkpad, 0, GST_SEEK_METHOD_SET) != 0) goto error; ogg_sync_clear (&ogg->sync); } else { + /* seek forwards flush and skip */ FOR_PAD_IN_CURRENT_CHAIN (ogg, pad, if (GST_PAD_IS_USABLE (pad->pad)) gst_pad_push (pad->pad, GST_DATA (gst_event_new (GST_EVENT_FLUSH)));); @@ -449,10 +530,15 @@ gst_ogg_demux_src_event (GstPad * pad, GstEvent * event) GST_OGG_SET_STATE (ogg, GST_OGG_STATE_SEEK); FOR_PAD_IN_CURRENT_CHAIN (ogg, pad, pad->flags |= GST_OGG_PAD_NEEDS_DISCONT;); - GST_DEBUG_OBJECT (ogg, "initiating seeking to offset %" G_GUINT64_FORMAT, + GST_DEBUG_OBJECT (ogg, + "initiating seeking to format %d, offset %" G_GUINT64_FORMAT, format, offset); + + /* store format and position we seek to */ ogg->seek_pad = cur; ogg->seek_to = offset; + ogg->seek_format = format; + gst_event_unref (event); return TRUE; } @@ -467,6 +553,23 @@ error: return FALSE; } +static gboolean +gst_ogg_demux_src_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = FALSE; + GstOggDemux *ogg; + GstOggPad *cur; + + ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); + cur = gst_ogg_get_pad_by_pad (ogg, pad); + + /* fill me, not sure with what... */ + + return res; +} + static void gst_ogg_start_playing (GstOggDemux * ogg) { @@ -1164,7 +1267,20 @@ gst_ogg_demux_push (GstOggDemux * ogg, ogg_page * page) " (pad %d) - desired offset %" G_GUINT64_FORMAT " (pad %d)", cur->known_offset, cur->serial, ogg->seek_to, ogg->seek_pad->serial); if (cur == ogg->seek_pad) { - if (ogg_page_granulepos (page) > ogg->seek_to) { + gint64 position; + + position = ogg_page_granulepos (page); + + /* see if we reached the destination position when seeking */ + if (ogg->seek_format != GST_FORMAT_DEFAULT) { + if (!gst_pad_convert (GST_PAD_PEER (cur->pad), + GST_FORMAT_DEFAULT, position, &ogg->seek_format, &position)) { + /* let's just stop then */ + position = G_MAXINT64; + } + } + + if (position >= ogg->seek_to) { GST_OGG_SET_STATE (ogg, GST_OGG_STATE_PLAY); GST_DEBUG_OBJECT (ogg, "ended seek at offset %" G_GUINT64_FORMAT " (requested %" @@ -1234,6 +1350,11 @@ gst_ogg_pad_push (GstOggDemux * ogg, GstOggPad * pad) GST_DEBUG_FUNCPTR (gst_ogg_demux_src_query)); gst_pad_set_query_type_function (pad->pad, GST_DEBUG_FUNCPTR (gst_ogg_demux_get_query_types)); + gst_pad_set_formats_function (pad->pad, + GST_DEBUG_FUNCPTR (gst_ogg_demux_get_formats)); + gst_pad_set_convert_function (pad->pad, + GST_DEBUG_FUNCPTR (gst_ogg_demux_src_convert)); + gst_pad_use_explicit_caps (pad->pad); gst_pad_set_explicit_caps (pad->pad, caps); gst_pad_set_active (pad->pad, TRUE); diff --git a/ext/theora/theoradec.c b/ext/theora/theoradec.c index 6c35c2b743..91de86a046 100644 --- a/ext/theora/theoradec.c +++ b/ext/theora/theoradec.c @@ -243,10 +243,11 @@ theora_dec_src_convert (GstPad * pad, case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_BYTES: - scale = 4 * (dec->info.width * dec->info.height) / 3; + scale = 3 * (dec->info.width * dec->info.height) / 2; case GST_FORMAT_DEFAULT: - *dest_value = src_value * scale * dec->info.fps_numerator / - (dec->info.fps_denominator * GST_SECOND); + *dest_value = + scale * (((guint64) src_value * dec->info.fps_numerator) / + (dec->info.fps_denominator * GST_SECOND)); break; default: res = FALSE; @@ -339,10 +340,10 @@ theora_dec_src_query (GstPad * pad, GstQueryType query, GstFormat * format, /* and convert to the final format in two steps with time as the * intermediate step */ my_format = GST_FORMAT_TIME; - if (!theora_dec_sink_convert (dec->sinkpad, granulepos, GST_FORMAT_DEFAULT, + if (!theora_dec_sink_convert (dec->sinkpad, GST_FORMAT_DEFAULT, granulepos, &my_format, &time)) return FALSE; - if (!theora_dec_src_convert (pad, my_format, time, format, value)) + if (!gst_pad_convert (pad, my_format, time, format, value)) return FALSE; GST_LOG_OBJECT (dec, @@ -364,24 +365,31 @@ theora_dec_src_event (GstPad * pad, GstEvent * event) case GST_EVENT_SEEK:{ guint64 value; - /* convert to granulepos, this will fail as we cannot generate - * a granulepos, only convert an existing one to something else, - * which basically means that seeking doesn't work this way */ - format = GST_FORMAT_DEFAULT; - res = theora_dec_sink_convert (pad, GST_EVENT_SEEK_FORMAT (event), + /* we have to ask our peer to seek to time here as we know + * nothing about how to generate a granulepos from the src + * formats or anything. + * + * First bring the requested format to time + */ + format = GST_FORMAT_TIME; + res = gst_pad_convert (pad, GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event), &format, &value); - if (res) { - GstEvent *real_seek = gst_event_new_seek ( - (GST_EVENT_SEEK_TYPE (event) & ~GST_SEEK_FORMAT_MASK) | - GST_FORMAT_DEFAULT, - value); + if (!res) + goto error; - res = gst_pad_send_event (GST_PAD_PEER (dec->sinkpad), real_seek); - if (res) { - /* sync to keyframe */ - dec->need_keyframe = TRUE; - } - } + /* then seek with time on the peer */ + GstEvent *real_seek = gst_event_new_seek ( + (GST_EVENT_SEEK_TYPE (event) & ~GST_SEEK_FORMAT_MASK) | + format, value); + + res = gst_pad_send_event (GST_PAD_PEER (dec->sinkpad), real_seek); + if (!res) + goto error; + + /* all worked, make sure we sync to keyframe */ + dec->need_keyframe = TRUE; + + error: gst_event_unref (event); break; } diff --git a/ext/vorbis/vorbisdec.c b/ext/vorbis/vorbisdec.c index d7001a2b32..5724e06b74 100644 --- a/ext/vorbis/vorbisdec.c +++ b/ext/vorbis/vorbisdec.c @@ -246,7 +246,7 @@ vorbis_dec_src_query (GstPad * pad, GstQueryType query, GstFormat * format, } /* and convert to the final format */ - if (!vorbis_dec_convert (pad, GST_FORMAT_DEFAULT, granulepos, format, value)) + if (!gst_pad_convert (pad, GST_FORMAT_DEFAULT, granulepos, format, value)) return FALSE; GST_LOG_OBJECT (dec,