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; }