appsink: Unblock pull-preroll on GAP event

... and add support for preroll sample with buffer list.

From application point of view, it's tricky to know that whether
pipeline is prerolled by buffer/buffer-list or by GAP event.
Then, if pipeline is prerolled by GAP event but pipeline's state
is staying in PAUSED, pull-preroll would be blocked forever
which doesn't seem to very desired behavior.
This commit is contained in:
Seungha Yang 2021-07-31 21:16:28 +09:00
parent f2b249e92b
commit f274a37f31
3 changed files with 164 additions and 32 deletions

View file

@ -152,10 +152,18 @@ If an EOS event was received before any buffers, this function returns
%NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
This function blocks until a preroll sample or EOS is received or the appsink
element is set to the READY/NULL state.</doc>
element is set to the READY/NULL state.
As of 1.24, gst_app_sink_pull_preroll() will be unblocked if the appsink
is prerolled via GAP event, then this function will return %NULL.
And if #GstBufferList support was enabled via
gst_app_sink_set_buffer_list_support(), returned #GstSample will contain
#GstBufferList instead of #GstBuffer when the preroll object is
#GstBufferList</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.h"/>
<return-value transfer-ownership="full" nullable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is stopped or EOS.
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is
stopped, EOS, or preroll object is GAP event.
Call gst_sample_unref() after usage.</doc>
<type name="Gst.Sample" c:type="GstSample*"/>
</return-value>
@ -247,10 +255,18 @@ this function returns %NULL. Use gst_app_sink_is_eos () to check for the EOS
condition.
This function blocks until a preroll sample or EOS is received, the appsink
element is set to the READY/NULL state, or the timeout expires.</doc>
element is set to the READY/NULL state, or the timeout expires.
As of 1.24, gst_app_sink_try_pull_preroll() will be unblocked if the appsink
is prerolled via GAP event, then this function will return %NULL.
And if #GstBufferList support was enabled via
gst_app_sink_set_buffer_list_support(), returned #GstSample will contain
#GstBufferList instead of #GstBuffer when the preroll object is
#GstBufferList</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.h"/>
<return-value transfer-ownership="full" nullable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is stopped or EOS or the timeout expires.
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is
stopped, EOS, or preroll object is GAP event or the timeout expires.
Call gst_sample_unref() after usage.</doc>
<type name="Gst.Sample" c:type="GstSample*"/>
</return-value>
@ -481,10 +497,18 @@ If an EOS event was received before any buffers, this function returns
%NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
This function blocks until a preroll sample or EOS is received or the appsink
element is set to the READY/NULL state.</doc>
element is set to the READY/NULL state.
As of 1.24, gst_app_sink_pull_preroll() will be unblocked if the appsink
is prerolled via GAP event, then this function will return %NULL.
And if #GstBufferList support was enabled via
gst_app_sink_set_buffer_list_support(), returned #GstSample will contain
#GstBufferList instead of #GstBuffer when the preroll object is
#GstBufferList</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.h"/>
<return-value transfer-ownership="full" nullable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is stopped or EOS.
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is
stopped, EOS, or preroll object is GAP event.
Call gst_sample_unref() after usage.</doc>
<type name="Gst.Sample" c:type="GstSample*"/>
</return-value>
@ -763,10 +787,18 @@ this function returns %NULL. Use gst_app_sink_is_eos () to check for the EOS
condition.
This function blocks until a preroll sample or EOS is received, the appsink
element is set to the READY/NULL state, or the timeout expires.</doc>
element is set to the READY/NULL state, or the timeout expires.
As of 1.24, gst_app_sink_try_pull_preroll() will be unblocked if the appsink
is prerolled via GAP event, then this function will return %NULL.
And if #GstBufferList support was enabled via
gst_app_sink_set_buffer_list_support(), returned #GstSample will contain
#GstBufferList instead of #GstBuffer when the preroll object is
#GstBufferList</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.h"/>
<return-value transfer-ownership="full" nullable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is stopped or EOS or the timeout expires.
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is
stopped, EOS, or preroll object is GAP event or the timeout expires.
Call gst_sample_unref() after usage.</doc>
<type name="Gst.Sample" c:type="GstSample*"/>
</return-value>
@ -1211,7 +1243,8 @@ gst_app_sink_set_callbacks().</doc>
<callback name="pull_preroll">
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.h"/>
<return-value transfer-ownership="full" nullable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is stopped or EOS.
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is
stopped, EOS, or preroll object is GAP event.
Call gst_sample_unref() after usage.</doc>
<type name="Gst.Sample" c:type="GstSample*"/>
</return-value>
@ -1243,7 +1276,8 @@ gst_app_sink_set_callbacks().</doc>
<callback name="try_pull_preroll">
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.h"/>
<return-value transfer-ownership="full" nullable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is stopped or EOS or the timeout expires.
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c">a #GstSample or NULL when the appsink is
stopped, EOS, or preroll object is GAP event or the timeout expires.
Call gst_sample_unref() after usage.</doc>
<type name="Gst.Sample" c:type="GstSample*"/>
</return-value>

