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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4185>
This commit is contained in:
Shengqi Yu 2023-03-16 14:38:56 +08:00 committed by Sebastian Dröge
parent c798f01fae
commit 96a46e31c7
4 changed files with 179 additions and 15 deletions

View file

@ -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": [],

View file

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

View file

@ -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

View file

@ -23,6 +23,7 @@
#include <gst/check/gstcheck.h>
#include <gst/app/gstappsink.h>
#include <gst/video/video.h>
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;
}