From 96a46e31c7830b8d94b82e985ae06ce007220338 Mon Sep 17 00:00:00 2001 From: Shengqi Yu Date: Thu, 16 Mar 2023 14:38:56 +0800 Subject: [PATCH] appsink: add propose_allocation support Adding propose_allocation is to meet the requirement of Application to request buffers. Application sometimes need to create buffer pool and request buffers to maintain buffer management itself, and Gstreamer plugin import Application's buffers to use. So, add propose_allocation in appsink like waylandsink and kmssink etc. Part-of: --- .../docs/plugins/gst_plugins_cache.json | 10 +++ .../gst-libs/gst/app/gstappsink.c | 50 +++++++++++ .../gst-libs/gst/app/gstappsink.h | 45 ++++++---- .../tests/check/elements/appsink.c | 89 +++++++++++++++++++ 4 files changed, 179 insertions(+), 15 deletions(-) diff --git a/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json index f9ede63c06..36795634d4 100644 --- a/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json @@ -374,6 +374,16 @@ "return-type": "gboolean", "when": "last" }, + "propose-allocation": { + "args": [ + { + "name": "arg0", + "type": "GstQuery" + } + ], + "return-type": "gboolean", + "when": "last" + }, "pull-preroll": { "action": true, "args": [], diff --git a/subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c b/subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c index fb6ce0d857..bea0278cce 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c +++ b/subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.c @@ -155,6 +155,7 @@ enum SIGNAL_TRY_PULL_PREROLL, SIGNAL_TRY_PULL_SAMPLE, SIGNAL_TRY_PULL_OBJECT, + SIGNAL_PROPOSE_ALLOCATION, LAST_SIGNAL }; @@ -212,6 +213,8 @@ static GstFlowReturn gst_app_sink_render_list (GstBaseSink * psink, GstBufferList * list); static gboolean gst_app_sink_setcaps (GstBaseSink * sink, GstCaps * caps); static GstCaps *gst_app_sink_getcaps (GstBaseSink * psink, GstCaps * filter); +static gboolean gst_app_sink_propose_allocation (GstBaseSink * bsink, + GstQuery * query); static guint gst_app_sink_signals[LAST_SIGNAL] = { 0 }; @@ -335,6 +338,23 @@ gst_app_sink_class_init (GstAppSinkClass * klass) G_STRUCT_OFFSET (GstAppSinkClass, new_sample), NULL, NULL, NULL, GST_TYPE_FLOW_RETURN, 0, G_TYPE_NONE); + /** + * GstAppSink::propose-allocation: + * @appsink: the appsink element that emitted the signal + * @query: the allocation query + * + * Signal that a new propose_allocation query is available. + * + * This signal is emitted from the streaming thread and only when the + * "emit-signals" property is %TRUE. + * + * Since: 1.24 + */ + gst_app_sink_signals[SIGNAL_PROPOSE_ALLOCATION] = + g_signal_new ("propose-allocation", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstAppSinkClass, propose_allocation), + NULL, NULL, NULL, G_TYPE_BOOLEAN, 1, + GST_TYPE_QUERY | G_SIGNAL_TYPE_STATIC_SCOPE); /** * GstAppSink::new-serialized-event: * @appsink: the appsink element that emitted the signal @@ -539,6 +559,7 @@ gst_app_sink_class_init (GstAppSinkClass * klass) basesink_class->get_caps = gst_app_sink_getcaps; basesink_class->set_caps = gst_app_sink_setcaps; basesink_class->query = gst_app_sink_query; + basesink_class->propose_allocation = gst_app_sink_propose_allocation; klass->pull_preroll = gst_app_sink_pull_preroll; klass->pull_sample = gst_app_sink_pull_sample; @@ -2047,3 +2068,32 @@ gst_app_sink_uri_handler_init (gpointer g_iface, gpointer iface_data) iface->set_uri = gst_app_sink_uri_set_uri; } + +static gboolean +gst_app_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) +{ + gboolean ret = FALSE; + GstAppSink *appsink = GST_APP_SINK_CAST (bsink); + GstAppSinkPrivate *priv = appsink->priv; + Callbacks *callbacks = NULL; + gboolean emit; + + g_mutex_lock (&priv->mutex); + emit = priv->emit_signals; + if (priv->callbacks) + callbacks = callbacks_ref (priv->callbacks); + g_mutex_unlock (&priv->mutex); + + if (callbacks && callbacks->callbacks.propose_allocation) { + ret = + callbacks->callbacks.propose_allocation (appsink, query, + callbacks->user_data); + } else if (emit) { + g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_PROPOSE_ALLOCATION], 0, + query, &ret); + } + + g_clear_pointer (&callbacks, callbacks_unref); + + return ret; +} diff --git a/subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.h b/subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.h index 90e678f53f..050bb2d400 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.h +++ b/subprojects/gst-plugins-base/gst-libs/gst/app/gstappsink.h @@ -67,18 +67,25 @@ typedef struct _GstAppSinkPrivate GstAppSinkPrivate; * The callback should return %TRUE if the event has been handled, * %FALSE otherwise. * Since: 1.20 + * @propose_allocation: Called when the propose_allocation query is available. + * This callback is called from the streaming thread. + * The allocation query can be retrieved with + * gst_app_sink_propose_allocation() either from this callback + * or from any other thread. + * Since: 1.24 * * A set of callbacks that can be installed on the appsink with * gst_app_sink_set_callbacks(). */ typedef struct { - void (*eos) (GstAppSink *appsink, gpointer user_data); - GstFlowReturn (*new_preroll) (GstAppSink *appsink, gpointer user_data); - GstFlowReturn (*new_sample) (GstAppSink *appsink, gpointer user_data); - gboolean (*new_event) (GstAppSink *appsink, gpointer user_data); + void (*eos) (GstAppSink *appsink, gpointer user_data); + GstFlowReturn (*new_preroll) (GstAppSink *appsink, gpointer user_data); + GstFlowReturn (*new_sample) (GstAppSink *appsink, gpointer user_data); + gboolean (*new_event) (GstAppSink *appsink, gpointer user_data); + gboolean (*propose_allocation) (GstAppSink *appsink, GstQuery *query, gpointer user_data); /*< private >*/ - gpointer _gst_reserved[GST_PADDING - 1]; + gpointer _gst_reserved[GST_PADDING - 2]; } GstAppSinkCallbacks; struct _GstAppSink @@ -97,16 +104,16 @@ struct _GstAppSinkClass GstBaseSinkClass basesink_class; /* signals */ - void (*eos) (GstAppSink *appsink); - GstFlowReturn (*new_preroll) (GstAppSink *appsink); - GstFlowReturn (*new_sample) (GstAppSink *appsink); + void (*eos) (GstAppSink *appsink); + GstFlowReturn (*new_preroll) (GstAppSink *appsink); + GstFlowReturn (*new_sample) (GstAppSink *appsink); /* new_event is missing as we ran out padding */ /* actions */ - GstSample * (*pull_preroll) (GstAppSink *appsink); - GstSample * (*pull_sample) (GstAppSink *appsink); - GstSample * (*try_pull_preroll) (GstAppSink *appsink, GstClockTime timeout); - GstSample * (*try_pull_sample) (GstAppSink *appsink, GstClockTime timeout); + GstSample * (*pull_preroll) (GstAppSink *appsink); + GstSample * (*pull_sample) (GstAppSink *appsink); + GstSample * (*try_pull_preroll) (GstAppSink *appsink, GstClockTime timeout); + GstSample * (*try_pull_sample) (GstAppSink *appsink, GstClockTime timeout); /** * GstAppSinkClass::try_pull_object: * @@ -114,10 +121,18 @@ struct _GstAppSinkClass * * Since: 1.20 */ - GstMiniObject * (*try_pull_object) (GstAppSink *appsink, GstClockTime timeout); - + + GstMiniObject * (*try_pull_object) (GstAppSink *appsink, GstClockTime timeout); + /** + * GstAppSinkClass::propose_allocation: + * + * See #GstAppSink::propose-allocation: signal. + * + * Since: 1.24 + */ + gboolean (*propose_allocation) (GstAppSink *appsink, GstQuery *query); /*< private >*/ - gpointer _gst_reserved[GST_PADDING - 3]; + gpointer _gst_reserved[GST_PADDING - 4]; }; GST_APP_API diff --git a/subprojects/gst-plugins-base/tests/check/elements/appsink.c b/subprojects/gst-plugins-base/tests/check/elements/appsink.c index 2d3a599cae..f9ca290633 100644 --- a/subprojects/gst-plugins-base/tests/check/elements/appsink.c +++ b/subprojects/gst-plugins-base/tests/check/elements/appsink.c @@ -23,6 +23,7 @@ #include #include +#include gint global_testdata; @@ -1068,6 +1069,92 @@ GST_START_TEST (test_caps_before_flush_race_condition) GST_END_TEST; +static gboolean +propose_allocation_cb (GstAppSink * appsink, GstQuery * query, + gpointer callback_data) +{ + guint *allocation_query_count = callback_data; + *allocation_query_count += 1; + fail_unless (gst_query_is_writable (query)); + fail_unless (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION); + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + return TRUE; +} + +/* Verifies that the allocation query callback is called */ +GST_START_TEST (test_query_allocation_callback) +{ + GstElement *sink; + GstAppSinkCallbacks callbacks = { NULL }; + GstAppSink *app_sink; + GstQuery *query = NULL; + guint allocation_query_count = 0; + GstPad *sinkpad; + + sink = setup_appsink (); + app_sink = GST_APP_SINK (sink); + + sinkpad = gst_element_get_static_pad (sink, "sink"); + fail_unless (sinkpad); + + callbacks.propose_allocation = propose_allocation_cb; + gst_app_sink_set_callbacks (app_sink, &callbacks, &allocation_query_count, + NULL); + + query = gst_query_new_allocation (NULL, FALSE); + fail_unless (gst_pad_query (sinkpad, query)); + + fail_unless_equals_int (allocation_query_count, 1); + fail_unless (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, + NULL)); + + ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC); + + gst_object_unref (sinkpad); + gst_query_unref (query); + + GST_DEBUG ("cleaning up appsink"); + ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS); + cleanup_appsink (sink); +} + +GST_END_TEST; + +GST_START_TEST (test_query_allocation_signals) +{ + GstElement *sink; + GstQuery *query = NULL; + guint allocation_query_count = 0; + GstPad *sinkpad; + + sink = setup_appsink (); + + g_object_set (sink, "emit-signals", TRUE, NULL); + g_signal_connect (sink, "propose-allocation", + G_CALLBACK (propose_allocation_cb), &allocation_query_count); + + sinkpad = gst_element_get_static_pad (sink, "sink"); + fail_unless (sinkpad); + query = gst_query_new_allocation (NULL, FALSE); + fail_unless (gst_pad_query (sinkpad, query)); + + fail_unless_equals_int (allocation_query_count, 1); + fail_unless (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, + NULL)); + + ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC); + + gst_object_unref (sinkpad); + if (query) + gst_query_unref (query); + + GST_DEBUG ("cleaning up appsink"); + ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS); + cleanup_appsink (sink); +} + +GST_END_TEST; + static Suite * appsink_suite (void) { @@ -1094,6 +1181,8 @@ appsink_suite (void) tcase_add_test (tc_chain, test_event_paused); tcase_add_test (tc_chain, test_reverse_stepping); tcase_add_test (tc_chain, test_caps_before_flush_race_condition); + tcase_add_test (tc_chain, test_query_allocation_callback); + tcase_add_test (tc_chain, test_query_allocation_signals); return s; }