mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-10 17:35:59 +00:00
basesink: first stab at frame stepping in PAUSED
Unlock the prerolled frame and recheck if we need to step. Keep a simple counter for the frames we're about to skip while stepping and preroll/post step_done when stepping finished.
This commit is contained in:
parent
386c516e51
commit
546c959f26
1 changed files with 161 additions and 2 deletions
|
@ -152,6 +152,19 @@ GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
|
||||||
#define GST_BASE_SINK_GET_PRIVATE(obj) \
|
#define GST_BASE_SINK_GET_PRIVATE(obj) \
|
||||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_SINK, GstBaseSinkPrivate))
|
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_SINK, GstBaseSinkPrivate))
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
guint cookie;
|
||||||
|
guint32 seqnum;
|
||||||
|
GstFormat format;
|
||||||
|
guint64 remaining;
|
||||||
|
guint64 amount;
|
||||||
|
guint64 duration;
|
||||||
|
gdouble rate;
|
||||||
|
gboolean intermediate;
|
||||||
|
gboolean need_preroll;
|
||||||
|
} GstStepInfo;
|
||||||
|
|
||||||
/* FIXME, some stuff in ABI.data and other in Private...
|
/* FIXME, some stuff in ABI.data and other in Private...
|
||||||
* Make up your mind please.
|
* Make up your mind please.
|
||||||
*/
|
*/
|
||||||
|
@ -224,6 +237,8 @@ struct _GstBaseSinkPrivate
|
||||||
guint32 seqnum;
|
guint32 seqnum;
|
||||||
|
|
||||||
gboolean call_preroll;
|
gboolean call_preroll;
|
||||||
|
|
||||||
|
GstStepInfo step;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
|
#define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
|
||||||
|
@ -1882,6 +1897,58 @@ flushing:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
handle_stepping (GstBaseSink * basesink)
|
||||||
|
{
|
||||||
|
GstBaseSinkPrivate *priv;
|
||||||
|
|
||||||
|
priv = basesink->priv;
|
||||||
|
|
||||||
|
/* see if we need to skip this buffer because of stepping */
|
||||||
|
if (priv->step.remaining != -1) {
|
||||||
|
GstMessage *message;
|
||||||
|
GstStepInfo *step;
|
||||||
|
|
||||||
|
step = &priv->step;
|
||||||
|
|
||||||
|
switch (step->format) {
|
||||||
|
case GST_FORMAT_TIME:
|
||||||
|
GST_DEBUG_OBJECT (basesink,
|
||||||
|
"got time step %" GST_TIME_FORMAT "/%" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (step->remaining), GST_TIME_ARGS (step->amount));
|
||||||
|
break;
|
||||||
|
case GST_FORMAT_BUFFERS:
|
||||||
|
GST_DEBUG_OBJECT (basesink,
|
||||||
|
"got default step %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
|
||||||
|
step->remaining, step->amount);
|
||||||
|
if (step->remaining > 0) {
|
||||||
|
step->remaining--;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_FORMAT_DEFAULT:
|
||||||
|
default:
|
||||||
|
GST_DEBUG_OBJECT (basesink,
|
||||||
|
"got unknown step %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
|
||||||
|
step->remaining, step->amount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
GST_DEBUG_OBJECT (basesink, "step complete");
|
||||||
|
|
||||||
|
message =
|
||||||
|
gst_message_new_step_done (GST_OBJECT_CAST (basesink), step->format,
|
||||||
|
step->amount, step->rate, step->duration, step->intermediate);
|
||||||
|
gst_message_set_seqnum (message, step->seqnum);
|
||||||
|
gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
|
||||||
|
|
||||||
|
step->remaining = -1;
|
||||||
|
if (!step->intermediate)
|
||||||
|
basesink->need_preroll = step->need_preroll;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* with STREAM_LOCK, PREROLL_LOCK
|
/* with STREAM_LOCK, PREROLL_LOCK
|
||||||
*
|
*
|
||||||
* Make sure we are in PLAYING and synchronize an object to the clock.
|
* Make sure we are in PLAYING and synchronize an object to the clock.
|
||||||
|
@ -1915,9 +1982,11 @@ gst_base_sink_do_sync (GstBaseSink * basesink, GstPad * pad,
|
||||||
gboolean do_sync;
|
gboolean do_sync;
|
||||||
GstBaseSinkPrivate *priv;
|
GstBaseSinkPrivate *priv;
|
||||||
GstFlowReturn ret;
|
GstFlowReturn ret;
|
||||||
|
guint step_cookie;
|
||||||
|
|
||||||
priv = basesink->priv;
|
priv = basesink->priv;
|
||||||
|
|
||||||
|
do_step:
|
||||||
sstart = sstop = rstart = rstop = -1;
|
sstart = sstop = rstart = rstop = -1;
|
||||||
do_sync = TRUE;
|
do_sync = TRUE;
|
||||||
|
|
||||||
|
@ -1932,21 +2001,30 @@ gst_base_sink_do_sync (GstBaseSink * basesink, GstPad * pad,
|
||||||
if (G_UNLIKELY (!syncable))
|
if (G_UNLIKELY (!syncable))
|
||||||
goto not_syncable;
|
goto not_syncable;
|
||||||
|
|
||||||
|
/* see if we need to skip this buffer as part of stepping */
|
||||||
|
if (!handle_stepping (basesink))
|
||||||
|
goto step_skipped;
|
||||||
|
|
||||||
/* store timing info for current object */
|
/* store timing info for current object */
|
||||||
priv->current_rstart = rstart;
|
priv->current_rstart = rstart;
|
||||||
priv->current_rstop = (rstop != -1 ? rstop : rstart);
|
priv->current_rstop = (rstop != -1 ? rstop : rstart);
|
||||||
|
|
||||||
/* save sync time for eos when the previous object needed sync */
|
/* save sync time for eos when the previous object needed sync */
|
||||||
priv->eos_rtime = (do_sync ? priv->current_rstop : -1);
|
priv->eos_rtime = (do_sync ? priv->current_rstop : -1);
|
||||||
|
|
||||||
|
step_cookie = priv->step.cookie;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
/* first do preroll, this makes sure we commit our state
|
/* first do preroll, this makes sure we commit our state
|
||||||
* to PAUSED and can continue to PLAYING. We cannot perform
|
* to PAUSED and can continue to PLAYING. We cannot perform
|
||||||
* any clock sync in PAUSED because there is no clock.
|
* any clock sync in PAUSED because there is no clock. */
|
||||||
*/
|
|
||||||
ret = gst_base_sink_do_preroll (basesink, obj);
|
ret = gst_base_sink_do_preroll (basesink, obj);
|
||||||
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
||||||
goto preroll_failed;
|
goto preroll_failed;
|
||||||
|
|
||||||
|
if (step_cookie != priv->step.cookie)
|
||||||
|
goto do_step;
|
||||||
|
|
||||||
/* After rendering we store the position of the last buffer so that we can use
|
/* After rendering we store the position of the last buffer so that we can use
|
||||||
* it to report the position. We need to take the lock here. */
|
* it to report the position. We need to take the lock here. */
|
||||||
GST_OBJECT_LOCK (basesink);
|
GST_OBJECT_LOCK (basesink);
|
||||||
|
@ -2006,6 +2084,11 @@ done:
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
/* ERRORS */
|
/* ERRORS */
|
||||||
|
step_skipped:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (basesink, "skipped stepped object %p", obj);
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
not_syncable:
|
not_syncable:
|
||||||
{
|
{
|
||||||
GST_DEBUG_OBJECT (basesink, "non syncable object %p", obj);
|
GST_DEBUG_OBJECT (basesink, "non syncable object %p", obj);
|
||||||
|
@ -3173,6 +3256,77 @@ gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_base_sink_perform_step (GstBaseSink * sink, GstPad * pad, GstEvent * event)
|
||||||
|
{
|
||||||
|
GstBaseSinkPrivate *priv;
|
||||||
|
GstBaseSinkClass *bclass;
|
||||||
|
gboolean flush, intermediate;
|
||||||
|
gdouble rate;
|
||||||
|
GstFormat format;
|
||||||
|
guint64 amount;
|
||||||
|
guint seqnum;
|
||||||
|
|
||||||
|
bclass = GST_BASE_SINK_GET_CLASS (sink);
|
||||||
|
priv = sink->priv;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (sink, "performing step with event %p", event);
|
||||||
|
|
||||||
|
gst_event_parse_step (event, &format, &amount, &rate, &flush, &intermediate);
|
||||||
|
seqnum = gst_event_get_seqnum (event);
|
||||||
|
|
||||||
|
if (flush) {
|
||||||
|
/* we need to call ::unlock before locking PREROLL_LOCK
|
||||||
|
* since we lock it before going into ::render */
|
||||||
|
if (bclass->unlock)
|
||||||
|
bclass->unlock (sink);
|
||||||
|
|
||||||
|
GST_PAD_PREROLL_LOCK (sink->sinkpad);
|
||||||
|
priv->step.cookie++;
|
||||||
|
|
||||||
|
/* update the segment */
|
||||||
|
priv->step.seqnum = seqnum;
|
||||||
|
priv->step.format = format;
|
||||||
|
priv->step.amount = amount;
|
||||||
|
priv->step.remaining = amount;
|
||||||
|
priv->step.rate = rate;
|
||||||
|
priv->step.intermediate = intermediate;
|
||||||
|
|
||||||
|
/* now that we have the PREROLL lock, clear our unlock request */
|
||||||
|
if (bclass->unlock_stop)
|
||||||
|
bclass->unlock_stop (sink);
|
||||||
|
|
||||||
|
if (sink->priv->async_enabled) {
|
||||||
|
/* and we need to commit our state again on the next
|
||||||
|
* prerolled buffer */
|
||||||
|
sink->playing_async = TRUE;
|
||||||
|
priv->step.need_preroll = TRUE;
|
||||||
|
sink->need_preroll = FALSE;
|
||||||
|
gst_element_lost_state_full (GST_ELEMENT_CAST (sink), FALSE);
|
||||||
|
} else {
|
||||||
|
sink->priv->have_latency = TRUE;
|
||||||
|
sink->need_preroll = FALSE;
|
||||||
|
}
|
||||||
|
priv->call_preroll = TRUE;
|
||||||
|
gst_base_sink_set_last_buffer (sink, NULL);
|
||||||
|
gst_base_sink_reset_qos (sink);
|
||||||
|
|
||||||
|
if (sink->clock_id) {
|
||||||
|
gst_clock_id_unschedule (sink->clock_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_element_post_message (GST_ELEMENT_CAST (sink),
|
||||||
|
gst_message_new_async_start (GST_OBJECT_CAST (sink), FALSE));
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (sink, "signal waiter");
|
||||||
|
GST_PAD_PREROLL_SIGNAL (sink->sinkpad);
|
||||||
|
|
||||||
|
GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/* with STREAM_LOCK
|
/* with STREAM_LOCK
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
|
@ -3595,6 +3749,10 @@ gst_base_sink_send_event (GstElement * element, GstEvent * event)
|
||||||
if (mode == GST_ACTIVATE_PULL)
|
if (mode == GST_ACTIVATE_PULL)
|
||||||
result = gst_base_sink_perform_seek (basesink, pad, event);
|
result = gst_base_sink_perform_seek (basesink, pad, event);
|
||||||
break;
|
break;
|
||||||
|
case GST_EVENT_STEP:
|
||||||
|
result = gst_base_sink_perform_step (basesink, pad, event);
|
||||||
|
forward = FALSE;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -4032,6 +4190,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
|
||||||
gst_base_sink_reset_qos (basesink);
|
gst_base_sink_reset_qos (basesink);
|
||||||
priv->commited = FALSE;
|
priv->commited = FALSE;
|
||||||
priv->call_preroll = TRUE;
|
priv->call_preroll = TRUE;
|
||||||
|
priv->step.remaining = -1;
|
||||||
if (priv->async_enabled) {
|
if (priv->async_enabled) {
|
||||||
GST_DEBUG_OBJECT (basesink, "doing async state change");
|
GST_DEBUG_OBJECT (basesink, "doing async state change");
|
||||||
/* when async enabled, post async-start message and return ASYNC from
|
/* when async enabled, post async-start message and return ASYNC from
|
||||||
|
|
Loading…
Reference in a new issue