From 5797fa09afc61581ff94f927428afb652e271369 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Tue, 4 Apr 2023 17:50:39 -0400 Subject: [PATCH] codectimestamper: Implement QUERY_CAPS support This is required to ensure that downstream restrcitions are also propagated upstream. Part-of: --- .../codectimestamper/gstcodectimestamper.c | 89 +++++++++++++++++-- .../codectimestamper/gstcodectimestamper.h | 3 + .../h264_propagate_caps.validatetest | 9 ++ .../flow-expectations/log-sink-sink-expected | 5 ++ .../tests/validate/meson.build | 1 + 5 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 subprojects/gst-plugins-bad/tests/validate/codectimestamper/h264_propagate_caps.validatetest create mode 100644 subprojects/gst-plugins-bad/tests/validate/codectimestamper/h264_propagate_caps/flow-expectations/log-sink-sink-expected diff --git a/subprojects/gst-plugins-bad/gst/codectimestamper/gstcodectimestamper.c b/subprojects/gst-plugins-bad/gst/codectimestamper/gstcodectimestamper.c index 396ad26fe4..c30f88d5c3 100644 --- a/subprojects/gst-plugins-bad/gst/codectimestamper/gstcodectimestamper.c +++ b/subprojects/gst-plugins-bad/gst/codectimestamper/gstcodectimestamper.c @@ -76,13 +76,16 @@ static GstFlowReturn gst_codec_timestamper_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer); static gboolean gst_codec_timestamper_sink_event (GstPad * pad, GstObject * parent, GstEvent * event); +static gboolean gst_codec_timestamper_sink_query (GstPad * pad, + GstObject * parent, GstQuery * query); static gboolean gst_codec_timestamper_src_query (GstPad * pad, GstObject * parent, GstQuery * query); -static GstStateChangeReturn -gst_codec_timestamper_change_state (GstElement * element, - GstStateChange transition); -static void -gst_codec_timestamper_clear_frame (GstCodecTimestamperFrame * frame); +static GstCaps *gst_timestamper_get_caps (GstCodecTimestamper * self, + GstCaps * filter); +static GstStateChangeReturn gst_codec_timestamper_change_state (GstElement * + element, GstStateChange transition); +static void gst_codec_timestamper_clear_frame (GstCodecTimestamperFrame * + frame); static void gst_codec_timestamper_reset (GstCodecTimestamper * self); static void gst_codec_timestamper_drain (GstCodecTimestamper * self); @@ -142,6 +145,9 @@ gst_codec_timestamper_class_init (GstCodecTimestamperClass * klass) element_class->change_state = GST_DEBUG_FUNCPTR (gst_codec_timestamper_change_state); + /* Default implementation is correct for both H264 and H265 */ + klass->get_sink_caps = gst_timestamper_get_caps; + GST_DEBUG_CATEGORY_INIT (gst_codec_timestamper_debug, "codectimestamper", 0, "codectimestamper"); @@ -169,6 +175,9 @@ gst_codec_timestamper_init (GstCodecTimestamper * self, GST_DEBUG_FUNCPTR (gst_codec_timestamper_chain)); gst_pad_set_event_function (self->sinkpad, GST_DEBUG_FUNCPTR (gst_codec_timestamper_sink_event)); + gst_pad_set_query_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_codec_timestamper_sink_query)); + GST_PAD_SET_PROXY_SCHEDULING (self->sinkpad); GST_PAD_SET_ACCEPT_INTERSECT (self->sinkpad); GST_PAD_SET_ACCEPT_TEMPLATE (self->sinkpad); @@ -582,6 +591,76 @@ gst_codec_timestamper_chain (GstPad * pad, GstObject * parent, return gst_codec_timestamper_process_output_frame (self); } +static GstCaps * +gst_timestamper_get_caps (GstCodecTimestamper * self, GstCaps * filter) +{ + GstCaps *peercaps, *templ; + GstCaps *res, *tmp, *pcopy; + + templ = gst_pad_get_pad_template_caps (self->sinkpad); + if (filter) { + GstCaps *fcopy = gst_caps_copy (filter); + + peercaps = gst_pad_peer_query_caps (self->srcpad, fcopy); + gst_caps_unref (fcopy); + } else { + peercaps = gst_pad_peer_query_caps (self->srcpad, NULL); + } + + pcopy = gst_caps_copy (peercaps); + + res = gst_caps_intersect_full (pcopy, templ, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (pcopy); + gst_caps_unref (templ); + + if (filter) { + GstCaps *tmp = gst_caps_intersect_full (res, filter, + GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (res); + res = tmp; + } + + /* Try if we can put the downstream caps first */ + pcopy = gst_caps_copy (peercaps); + tmp = gst_caps_intersect_full (pcopy, res, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (pcopy); + if (!gst_caps_is_empty (tmp)) + res = gst_caps_merge (tmp, res); + else + gst_caps_unref (tmp); + + gst_caps_unref (peercaps); + return res; +} + +static gboolean +gst_codec_timestamper_sink_query (GstPad * pad, GstObject * parent, + GstQuery * query) +{ + GstCodecTimestamper *self = GST_CODEC_TIMESTAMPER (parent); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS:{ + GstCaps *caps, *filter; + GstCodecTimestamperClass *klass = GST_CODEC_TIMESTAMPER_GET_CLASS (self); + + gst_query_parse_caps (query, &filter); + g_assert (klass->get_sink_caps); + caps = klass->get_sink_caps (self, filter); + GST_LOG_OBJECT (self, "sink getcaps returning caps %" GST_PTR_FORMAT, + caps); + gst_query_set_caps_result (query, caps); + gst_caps_unref (caps); + + return TRUE; + } + default: + break; + } + + return gst_pad_query_default (pad, parent, query); +} + static gboolean gst_codec_timestamper_src_query (GstPad * pad, GstObject * parent, GstQuery * query) diff --git a/subprojects/gst-plugins-bad/gst/codectimestamper/gstcodectimestamper.h b/subprojects/gst-plugins-bad/gst/codectimestamper/gstcodectimestamper.h index ac9d50bb37..1cb6af1e89 100644 --- a/subprojects/gst-plugins-bad/gst/codectimestamper/gstcodectimestamper.h +++ b/subprojects/gst-plugins-bad/gst/codectimestamper/gstcodectimestamper.h @@ -54,6 +54,9 @@ struct _GstCodecTimestamperClass gboolean (*set_caps) (GstCodecTimestamper * timestamper, GstCaps * caps); + GstCaps * (*get_sink_caps) (GstCodecTimestamper * timestamper, + GstCaps * filter); + GstFlowReturn (*handle_buffer) (GstCodecTimestamper * timestamper, GstBuffer * buffer); }; diff --git a/subprojects/gst-plugins-bad/tests/validate/codectimestamper/h264_propagate_caps.validatetest b/subprojects/gst-plugins-bad/tests/validate/codectimestamper/h264_propagate_caps.validatetest new file mode 100644 index 0000000000..96ef6866f2 --- /dev/null +++ b/subprojects/gst-plugins-bad/tests/validate/codectimestamper/h264_propagate_caps.validatetest @@ -0,0 +1,9 @@ +meta, + args = { + "videotestsrc num-buffers=1 ! video/x-raw,width=1920,height=1080,format=I420 ! videoscale ! x264enc tune=zerolatency ! h264timestamper ! video/x-h264,width=50,height=50 ! fakesink name=sink", + }, + configs = { + "$(validateflow), pad=sink:sink, buffers-checksum=as-id, caps-properties={width, height}, ignored-event-types={tag}", + } + + diff --git a/subprojects/gst-plugins-bad/tests/validate/codectimestamper/h264_propagate_caps/flow-expectations/log-sink-sink-expected b/subprojects/gst-plugins-bad/tests/validate/codectimestamper/h264_propagate_caps/flow-expectations/log-sink-sink-expected new file mode 100644 index 0000000000..e031526b3a --- /dev/null +++ b/subprojects/gst-plugins-bad/tests/validate/codectimestamper/h264_propagate_caps/flow-expectations/log-sink-sink-expected @@ -0,0 +1,5 @@ +event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE, group-id=(uint)1; +event caps: video/x-h264, height=(int)50, width=(int)50; +event segment: format=TIME, start=1000:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=1000:00:00.000000000 +buffer: content-id=0, dts=1000:00:00.000000000, pts=1000:00:00.000000000, dur=0:00:00.033333333, flags=discont marker +event eos: (no structure) diff --git a/subprojects/gst-plugins-bad/tests/validate/meson.build b/subprojects/gst-plugins-bad/tests/validate/meson.build index 2474376dc0..b0c826f846 100644 --- a/subprojects/gst-plugins-bad/tests/validate/meson.build +++ b/subprojects/gst-plugins-bad/tests/validate/meson.build @@ -6,6 +6,7 @@ endif tests = [ {'path': 'opencv/cvtracker'}, {'path': 'testsrcbin/caps_spec'}, + {'path': 'codectimestamper/h264_propagate_caps'}, {'path': 'wpe/load_bytes_first', 'skip': not building_wpe}, ]