From: <wt...@ke...> - 2008-10-20 15:18:34
|
CVS Root: /cvs/gstreamer Module: gstreamer Changes by: wtay Date: Mon Oct 20 2008 15:18:28 UTC Log message: * libs/gst/base/gstbasesink.c: (gst_base_sink_default_do_seek), (gst_base_sink_default_prepare_seek_segment), (gst_base_sink_perform_seek), (gst_base_sink_get_position_last), (gst_base_sink_get_position_paused), (gst_base_sink_get_position), (gst_base_sink_query): Implement more seeking in pull mode. Use pad convert functions to convert position to the requested format. Fix position/duration reporting in pull mode. Implement position and duration reporting in other formats than time. * libs/gst/base/gstbasesink.h: Add member to keep track of when the segment is playing. Modified files: . : ChangeLog libs/gst/base : gstbasesink.c gstbasesink.h Links: http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/ChangeLog.diff?r1=1.4114&r2=1.4115 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/libs/gst/base/gstbasesink.c.diff?r1=1.220&r2=1.221 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/libs/gst/base/gstbasesink.h.diff?r1=1.52&r2=1.53 ====Begin Diffs==== Index: ChangeLog =================================================================== RCS file: /cvs/gstreamer/gstreamer/ChangeLog,v retrieving revision 1.4114 retrieving revision 1.4115 diff -u -d -r1.4114 -r1.4115 --- ChangeLog 20 Oct 2008 13:32:05 -0000 1.4114 +++ ChangeLog 20 Oct 2008 15:18:11 -0000 1.4115 @@ -1,5 +1,20 @@ 2008-10-20 Wim Taymans <wim...@co...> + * libs/gst/base/gstbasesink.c: (gst_base_sink_default_do_seek), + (gst_base_sink_default_prepare_seek_segment), + (gst_base_sink_perform_seek), (gst_base_sink_get_position_last), + (gst_base_sink_get_position_paused), (gst_base_sink_get_position), + (gst_base_sink_query): + Implement more seeking in pull mode. + Use pad convert functions to convert position to the requested format. + Fix position/duration reporting in pull mode. + Implement position and duration reporting in other formats than time. + + * libs/gst/base/gstbasesink.h: + Add member to keep track of when the segment is playing. +2008-10-20 Wim Taymans <wim...@co...> * gst/gstpad.c: (gst_pad_configure_src): When we use gst_pad_alloc_buffer() without wanting to set the caps we also don't need to check if the caps are compatible because the caller Index: gstbasesink.c RCS file: /cvs/gstreamer/gstreamer/libs/gst/base/gstbasesink.c,v retrieving revision 1.220 retrieving revision 1.221 diff -u -d -r1.220 -r1.221 --- gstbasesink.c 17 Oct 2008 13:27:59 -0000 1.220 +++ gstbasesink.c 20 Oct 2008 15:18:13 -0000 1.221 @@ -216,6 +216,8 @@ GstCaps *pull_caps; guint blocksize; + gboolean discont; }; #define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size)) @@ -305,6 +307,10 @@ GstPad * pad, gboolean flushing); static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink, gboolean active); +static gboolean gst_base_sink_default_do_seek (GstBaseSink * sink, + GstSegment * segment); +static gboolean gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink, + GstEvent * event, GstSegment * segment); static GstStateChangeReturn gst_base_sink_change_state (GstElement * element, GstStateChange transition); @@ -2931,50 +2937,201 @@ } } +static gboolean +gst_base_sink_default_do_seek (GstBaseSink * sink, GstSegment * segment) +{ + gboolean res = TRUE; + /* update our offset if the start/stop position was updated */ + if (segment->format == GST_FORMAT_BYTES) { + segment->time = segment->start; + } else if (segment->start == 0) { + /* seek to start, we can implement a default for this. */ + segment->time = 0; + } else { + res = FALSE; + GST_INFO_OBJECT (sink, "Can't do a default seek"); + } + return res; +} +#define SEEK_TYPE_IS_RELATIVE(t) (((t) != GST_SEEK_TYPE_NONE) && ((t) != GST_SEEK_TYPE_SET)) +gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink, + 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; + 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 (sink->sinkpad, seek_format, cur, &dest_format, + &cur); + cur_type = GST_SEEK_TYPE_SET; + if (res && stop_type != GST_SEEK_TYPE_NONE) { + gst_pad_query_convert (sink->sinkpad, 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; +no_format: + { + GST_DEBUG_OBJECT (sink, "undefined format given, seek aborted."); + return FALSE; /* perform a seek, only executed in pull mode */ static gboolean -gst_base_sink_perform_seek (GstBaseSink * basesink, GstPad * pad, - GstEvent * event) +gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event) { gboolean flush; gdouble rate; - GstFormat seek_format; GstSeekFlags flags; GstSeekType cur_type, stop_type; + gboolean seekseg_configured = FALSE; gint64 cur, stop; + gboolean update, res = TRUE; + GstSegment seeksegment; + dest_format = sink->segment.format; if (event) { - GST_DEBUG_OBJECT (basesink, "performing seek with event %p", event); + GST_DEBUG_OBJECT (sink, "performing seek with event %p", event); gst_event_parse_seek (event, &rate, &seek_format, &flags, &cur_type, &cur, &stop_type, &stop); flush = flags & GST_SEEK_FLAG_FLUSH; } else { - GST_DEBUG_OBJECT (basesink, "performing seek without event"); + GST_DEBUG_OBJECT (sink, "performing seek without event"); flush = FALSE; if (flush) { - GST_DEBUG_OBJECT (basesink, "flushing upstream"); + GST_DEBUG_OBJECT (sink, "flushing upstream"); gst_pad_push_event (pad, gst_event_new_flush_start ()); - gst_base_sink_flush_start (basesink, pad); + gst_base_sink_flush_start (sink, pad); - GST_DEBUG_OBJECT (basesink, "pausing pulling thread"); + GST_DEBUG_OBJECT (sink, "pausing pulling thread"); GST_PAD_STREAM_LOCK (pad); + /* 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, &sink->segment, sizeof (GstSegment)); + /* now configure the final seek segment */ + if (event) { + if (sink->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_sink_default_prepare_seek_segment (sink, event, + &seeksegment)) { + GST_DEBUG_OBJECT (sink, + "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. */ + if (res) { + GST_DEBUG_OBJECT (sink, "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 the new position. */ + res = gst_base_sink_default_do_seek (sink, &seeksegment); - GST_DEBUG_OBJECT (basesink, "stop flushing upstream"); + GST_DEBUG_OBJECT (sink, "stop flushing upstream"); gst_pad_push_event (pad, gst_event_new_flush_stop ()); - gst_base_sink_flush_stop (basesink, pad); - } else { - GST_DEBUG_OBJECT (basesink, "restarting pulling thread"); + gst_base_sink_flush_stop (sink, pad); + } else if (res && sink->abidata.ABI.running) { + /* we are running the current segment and doing a non-flushing seek, + * close the segment first based on the last_stop. */ + GST_DEBUG_OBJECT (sink, "closing running segment %" G_GINT64_FORMAT + " to %" G_GINT64_FORMAT, sink->segment.start, sink->segment.last_stop); + /* The subclass must have converted the segment to the processing format + * by now */ + if (res && seeksegment.format != dest_format) { + GST_DEBUG_OBJECT (sink, "Subclass failed to prepare a seek segment " + "in the correct format. Aborting seek."); + /* if successfull seek, we update our real segment and push + * out the new segment. */ + memcpy (&sink->segment, &seeksegment, sizeof (GstSegment)); + if (sink->segment.flags & GST_SEEK_FLAG_SEGMENT) { + gst_element_post_message (GST_ELEMENT (sink), + gst_message_new_segment_start (GST_OBJECT (sink), + sink->segment.format, sink->segment.last_stop)); + sink->priv->discont = TRUE; + sink->abidata.ABI.running = TRUE; GST_PAD_STREAM_UNLOCK (pad); - return FALSE; /* with STREAM_LOCK @@ -3429,28 +3586,63 @@ * for EOS and for making sure that we don't report a position we * have not reached yet. */ -gst_base_sink_get_position_last (GstBaseSink * basesink, gint64 * cur) +gst_base_sink_get_position_last (GstBaseSink * basesink, GstFormat format, + gint64 * cur) - /* return last observed stream time */ - *cur = basesink->priv->current_sstop; + GstFormat oformat; + GstSegment *segment; + gboolean ret = TRUE; + segment = &basesink->segment; + oformat = segment->format; + if (oformat == GST_FORMAT_TIME) { + /* return last observed stream time, we keep the stream time around in the + * time format. */ + *cur = basesink->priv->current_sstop; + /* convert last stop to stream time */ + *cur = gst_segment_to_stream_time (segment, oformat, segment->last_stop); + if (*cur != -1 && oformat != format) { + /* convert to the target format if we need to */ + ret = + gst_pad_query_convert (basesink->sinkpad, oformat, *cur, &format, cur); GST_DEBUG_OBJECT (basesink, "POSITION: %" GST_TIME_FORMAT, GST_TIME_ARGS (*cur)); - return TRUE; + return ret; /* get the position when we are PAUSED, this is the stream time of the buffer * that prerolled. If no buffer is prerolled (we are still flushing), this * value will be -1. */ -gst_base_sink_get_position_paused (GstBaseSink * basesink, gint64 * cur) +gst_base_sink_get_position_paused (GstBaseSink * basesink, GstFormat format, gboolean res; gint64 time; GstSegment *segment; - *cur = basesink->priv->current_sstart; - segment = basesink->abidata.ABI.clip_segment; + /* we don't use the clip segment in pull mode, when seeking we update the + * main segment directly with the new segment values without it having to be + * activated by the rendering after preroll */ + if (basesink->pad_mode == GST_ACTIVATE_PUSH) + segment = basesink->abidata.ABI.clip_segment; + else + segment = &basesink->segment; + *cur = basesink->priv->current_sstart; time = segment->time; @@ -3468,13 +3660,16 @@ } else { /* reverse, next expected timestamp is segment->stop. We use the function * to get things right for negative applied_rates. */ - *cur = - gst_segment_to_stream_time (segment, GST_FORMAT_TIME, segment->stop); + *cur = gst_segment_to_stream_time (segment, oformat, segment->stop); GST_DEBUG_OBJECT (basesink, "reverse POSITION: %" GST_TIME_FORMAT, GST_TIME_ARGS (*cur)); } res = (*cur != -1); + if (res && oformat != format) { return res; @@ -3485,102 +3680,114 @@ GstClock *clock; gboolean res = FALSE; + GstFormat oformat, tformat; + GstClockTime now, base, latency; + gint64 time, accum, duration; + gint64 last; - switch (format) { - /* we can answer time format */ - case GST_FORMAT_TIME: - { - GstClockTime now, base, latency; - gint64 time, accum, duration; - gdouble rate; - gint64 last; + GST_OBJECT_LOCK (basesink); + /* our intermediate time format */ + tformat = GST_FORMAT_TIME; + /* get the format in the segment */ + oformat = basesink->segment.format; - GST_OBJECT_LOCK (basesink); + /* can only give answer based on the clock if not EOS */ + if (G_UNLIKELY (basesink->eos)) + goto in_eos; - /* can only give answer based on the clock if not EOS */ - if (G_UNLIKELY (basesink->eos)) - goto in_eos; + /* we can only get the segment when we are not NULL or READY */ + if (!basesink->have_newsegment) + goto wrong_state; - /* we can only get the segment when we are not NULL or READY */ - if (!basesink->have_newsegment) - goto wrong_state; + /* when not in PLAYING or when we're busy with a state change, we + * cannot read from the clock so we report time based on the + * last seen timestamp. */ + if (GST_STATE (basesink) != GST_STATE_PLAYING || + GST_STATE_PENDING (basesink) != GST_STATE_VOID_PENDING) + goto in_pause; - /* when not in PLAYING or when we're busy with a state change, we - * cannot read from the clock so we report time based on the - * last seen timestamp. */ - if (GST_STATE (basesink) != GST_STATE_PLAYING || - GST_STATE_PENDING (basesink) != GST_STATE_VOID_PENDING) - goto in_pause; + /* we need to sync on the clock. */ + if (basesink->sync == FALSE) + goto no_sync; - /* we need to sync on the clock. */ - if (basesink->sync == FALSE) - goto no_sync; + /* and we need a clock */ + if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL)) - /* and we need a clock */ - if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL)) + /* collect all data we need holding the lock */ + if (GST_CLOCK_TIME_IS_VALID (basesink->segment.time)) + time = basesink->segment.time; + time = 0; - /* collect all data we need holding the lock */ - if (GST_CLOCK_TIME_IS_VALID (basesink->segment.time)) - time = basesink->segment.time; - else - time = 0; + if (GST_CLOCK_TIME_IS_VALID (basesink->segment.stop)) + duration = basesink->segment.stop - basesink->segment.start; + duration = 0; - if (GST_CLOCK_TIME_IS_VALID (basesink->segment.stop)) - duration = basesink->segment.stop - basesink->segment.start; - duration = 0; + base = GST_ELEMENT_CAST (basesink)->base_time; + accum = basesink->segment.accum; + rate = basesink->segment.rate * basesink->segment.applied_rate; + gst_base_sink_get_position_last (basesink, format, &last); + latency = basesink->priv->latency; - base = GST_ELEMENT_CAST (basesink)->base_time; - accum = basesink->segment.accum; - rate = basesink->segment.rate * basesink->segment.applied_rate; - gst_base_sink_get_position_last (basesink, &last); - latency = basesink->priv->latency; + gst_object_ref (clock); + /* need to release the object lock before we can get the time, + * a clock might take the LOCK of the provider, which could be + * a basesink subclass. */ + GST_OBJECT_UNLOCK (basesink); - gst_object_ref (clock); - /* need to release the object lock before we can get the time, - * a clock might take the LOCK of the provider, which could be - * a basesink subclass. */ - GST_OBJECT_UNLOCK (basesink); + now = gst_clock_get_time (clock); - now = gst_clock_get_time (clock); + if (oformat != tformat) { + /* convert accum, time and duration to time */ + if (!gst_pad_query_convert (basesink->sinkpad, oformat, accum, &tformat, + &accum)) + goto convert_failed; + if (!gst_pad_query_convert (basesink->sinkpad, oformat, duration, &tformat, + &duration)) + if (!gst_pad_query_convert (basesink->sinkpad, oformat, time, &tformat, + &time)) - /* subtract base time and accumulated time from the clock time. - * Make sure we don't go negative. This is the current time in - * the segment which we need to scale with the combined - * rate and applied rate. */ - base += accum; - base += latency; - base = MIN (now, base); + /* subtract base time and accumulated time from the clock time. + * Make sure we don't go negative. This is the current time in + * the segment which we need to scale with the combined + * rate and applied rate. */ + base += accum; + base += latency; + base = MIN (now, base); - /* for negative rates we need to count back from from the segment - * duration. */ - if (rate < 0.0) - time += duration; + /* for negative rates we need to count back from from the segment + * duration. */ + if (rate < 0.0) + time += duration; - *cur = time + gst_guint64_to_gdouble (now - base) * rate; + *cur = time + gst_guint64_to_gdouble (now - base) * rate; - /* never report more than last seen position */ - if (last != -1) - *cur = MIN (last, *cur); + /* never report more than last seen position */ + if (last != -1) + *cur = MIN (last, *cur); - gst_object_unref (clock); + gst_object_unref (clock); - res = TRUE; + GST_DEBUG_OBJECT (basesink, + "now %" GST_TIME_FORMAT " - base %" GST_TIME_FORMAT " - accum %" + GST_TIME_FORMAT " + time %" GST_TIME_FORMAT, + GST_TIME_ARGS (now), GST_TIME_ARGS (base), + GST_TIME_ARGS (accum), GST_TIME_ARGS (time)); - GST_DEBUG_OBJECT (basesink, - "now %" GST_TIME_FORMAT " - base %" GST_TIME_FORMAT " - accum %" - GST_TIME_FORMAT " + time %" GST_TIME_FORMAT, - GST_TIME_ARGS (now), GST_TIME_ARGS (base), - GST_TIME_ARGS (accum), GST_TIME_ARGS (time)); - break; - } - default: - /* cannot answer other than TIME, ask to send the query upstream. */ - *upstream = TRUE; + if (oformat != format) { + /* convert time to final format */ + if (!gst_pad_query_convert (basesink->sinkpad, tformat, *cur, &format, cur)) + res = TRUE; done: GST_DEBUG_OBJECT (basesink, "res: %d, POSITION: %" GST_TIME_FORMAT, res, GST_TIME_ARGS (*cur)); @@ -3590,14 +3797,14 @@ in_eos: { GST_DEBUG_OBJECT (basesink, "position in EOS"); - res = gst_base_sink_get_position_last (basesink, cur); + res = gst_base_sink_get_position_last (basesink, format, cur); GST_OBJECT_UNLOCK (basesink); goto done; in_pause: GST_DEBUG_OBJECT (basesink, "position in PAUSED"); - res = gst_base_sink_get_position_paused (basesink, cur); + res = gst_base_sink_get_position_paused (basesink, format, cur); @@ -3623,6 +3830,12 @@ return res; +convert_failed: + GST_DEBUG_OBJECT (basesink, "convert failed, try upstream"); + *upstream = TRUE; @@ -3667,19 +3880,24 @@ uformat = GST_FORMAT_BYTES; /* get the duration in bytes, in pull mode that's all we are sure to - * know. */ + * know. We have to explicitly get this value from upstream instead of + * using our cached value because it might change. Duration caching + * should be done at a higher level. */ res = gst_pad_query_peer_duration (basesink->sinkpad, &uformat, &uduration); - if (res && format != uformat) { - /* convert to the requested format */ - res = gst_pad_query_convert (basesink->sinkpad, uformat, uduration, - &format, &duration); - } else { - duration = uduration; - } if (res) { - /* set the result */ - gst_query_set_duration (query, format, duration); + gst_segment_set_duration (&basesink->segment, uformat, uduration); + if (format != uformat) { + /* convert to the requested format */ + res = gst_pad_query_convert (basesink->sinkpad, uformat, uduration, + &format, &duration); + } else { + duration = uduration; + } + if (res) { + /* set the result */ + gst_query_set_duration (query, format, duration); } } else { /* in push mode we simply forward upstream */ Index: gstbasesink.h RCS file: /cvs/gstreamer/gstreamer/libs/gst/base/gstbasesink.h,v retrieving revision 1.52 retrieving revision 1.53 diff -u -d -r1.52 -r1.53 --- gstbasesink.h 16 Oct 2008 14:09:18 -0000 1.52 +++ gstbasesink.h 20 Oct 2008 15:18:14 -0000 1.53 @@ -95,6 +95,7 @@ GstSegment *clip_segment; /* max amount of time a buffer can be late, -1 no limit. */ gint64 max_lateness; + gboolean running; } ABI; gpointer _gst_reserved[GST_PADDING_LARGE - 1]; } abidata; |