diff --git a/ChangeLog b/ChangeLog index 67ed0a8dc2..487ae0ea82 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2007-04-13 Jan Schmidt + + * docs/libs/gstreamer-libs-sections.txt: + * libs/gst/base/gstbasesrc.c: (gst_base_src_class_init), + (gst_base_src_default_prepare_seek_segment), + (gst_base_src_prepare_seek_segment), (gst_base_src_perform_seek): + * libs/gst/base/gstbasesrc.h: + Allow basesrc derived classes to execute seeks in other formats + by providing a prepare_seek_segment vmethod. Sub-classes can choose + to prepare the GstSegment in any format that their perform_seek method + will be able to understand. The default implementation provides the + old behaviour of attempting to convert the seek offsets to the + configured native format. + 2007-04-13 Jan Schmidt * gst/gstelement.c: (gst_element_get_state_func): diff --git a/docs/libs/gstreamer-libs-sections.txt b/docs/libs/gstreamer-libs-sections.txt index 720a20fef1..47f1bcbcb1 100644 --- a/docs/libs/gstreamer-libs-sections.txt +++ b/docs/libs/gstreamer-libs-sections.txt @@ -154,6 +154,7 @@ gst_base_src_wait_playing gst_base_src_is_live gst_base_src_set_live gst_base_src_set_format +gst_base_src_send_new_segment GST_BASE_SRC_PAD diff --git a/libs/gst/base/gstbasesrc.c b/libs/gst/base/gstbasesrc.c index e815a9bfdb..6da492e60b 100644 --- a/libs/gst/base/gstbasesrc.c +++ b/libs/gst/base/gstbasesrc.c @@ -288,6 +288,8 @@ static gboolean gst_base_src_default_negotiate (GstBaseSrc * basesrc); static gboolean gst_base_src_default_do_seek (GstBaseSrc * src, GstSegment * segment); static gboolean gst_base_src_default_query (GstBaseSrc * src, GstQuery * query); +static gboolean gst_base_src_default_prepare_seek_segment (GstBaseSrc * src, + GstEvent * event, GstSegment * segment); static gboolean gst_base_src_unlock (GstBaseSrc * basesrc); static gboolean gst_base_src_unlock_stop (GstBaseSrc * basesrc); @@ -353,6 +355,8 @@ gst_base_src_class_init (GstBaseSrcClass * klass) klass->query = GST_DEBUG_FUNCPTR (gst_base_src_default_query); klass->check_get_range = GST_DEBUG_FUNCPTR (gst_base_src_default_check_get_range); + klass->prepare_seek_segment = + GST_DEBUG_FUNCPTR (gst_base_src_default_prepare_seek_segment); } static void @@ -808,6 +812,84 @@ gst_base_src_do_seek (GstBaseSrc * src, GstSegment * segment) return result; } +#define SEEK_TYPE_IS_RELATIVE(t) (((t) != GST_SEEK_TYPE_NONE) && ((t) != GST_SEEK_TYPE_SET)) + +static gboolean +gst_base_src_default_prepare_seek_segment (GstBaseSrc * src, GstEvent * event, + GstSegment * segment) +{ + /* By default, we try one of 2 things: + * - For absolute seek positions, convert the requested position to our + * configured processing format and place it in the output segment \ + * - For relative seek positions, convert our current (input) values to the + * seek format, adjust by the relative seek offset and then convert back to + * the processing format + */ + GstSeekType cur_type, stop_type; + gint64 cur, stop; + GstSeekFlags flags; + GstFormat seek_format, dest_format; + gdouble rate; + gboolean update; + gboolean res = TRUE; + + gst_event_parse_seek (event, &rate, &seek_format, &flags, + &cur_type, &cur, &stop_type, &stop); + dest_format = segment->format; + + if (seek_format == dest_format) { + gst_segment_set_seek (segment, rate, seek_format, flags, + cur_type, cur, stop_type, stop, &update); + return TRUE; + } + + if (cur_type != GST_SEEK_TYPE_NONE) { + /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */ + res = + gst_pad_query_convert (src->srcpad, seek_format, cur, &dest_format, + &cur); + cur_type = GST_SEEK_TYPE_SET; + } + + if (res && stop_type != GST_SEEK_TYPE_NONE) { + /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */ + res = + gst_pad_query_convert (src->srcpad, seek_format, stop, &dest_format, + &stop); + stop_type = GST_SEEK_TYPE_SET; + } + + /* And finally, configure our output segment in the desired format */ + gst_segment_set_seek (segment, rate, dest_format, flags, cur_type, cur, + stop_type, stop, &update); + + if (!res) + goto no_format; + + return res; + +no_format: + { + GST_DEBUG_OBJECT (src, "undefined format given, seek aborted."); + return FALSE; + } +} + +static gboolean +gst_base_src_prepare_seek_segment (GstBaseSrc * src, GstEvent * event, + GstSegment * seeksegment) +{ + GstBaseSrcClass *bclass; + gboolean result = FALSE; + + bclass = GST_BASE_SRC_GET_CLASS (src); + + if (bclass->prepare_seek_segment) + result = bclass->prepare_seek_segment (src, event, seeksegment); + + return result; +} + /* this code implements the seeking. It is a good example * handling all cases. * @@ -861,43 +943,45 @@ gst_base_src_do_seek (GstBaseSrc * src, GstSegment * segment) static gboolean gst_base_src_perform_seek (GstBaseSrc * src, GstEvent * event, gboolean unlock) { - gboolean res; + gboolean res = TRUE; gdouble rate; - GstFormat format; + GstFormat seek_format, dest_format; GstSeekFlags flags; GstSeekType cur_type, stop_type; gint64 cur, stop; gboolean flush; gboolean update; + gboolean relative_seek = FALSE; + gboolean seekseg_configured = FALSE; GstSegment seeksegment; GST_DEBUG_OBJECT (src, "doing seek"); + dest_format = src->segment.format; + if (event) { - gst_event_parse_seek (event, &rate, &format, &flags, + gst_event_parse_seek (event, &rate, &seek_format, &flags, &cur_type, &cur, &stop_type, &stop); - /* we have to have a format as the segment format. Try to convert - * if not. */ - if (src->segment.format != format) { - GstFormat fmt; + relative_seek = SEEK_TYPE_IS_RELATIVE (cur_type) || + SEEK_TYPE_IS_RELATIVE (stop_type); - fmt = src->segment.format; - res = TRUE; - if (cur_type != GST_SEEK_TYPE_NONE) - res = gst_pad_query_convert (src->srcpad, format, cur, &fmt, &cur); - if (res && stop_type != GST_SEEK_TYPE_NONE) - res = gst_pad_query_convert (src->srcpad, format, stop, &fmt, &stop); - if (!res) - goto no_format; + if (dest_format != seek_format && !relative_seek) { + /* If we have an ABSOLUTE position (SEEK_SET only), we can convert it + * here before taking the stream lock, otherwise we must convert it later, + * once we have the stream lock and can read the current position */ + gst_segment_init (&seeksegment, dest_format); - format = fmt; + if (!gst_base_src_prepare_seek_segment (src, event, &seeksegment)) + goto prepare_failed; + + seekseg_configured = TRUE; } - } else { - flags = 0; - } - flush = flags & GST_SEEK_FLAG_FLUSH; + flush = flags & GST_SEEK_FLAG_FLUSH; + } else { + flush = FALSE; + } /* send flush start */ if (flush) @@ -917,22 +1001,42 @@ gst_base_src_perform_seek (GstBaseSrc * src, GstEvent * event, gboolean unlock) if (unlock) gst_base_src_unlock_stop (src); - /* make copy into temp structure, we can only update the main one - * when the subclass actually could do the seek. */ - memcpy (&seeksegment, &src->segment, sizeof (GstSegment)); + /* If we configured the seeksegment above, don't overwrite it now. Otherwise + * copy the current segment info into the temp segment that we can actually + * attempt the seek with. We only update the real segment if the seek suceeds. */ + if (!seekseg_configured) { + memcpy (&seeksegment, &src->segment, sizeof (GstSegment)); - /* now configure the seek segment */ - if (event) { - gst_segment_set_seek (&seeksegment, rate, format, flags, - cur_type, cur, stop_type, stop, &update); + /* now configure the final seek segment */ + if (event) { + if (src->segment.format != seek_format) { + /* OK, here's where we give the subclass a chance to convert the relative + * seek into an absolute one in the processing format. We set up any + * absolute seek above, before taking the stream lock. */ + if (!gst_base_src_prepare_seek_segment (src, event, &seeksegment)) { + GST_DEBUG_OBJECT (src, "Preparing the seek failed after flushing. " + "Aborting seek"); + res = FALSE; + } + } else { + /* The seek format matches our processing format, no need to ask the + * the subclass to configure the segment. */ + gst_segment_set_seek (&seeksegment, rate, seek_format, flags, + cur_type, cur, stop_type, stop, &update); + } + } + /* Else, no seek event passed, so we're just (re)starting the + current segment. */ } - GST_DEBUG_OBJECT (src, "segment configured from %" G_GINT64_FORMAT - " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT, - seeksegment.start, seeksegment.stop, seeksegment.last_stop); + if (res) { + GST_DEBUG_OBJECT (src, "segment configured from %" G_GINT64_FORMAT + " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT, + seeksegment.start, seeksegment.stop, seeksegment.last_stop); - /* do the seek, segment.last_stop contains new position. */ - res = gst_base_src_do_seek (src, &seeksegment); + /* do the seek, segment.last_stop contains the new position. */ + res = gst_base_src_do_seek (src, &seeksegment); + } /* and prepare to continue streaming */ if (flush) { @@ -954,6 +1058,14 @@ gst_base_src_perform_seek (GstBaseSrc * src, GstEvent * event, gboolean unlock) src->segment.start, src->segment.last_stop, src->segment.time); } + /* The subclass must have converted the segment to the processing format + * by now */ + if (res && seeksegment.format != dest_format) { + GST_DEBUG_OBJECT (src, "Subclass failed to prepare a seek segment " + "in the correct format. Aborting seek."); + res = FALSE; + } + /* if successfull seek, we update our real segment and push * out the new segment. */ if (res) { @@ -996,11 +1108,10 @@ gst_base_src_perform_seek (GstBaseSrc * src, GstEvent * event, gboolean unlock) return res; /* ERROR */ -no_format: - { - GST_DEBUG_OBJECT (src, "undefined format given, seek aborted."); - return FALSE; - } +prepare_failed: + GST_DEBUG_OBJECT (src, "Preparing the seek failed before flushing. " + "Aborting seek"); + return FALSE; } static const GstQueryType * diff --git a/libs/gst/base/gstbasesrc.h b/libs/gst/base/gstbasesrc.h index 0acf741332..e34a9c5776 100644 --- a/libs/gst/base/gstbasesrc.h +++ b/libs/gst/base/gstbasesrc.h @@ -137,6 +137,11 @@ struct _GstBaseSrc { * @event: Override this to implement custom event handling. * @create: Ask the subclass to create a buffer with offset and size. * @do_seek: Perform seeking on the resource to the indicated segment. + * @prepare_seek_segment: Prepare the GstSegment that will be passed to the + * do_seek vmethod for executing a seek request. Sub-classes should override + * this if they support seeking in formats other than the configured native + * format. By default, it tries to convert the seek arguments to the + * configured native format and prepare a segment in that format. * @query: Handle a requested query. * @check_get_range: Check whether the source would support pull-based * operation if it were to be opened now. This vfunc is optional, but @@ -213,8 +218,13 @@ struct _GstBaseSrcClass { /* Clear any pending unlock request, as we succeeded in unlocking */ gboolean (*unlock_stop) (GstBaseSrc *src); + /* Prepare the segment on which to perform do_seek(), converting to the + * current basesrc format. */ + gboolean (*prepare_seek_segment) (GstBaseSrc *src, GstEvent *seek, + GstSegment *segment); + /*< private >*/ - gpointer _gst_reserved[GST_PADDING_LARGE - 5]; + gpointer _gst_reserved[GST_PADDING_LARGE - 6]; }; GType gst_base_src_get_type (void);