mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-07 07:58:51 +00:00
libs/gst/base/gstbasesink.c: Implement more seeking in pull mode.
Original commit message from CVS: * 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.
This commit is contained in:
parent
b340b510ce
commit
ca9ccf1d02
3 changed files with 352 additions and 118 deletions
15
ChangeLog
15
ChangeLog
|
@ -1,3 +1,18 @@
|
||||||
|
2008-10-20 Wim Taymans <wim.taymans@collabora.co.uk>
|
||||||
|
|
||||||
|
* 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.taymans@collabora.co.uk>
|
2008-10-20 Wim Taymans <wim.taymans@collabora.co.uk>
|
||||||
|
|
||||||
* gst/gstpad.c: (gst_pad_configure_src):
|
* gst/gstpad.c: (gst_pad_configure_src):
|
||||||
|
|
|
@ -216,6 +216,8 @@ struct _GstBaseSinkPrivate
|
||||||
GstCaps *pull_caps;
|
GstCaps *pull_caps;
|
||||||
|
|
||||||
guint blocksize;
|
guint blocksize;
|
||||||
|
|
||||||
|
gboolean discont;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
|
#define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
|
||||||
|
@ -305,6 +307,10 @@ static gboolean gst_base_sink_set_flushing (GstBaseSink * basesink,
|
||||||
GstPad * pad, gboolean flushing);
|
GstPad * pad, gboolean flushing);
|
||||||
static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink,
|
static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink,
|
||||||
gboolean active);
|
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,
|
static GstStateChangeReturn gst_base_sink_change_state (GstElement * element,
|
||||||
GstStateChange transition);
|
GstStateChange transition);
|
||||||
|
@ -2931,50 +2937,201 @@ wrong_mode:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
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;
|
||||||
|
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 (sink->sinkpad, 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 (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;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
no_format:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (sink, "undefined format given, seek aborted.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* perform a seek, only executed in pull mode */
|
/* perform a seek, only executed in pull mode */
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_base_sink_perform_seek (GstBaseSink * basesink, GstPad * pad,
|
gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
|
||||||
GstEvent * event)
|
|
||||||
{
|
{
|
||||||
gboolean flush;
|
gboolean flush;
|
||||||
gdouble rate;
|
gdouble rate;
|
||||||
GstFormat seek_format;
|
GstFormat seek_format, dest_format;
|
||||||
GstSeekFlags flags;
|
GstSeekFlags flags;
|
||||||
GstSeekType cur_type, stop_type;
|
GstSeekType cur_type, stop_type;
|
||||||
|
gboolean seekseg_configured = FALSE;
|
||||||
gint64 cur, stop;
|
gint64 cur, stop;
|
||||||
|
gboolean update, res = TRUE;
|
||||||
|
GstSegment seeksegment;
|
||||||
|
|
||||||
|
dest_format = sink->segment.format;
|
||||||
|
|
||||||
if (event) {
|
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,
|
gst_event_parse_seek (event, &rate, &seek_format, &flags,
|
||||||
&cur_type, &cur, &stop_type, &stop);
|
&cur_type, &cur, &stop_type, &stop);
|
||||||
|
|
||||||
flush = flags & GST_SEEK_FLAG_FLUSH;
|
flush = flags & GST_SEEK_FLAG_FLUSH;
|
||||||
} else {
|
} else {
|
||||||
GST_DEBUG_OBJECT (basesink, "performing seek without event");
|
GST_DEBUG_OBJECT (sink, "performing seek without event");
|
||||||
flush = FALSE;
|
flush = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flush) {
|
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_pad_push_event (pad, gst_event_new_flush_start ());
|
||||||
gst_base_sink_flush_start (basesink, pad);
|
gst_base_sink_flush_start (sink, pad);
|
||||||
} else {
|
} else {
|
||||||
GST_DEBUG_OBJECT (basesink, "pausing pulling thread");
|
GST_DEBUG_OBJECT (sink, "pausing pulling thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_PAD_STREAM_LOCK (pad);
|
GST_PAD_STREAM_LOCK (pad);
|
||||||
|
|
||||||
if (flush) {
|
/* If we configured the seeksegment above, don't overwrite it now. Otherwise
|
||||||
GST_DEBUG_OBJECT (basesink, "stop flushing upstream");
|
* copy the current segment info into the temp segment that we can actually
|
||||||
gst_pad_push_event (pad, gst_event_new_flush_stop ());
|
* attempt the seek with. We only update the real segment if the seek suceeds. */
|
||||||
gst_base_sink_flush_stop (basesink, pad);
|
if (!seekseg_configured) {
|
||||||
} else {
|
memcpy (&seeksegment, &sink->segment, sizeof (GstSegment));
|
||||||
GST_DEBUG_OBJECT (basesink, "restarting pulling thread");
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (flush) {
|
||||||
|
GST_DEBUG_OBJECT (sink, "stop flushing upstream");
|
||||||
|
gst_pad_push_event (pad, gst_event_new_flush_stop ());
|
||||||
|
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.");
|
||||||
|
res = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if successfull seek, we update our real segment and push
|
||||||
|
* out the new segment. */
|
||||||
|
if (res) {
|
||||||
|
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);
|
GST_PAD_STREAM_UNLOCK (pad);
|
||||||
|
|
||||||
return FALSE;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* with STREAM_LOCK
|
/* with STREAM_LOCK
|
||||||
|
@ -3429,28 +3586,63 @@ gst_base_sink_peer_query (GstBaseSink * sink, GstQuery * query)
|
||||||
* for EOS and for making sure that we don't report a position we
|
* for EOS and for making sure that we don't report a position we
|
||||||
* have not reached yet. */
|
* have not reached yet. */
|
||||||
static gboolean
|
static gboolean
|
||||||
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 */
|
GstFormat oformat;
|
||||||
*cur = basesink->priv->current_sstop;
|
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;
|
||||||
|
} else {
|
||||||
|
/* 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_DEBUG_OBJECT (basesink, "POSITION: %" GST_TIME_FORMAT,
|
||||||
GST_TIME_ARGS (*cur));
|
GST_TIME_ARGS (*cur));
|
||||||
return TRUE;
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get the position when we are PAUSED, this is the stream time of the buffer
|
/* 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
|
* that prerolled. If no buffer is prerolled (we are still flushing), this
|
||||||
* value will be -1. */
|
* value will be -1. */
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_base_sink_get_position_paused (GstBaseSink * basesink, gint64 * cur)
|
gst_base_sink_get_position_paused (GstBaseSink * basesink, GstFormat format,
|
||||||
|
gint64 * cur)
|
||||||
{
|
{
|
||||||
gboolean res;
|
gboolean res;
|
||||||
gint64 time;
|
gint64 time;
|
||||||
GstSegment *segment;
|
GstSegment *segment;
|
||||||
|
GstFormat oformat;
|
||||||
|
|
||||||
*cur = basesink->priv->current_sstart;
|
/* we don't use the clip segment in pull mode, when seeking we update the
|
||||||
segment = basesink->abidata.ABI.clip_segment;
|
* 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;
|
||||||
|
oformat = segment->format;
|
||||||
|
|
||||||
|
if (oformat == GST_FORMAT_TIME) {
|
||||||
|
*cur = basesink->priv->current_sstart;
|
||||||
|
} else {
|
||||||
|
*cur = gst_segment_to_stream_time (segment, oformat, segment->last_stop);
|
||||||
|
}
|
||||||
|
|
||||||
time = segment->time;
|
time = segment->time;
|
||||||
|
|
||||||
|
@ -3468,13 +3660,16 @@ gst_base_sink_get_position_paused (GstBaseSink * basesink, gint64 * cur)
|
||||||
} else {
|
} else {
|
||||||
/* reverse, next expected timestamp is segment->stop. We use the function
|
/* reverse, next expected timestamp is segment->stop. We use the function
|
||||||
* to get things right for negative applied_rates. */
|
* to get things right for negative applied_rates. */
|
||||||
*cur =
|
*cur = gst_segment_to_stream_time (segment, oformat, segment->stop);
|
||||||
gst_segment_to_stream_time (segment, GST_FORMAT_TIME, segment->stop);
|
|
||||||
GST_DEBUG_OBJECT (basesink, "reverse POSITION: %" GST_TIME_FORMAT,
|
GST_DEBUG_OBJECT (basesink, "reverse POSITION: %" GST_TIME_FORMAT,
|
||||||
GST_TIME_ARGS (*cur));
|
GST_TIME_ARGS (*cur));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res = (*cur != -1);
|
res = (*cur != -1);
|
||||||
|
if (res && oformat != format) {
|
||||||
|
res =
|
||||||
|
gst_pad_query_convert (basesink->sinkpad, oformat, *cur, &format, cur);
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -3485,102 +3680,114 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
|
||||||
{
|
{
|
||||||
GstClock *clock;
|
GstClock *clock;
|
||||||
gboolean res = FALSE;
|
gboolean res = FALSE;
|
||||||
|
GstFormat oformat, tformat;
|
||||||
|
GstClockTime now, base, latency;
|
||||||
|
gint64 time, accum, duration;
|
||||||
|
gdouble rate;
|
||||||
|
gint64 last;
|
||||||
|
|
||||||
switch (format) {
|
GST_OBJECT_LOCK (basesink);
|
||||||
/* we can answer time format */
|
/* our intermediate time format */
|
||||||
case GST_FORMAT_TIME:
|
tformat = GST_FORMAT_TIME;
|
||||||
{
|
/* get the format in the segment */
|
||||||
GstClockTime now, base, latency;
|
oformat = basesink->segment.format;
|
||||||
gint64 time, accum, duration;
|
|
||||||
gdouble rate;
|
|
||||||
gint64 last;
|
|
||||||
|
|
||||||
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 */
|
/* we can only get the segment when we are not NULL or READY */
|
||||||
if (G_UNLIKELY (basesink->eos))
|
if (!basesink->have_newsegment)
|
||||||
goto in_eos;
|
goto wrong_state;
|
||||||
|
|
||||||
/* we can only get the segment when we are not NULL or READY */
|
/* when not in PLAYING or when we're busy with a state change, we
|
||||||
if (!basesink->have_newsegment)
|
* cannot read from the clock so we report time based on the
|
||||||
goto wrong_state;
|
* 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
|
/* we need to sync on the clock. */
|
||||||
* cannot read from the clock so we report time based on the
|
if (basesink->sync == FALSE)
|
||||||
* last seen timestamp. */
|
goto no_sync;
|
||||||
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. */
|
/* and we need a clock */
|
||||||
if (basesink->sync == FALSE)
|
if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL))
|
||||||
goto no_sync;
|
goto no_sync;
|
||||||
|
|
||||||
/* and we need a clock */
|
/* collect all data we need holding the lock */
|
||||||
if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL))
|
if (GST_CLOCK_TIME_IS_VALID (basesink->segment.time))
|
||||||
goto no_sync;
|
time = basesink->segment.time;
|
||||||
|
else
|
||||||
|
time = 0;
|
||||||
|
|
||||||
/* collect all data we need holding the lock */
|
if (GST_CLOCK_TIME_IS_VALID (basesink->segment.stop))
|
||||||
if (GST_CLOCK_TIME_IS_VALID (basesink->segment.time))
|
duration = basesink->segment.stop - basesink->segment.start;
|
||||||
time = basesink->segment.time;
|
else
|
||||||
else
|
duration = 0;
|
||||||
time = 0;
|
|
||||||
|
|
||||||
if (GST_CLOCK_TIME_IS_VALID (basesink->segment.stop))
|
base = GST_ELEMENT_CAST (basesink)->base_time;
|
||||||
duration = basesink->segment.stop - basesink->segment.start;
|
accum = basesink->segment.accum;
|
||||||
else
|
rate = basesink->segment.rate * basesink->segment.applied_rate;
|
||||||
duration = 0;
|
gst_base_sink_get_position_last (basesink, format, &last);
|
||||||
|
latency = basesink->priv->latency;
|
||||||
|
|
||||||
base = GST_ELEMENT_CAST (basesink)->base_time;
|
gst_object_ref (clock);
|
||||||
accum = basesink->segment.accum;
|
/* need to release the object lock before we can get the time,
|
||||||
rate = basesink->segment.rate * basesink->segment.applied_rate;
|
* a clock might take the LOCK of the provider, which could be
|
||||||
gst_base_sink_get_position_last (basesink, &last);
|
* a basesink subclass. */
|
||||||
latency = basesink->priv->latency;
|
GST_OBJECT_UNLOCK (basesink);
|
||||||
|
|
||||||
gst_object_ref (clock);
|
now = gst_clock_get_time (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);
|
if (oformat != tformat) {
|
||||||
|
/* convert accum, time and duration to time */
|
||||||
/* subtract base time and accumulated time from the clock time.
|
if (!gst_pad_query_convert (basesink->sinkpad, oformat, accum, &tformat,
|
||||||
* Make sure we don't go negative. This is the current time in
|
&accum))
|
||||||
* the segment which we need to scale with the combined
|
goto convert_failed;
|
||||||
* rate and applied rate. */
|
if (!gst_pad_query_convert (basesink->sinkpad, oformat, duration, &tformat,
|
||||||
base += accum;
|
&duration))
|
||||||
base += latency;
|
goto convert_failed;
|
||||||
base = MIN (now, base);
|
if (!gst_pad_query_convert (basesink->sinkpad, oformat, time, &tformat,
|
||||||
|
&time))
|
||||||
/* for negative rates we need to count back from from the segment
|
goto convert_failed;
|
||||||
* duration. */
|
|
||||||
if (rate < 0.0)
|
|
||||||
time += duration;
|
|
||||||
|
|
||||||
*cur = time + gst_guint64_to_gdouble (now - base) * rate;
|
|
||||||
|
|
||||||
/* never report more than last seen position */
|
|
||||||
if (last != -1)
|
|
||||||
*cur = MIN (last, *cur);
|
|
||||||
|
|
||||||
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));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
/* cannot answer other than TIME, ask to send the query upstream. */
|
|
||||||
*upstream = TRUE;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
*cur = time + gst_guint64_to_gdouble (now - base) * rate;
|
||||||
|
|
||||||
|
/* never report more than last seen position */
|
||||||
|
if (last != -1)
|
||||||
|
*cur = MIN (last, *cur);
|
||||||
|
|
||||||
|
gst_object_unref (clock);
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
if (oformat != format) {
|
||||||
|
/* convert time to final format */
|
||||||
|
if (!gst_pad_query_convert (basesink->sinkpad, tformat, *cur, &format, cur))
|
||||||
|
goto convert_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = TRUE;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
GST_DEBUG_OBJECT (basesink, "res: %d, POSITION: %" GST_TIME_FORMAT,
|
GST_DEBUG_OBJECT (basesink, "res: %d, POSITION: %" GST_TIME_FORMAT,
|
||||||
res, GST_TIME_ARGS (*cur));
|
res, GST_TIME_ARGS (*cur));
|
||||||
|
@ -3590,14 +3797,14 @@ done:
|
||||||
in_eos:
|
in_eos:
|
||||||
{
|
{
|
||||||
GST_DEBUG_OBJECT (basesink, "position 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);
|
GST_OBJECT_UNLOCK (basesink);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
in_pause:
|
in_pause:
|
||||||
{
|
{
|
||||||
GST_DEBUG_OBJECT (basesink, "position in PAUSED");
|
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);
|
||||||
GST_OBJECT_UNLOCK (basesink);
|
GST_OBJECT_UNLOCK (basesink);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
@ -3623,6 +3830,12 @@ no_sync:
|
||||||
GST_OBJECT_UNLOCK (basesink);
|
GST_OBJECT_UNLOCK (basesink);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
convert_failed:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (basesink, "convert failed, try upstream");
|
||||||
|
*upstream = TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -3667,19 +3880,24 @@ gst_base_sink_query (GstElement * element, GstQuery * query)
|
||||||
uformat = GST_FORMAT_BYTES;
|
uformat = GST_FORMAT_BYTES;
|
||||||
|
|
||||||
/* get the duration in bytes, in pull mode that's all we are sure to
|
/* 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,
|
res = gst_pad_query_peer_duration (basesink->sinkpad, &uformat,
|
||||||
&uduration);
|
&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) {
|
if (res) {
|
||||||
/* set the result */
|
gst_segment_set_duration (&basesink->segment, uformat, uduration);
|
||||||
gst_query_set_duration (query, format, duration);
|
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 {
|
} else {
|
||||||
/* in push mode we simply forward upstream */
|
/* in push mode we simply forward upstream */
|
||||||
|
|
|
@ -95,6 +95,7 @@ struct _GstBaseSink {
|
||||||
GstSegment *clip_segment;
|
GstSegment *clip_segment;
|
||||||
/* max amount of time a buffer can be late, -1 no limit. */
|
/* max amount of time a buffer can be late, -1 no limit. */
|
||||||
gint64 max_lateness;
|
gint64 max_lateness;
|
||||||
|
gboolean running;
|
||||||
} ABI;
|
} ABI;
|
||||||
gpointer _gst_reserved[GST_PADDING_LARGE - 1];
|
gpointer _gst_reserved[GST_PADDING_LARGE - 1];
|
||||||
} abidata;
|
} abidata;
|
||||||
|
|
Loading…
Reference in a new issue