View file

@ -124,7 +124,7 @@ struct _GstAppSinkPrivate
GCond cond;
GMutex mutex;
GstQueueArray *queue;
GstBuffer *preroll_buffer;
GstMiniObject *preroll_object;
GstCaps *preroll_caps;
GstCaps *last_caps;
GstSegment preroll_segment;
@ -209,8 +209,8 @@ static gboolean gst_app_sink_start (GstBaseSink * psink);
static gboolean gst_app_sink_stop (GstBaseSink * psink);
static gboolean gst_app_sink_event (GstBaseSink * sink, GstEvent * event);
static gboolean gst_app_sink_query (GstBaseSink * bsink, GstQuery * query);
static GstFlowReturn gst_app_sink_preroll (GstBaseSink * psink,
GstBuffer * buffer);
static GstFlowReturn gst_app_sink_preroll_object (GstBaseSink * psink,
GstMiniObject * object);
static GstFlowReturn gst_app_sink_render_common (GstBaseSink * psink,
GstMiniObject * data, gboolean is_list);
static GstFlowReturn gst_app_sink_render (GstBaseSink * psink,
@ -591,7 +591,7 @@ gst_app_sink_class_init (GstAppSinkClass * klass)
basesink_class->start = gst_app_sink_start;
basesink_class->stop = gst_app_sink_stop;
basesink_class->event = gst_app_sink_event;
basesink_class->preroll = gst_app_sink_preroll;
basesink_class->preroll_object = gst_app_sink_preroll_object;
basesink_class->render = gst_app_sink_render;
basesink_class->render_list = gst_app_sink_render_list;
basesink_class->get_caps = gst_app_sink_getcaps;
@ -648,7 +648,7 @@ gst_app_sink_dispose (GObject * obj)
callbacks = g_steal_pointer (&priv->callbacks);
while ((queue_obj = gst_queue_array_pop_head (priv->queue)))
gst_mini_object_unref (queue_obj);
gst_buffer_replace (&priv->preroll_buffer, NULL);
gst_clear_mini_object (&priv->preroll_object);
gst_caps_replace (&priv->preroll_caps, NULL);
gst_caps_replace (&priv->last_caps, NULL);
if (priv->sample) {
@ -799,7 +799,7 @@ gst_app_sink_flush_unlocked (GstAppSink * appsink)
GST_DEBUG_OBJECT (appsink, "flush stop appsink");
priv->is_eos = FALSE;
gst_buffer_replace (&priv->preroll_buffer, NULL);
gst_clear_mini_object (&priv->preroll_object);
while ((obj = gst_queue_array_pop_head (priv->queue)))
gst_mini_object_unref (obj);
@ -844,7 +844,7 @@ gst_app_sink_stop (GstBaseSink * psink)
priv->started = FALSE;
priv->wait_status = NOONE_WAITING;
gst_app_sink_flush_unlocked (appsink);
gst_buffer_replace (&priv->preroll_buffer, NULL);
gst_clear_mini_object (&priv->preroll_object);
gst_caps_replace (&priv->preroll_caps, NULL);
gst_caps_replace (&priv->last_caps, NULL);
gst_segment_init (&priv->preroll_segment, GST_FORMAT_UNDEFINED);
@ -871,7 +871,7 @@ gst_app_sink_setcaps (GstBaseSink * sink, GstCaps * caps)
gst_queue_array_push_tail (priv->queue, gst_event_new_caps (caps));
gst_queue_status_info_push_event (&priv->queue_status_info);
if (!priv->preroll_buffer)
if (!priv->preroll_object)
gst_caps_replace (&priv->preroll_caps, caps);
g_mutex_unlock (&priv->mutex);
@ -890,7 +890,7 @@ gst_app_sink_event (GstBaseSink * sink, GstEvent * event)
case GST_EVENT_SEGMENT:
g_mutex_lock (&priv->mutex);
GST_DEBUG_OBJECT (appsink, "receiving SEGMENT");
if (!priv->preroll_buffer)
if (!priv->preroll_object)
gst_event_copy_segment (event, &priv->preroll_segment);
g_mutex_unlock (&priv->mutex);
break;
@ -1004,7 +1004,7 @@ gst_app_sink_event (GstBaseSink * sink, GstEvent * event)
}
static GstFlowReturn
gst_app_sink_preroll (GstBaseSink * psink, GstBuffer * buffer)
gst_app_sink_preroll_object (GstBaseSink * psink, GstMiniObject * object)
{
GstFlowReturn res;
GstAppSink *appsink = GST_APP_SINK_CAST (psink);
@ -1016,8 +1016,8 @@ gst_app_sink_preroll (GstBaseSink * psink, GstBuffer * buffer)
if (priv->flushing)
goto flushing;
GST_DEBUG_OBJECT (appsink, "setting preroll buffer %p", buffer);
gst_buffer_replace (&priv->preroll_buffer, buffer);
GST_DEBUG_OBJECT (appsink, "setting preroll object %" GST_PTR_FORMAT, object);
gst_mini_object_replace (&priv->preroll_object, object);
if ((priv->wait_status & APP_WAITING))
g_cond_signal (&priv->cond);
@ -1284,7 +1284,7 @@ gst_app_sink_query (GstBaseSink * bsink, GstQuery * query)
{
g_mutex_lock (&priv->mutex);
GST_DEBUG_OBJECT (appsink, "waiting buffers to be consumed");
while (priv->queue_status_info.queued_buffers > 0 || priv->preroll_buffer) {
while (priv->queue_status_info.queued_buffers > 0 || priv->preroll_object) {
if (priv->unlock) {
/* we are asked to unlock, call the wait_preroll method */
g_mutex_unlock (&priv->mutex);
@ -1804,7 +1804,15 @@ gst_app_sink_get_wait_on_eos (GstAppSink * appsink)
* This function blocks until a preroll sample or EOS is received or the appsink
* element is set to the READY/NULL state.
*
* Returns: (transfer full) (nullable): a #GstSample or NULL when the appsink is stopped or EOS.
* As of 1.24, gst_app_sink_pull_preroll() will be unblocked if the appsink
* is prerolled via GAP event, then this function will return %NULL.
* And if #GstBufferList support was enabled via
* gst_app_sink_set_buffer_list_support(), returned #GstSample will contain
* #GstBufferList instead of #GstBuffer when the preroll object is
* #GstBufferList
*
* Returns: (transfer full) (nullable): a #GstSample or NULL when the appsink is
* stopped, EOS, or preroll object is GAP event.
* Call gst_sample_unref() after usage.
*/
GstSample *
@ -1898,7 +1906,15 @@ gst_app_sink_pull_object (GstAppSink * appsink)
* This function blocks until a preroll sample or EOS is received, the appsink
* element is set to the READY/NULL state, or the timeout expires.
*
* Returns: (transfer full) (nullable): a #GstSample or NULL when the appsink is stopped or EOS or the timeout expires.
* As of 1.24, gst_app_sink_try_pull_preroll() will be unblocked if the appsink
* is prerolled via GAP event, then this function will return %NULL.
* And if #GstBufferList support was enabled via
* gst_app_sink_set_buffer_list_support(), returned #GstSample will contain
* #GstBufferList instead of #GstBuffer when the preroll object is
* #GstBufferList
*
* Returns: (transfer full) (nullable): a #GstSample or NULL when the appsink is
* stopped, EOS, or preroll object is GAP event or the timeout expires.
* Call gst_sample_unref() after usage.
*
* Since: 1.10
@ -1928,7 +1944,7 @@ gst_app_sink_try_pull_preroll (GstAppSink * appsink, GstClockTime timeout)
if (!priv->started)
goto not_started;
if (priv->preroll_buffer != NULL)
if (priv->preroll_object != NULL)
break;
if (priv->is_eos)
@ -1945,11 +1961,31 @@ gst_app_sink_try_pull_preroll (GstAppSink * appsink, GstClockTime timeout)
}
priv->wait_status &= ~APP_WAITING;
}
sample =
gst_sample_new (priv->preroll_buffer, priv->preroll_caps,
&priv->preroll_segment, NULL);
gst_buffer_replace (&priv->preroll_buffer, NULL);
GST_DEBUG_OBJECT (appsink, "we have the preroll sample %p", sample);
if (GST_IS_BUFFER (priv->preroll_object)) {
sample =
gst_sample_new (GST_BUFFER_CAST (priv->preroll_object),
priv->preroll_caps, &priv->preroll_segment, NULL);
} else if (GST_IS_BUFFER_LIST (priv->preroll_object)) {
GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (priv->preroll_object);
sample = gst_sample_new (NULL, priv->preroll_caps, &priv->preroll_segment,
NULL);
if (appsink->priv->buffer_lists_supported) {
gst_sample_set_buffer_list (sample, buffer_list);
} else {
gst_sample_set_buffer (sample, gst_buffer_list_get (buffer_list, 0));
}
}
if (sample) {
GST_DEBUG_OBJECT (appsink, "we have the preroll sample %p", sample);
} else {
GST_DEBUG_OBJECT (appsink, "preroll object is not buffer or buffer list");
}
gst_clear_mini_object (&priv->preroll_object);
g_mutex_unlock (&priv->mutex);
return sample;
@ -2066,7 +2102,7 @@ gst_app_sink_try_pull_object (GstAppSink * appsink, GstClockTime timeout)
priv = appsink->priv;
g_mutex_lock (&priv->mutex);
gst_buffer_replace (&priv->preroll_buffer, NULL);
gst_clear_mini_object (&priv->preroll_object);
while (TRUE) {
GST_DEBUG_OBJECT (appsink, "trying to grab an object");

View file

@ -550,6 +550,66 @@ GST_START_TEST (test_pull_preroll)
GST_END_TEST;
GST_START_TEST (test_pull_preroll_with_gap)
{
GstElement *sink = NULL;
GstSample *pulled_preroll = NULL;
GstEvent *gap;
sink = setup_appsink ();
ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
gap = gst_event_new_gap (0, GST_SECOND);
fail_unless (gst_pad_push_event (mysrcpad, gap));
pulled_preroll = gst_app_sink_pull_preroll (GST_APP_SINK (sink));
fail_if (pulled_preroll);
fail_if (gst_app_sink_try_pull_preroll (GST_APP_SINK (sink), 0));
ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
cleanup_appsink (sink);
}
GST_END_TEST;
GST_START_TEST (test_pull_preroll_with_buffer_list_support)
{
GstElement *sink = NULL;
GstSample *pulled_preroll = NULL;
GstBufferList *list, *list_in_sample;
GstBuffer *buffer;
sink = setup_appsink ();
g_object_set (sink, "buffer-list", TRUE, NULL);
ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
list = create_buffer_list ();
fail_unless (gst_pad_push_list (mysrcpad, list) == GST_FLOW_OK);
pulled_preroll = gst_app_sink_pull_preroll (GST_APP_SINK (sink));
fail_unless (pulled_preroll);
buffer = gst_sample_get_buffer (pulled_preroll);
fail_if (buffer);
list_in_sample = gst_sample_get_buffer_list (pulled_preroll);
fail_unless (GST_IS_BUFFER_LIST (list_in_sample));
/* unmodified buffer list is expected */
fail_unless (list == list_in_sample);
gst_sample_unref (pulled_preroll);
ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
cleanup_appsink (sink);
}
GST_END_TEST;
GST_START_TEST (test_do_not_care_preroll)
{
GstElement *sink = NULL;
@ -1261,6 +1321,8 @@ appsink_suite (void)
tcase_add_test (tc_chain, test_pull_with_timeout);
tcase_add_test (tc_chain, test_query_drain);
tcase_add_test (tc_chain, test_pull_preroll);
tcase_add_test (tc_chain, test_pull_preroll_with_gap);
tcase_add_test (tc_chain, test_pull_preroll_with_buffer_list_support);
tcase_add_test (tc_chain, test_do_not_care_preroll);
tcase_add_test (tc_chain, test_pull_sample_refcounts);
tcase_add_test (tc_chain, test_event_callback);