From 86d21e94553ad72f8fb3e0281555663539bea921 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Thu, 18 Feb 2016 10:57:51 -0300 Subject: [PATCH] videoaggregator: fix caps queries to allow proper renegotiation When caps are already negotiated it should be possible to select formats other than the one that was negotiated. If downstream allows alpha video caps and it has already negotiated to a non-alpha format, caps queries should still return the alpha caps as a possible format as caps renegotiation can happen. Includes tests (for compositor) to check that caps queries done after a caps has been negotiated returns complete results https://bugzilla.gnome.org/show_bug.cgi?id=757610 --- gst-libs/gst/video/gstvideoaggregator.c | 10 +- tests/check/elements/compositor.c | 211 ++++++++++++++++++++++++ 2 files changed, 212 insertions(+), 9 deletions(-) diff --git a/gst-libs/gst/video/gstvideoaggregator.c b/gst-libs/gst/video/gstvideoaggregator.c index 2b0c87630e..1bf6ea0b20 100644 --- a/gst-libs/gst/video/gstvideoaggregator.c +++ b/gst-libs/gst/video/gstvideoaggregator.c @@ -892,15 +892,7 @@ gst_videoaggregator_pad_sink_getcaps (GstPad * pad, GstVideoAggregator * vagg, GST_DEBUG_OBJECT (pad, "Get caps with filter: %" GST_PTR_FORMAT, filter); - srccaps = gst_pad_get_current_caps (srcpad); - if (srccaps == NULL) { - srccaps = gst_pad_peer_query_caps (srcpad, template_caps); - GST_DEBUG_OBJECT (pad, "No output caps, using possible formats: %" - GST_PTR_FORMAT, srccaps); - } else { - GST_DEBUG_OBJECT (pad, "Using output caps: %" GST_PTR_FORMAT, srccaps); - } - + srccaps = gst_pad_peer_query_caps (srcpad, template_caps); srccaps = gst_caps_make_writable (srccaps); has_alpha = gst_videoaggregator_caps_has_alpha (srccaps); diff --git a/tests/check/elements/compositor.c b/tests/check/elements/compositor.c index 77c65cfed1..cf50e15321 100644 --- a/tests/check/elements/compositor.c +++ b/tests/check/elements/compositor.c @@ -245,6 +245,216 @@ GST_START_TEST (test_event) GST_END_TEST; +static GstBuffer * +create_video_buffer (GstCaps * caps, gint ts_in_seconds) +{ + GstVideoInfo info; + guint size; + GstBuffer *buf; + GstMapInfo mapinfo; + + fail_unless (gst_video_info_from_caps (&info, caps)); + + size = GST_VIDEO_INFO_WIDTH (&info) * GST_VIDEO_INFO_HEIGHT (&info); + + switch (GST_VIDEO_INFO_FORMAT (&info)) { + case GST_VIDEO_FORMAT_RGB: + size *= 3; + break; + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_ARGB: + size *= 4; + break; + case GST_VIDEO_FORMAT_I420: + size *= 2; + break; + default: + fail ("Unsupported test format"); + } + + buf = gst_buffer_new_and_alloc (size); + /* write something to avoid uninitialized error issues (valgrind) */ + gst_buffer_map (buf, &mapinfo, GST_MAP_WRITE); + memset (mapinfo.data, 0, mapinfo.size); + gst_buffer_unmap (buf, &mapinfo); + + GST_BUFFER_PTS (buf) = ts_in_seconds * GST_SECOND; + GST_BUFFER_DURATION (buf) = GST_SECOND; + return buf; +} + +#define MODE_ALL 1 +#define MODE_NON_ALPHA 2 + +/* mostly copied from videoaggregator */ +static inline GstCaps * +_get_non_alpha_caps_from_caps (GstCaps * caps) +{ + GstCaps *result; + guint i, size; + + size = gst_caps_get_size (caps); + result = gst_caps_new_empty (); + for (i = 0; i < size; i++) { + GstStructure *s = gst_caps_get_structure (caps, i); + const GValue *formats = gst_structure_get_value (s, "format"); + GValue new_formats = { 0, }; + gboolean has_format = FALSE; + + /* FIXME what to do if formats are missing? */ + if (formats) { + const GstVideoFormatInfo *info; + + if (GST_VALUE_HOLDS_LIST (formats)) { + guint list_size = gst_value_list_get_size (formats); + guint index; + + g_value_init (&new_formats, GST_TYPE_LIST); + + for (index = 0; index < list_size; index++) { + const GValue *list_item = gst_value_list_get_value (formats, index); + + info = + gst_video_format_get_info (gst_video_format_from_string + (g_value_get_string (list_item))); + if (!GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info)) { + has_format = TRUE; + gst_value_list_append_value (&new_formats, list_item); + } + } + + } else if (G_VALUE_HOLDS_STRING (formats)) { + info = + gst_video_format_get_info (gst_video_format_from_string + (g_value_get_string (formats))); + if (!GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info)) { + has_format = TRUE; + gst_value_init_and_copy (&new_formats, formats); + } + + } else { + g_assert_not_reached (); + GST_WARNING ("Unexpected type for video 'format' field: %s", + G_VALUE_TYPE_NAME (formats)); + } + + if (has_format) { + s = gst_structure_copy (s); + gst_structure_take_value (s, "format", &new_formats); + gst_caps_append_structure (result, s); + } + + } + } + + return result; +} + +static void +run_late_caps_query_test (GstCaps * input_caps, GstCaps * output_allowed_caps, + gint expected_caps_mode) +{ + GstElement *compositor, *capsfilter, *sink; + GstElement *pipeline; + gboolean res; + GstStateChangeReturn state_res; + GstPad *srcpad1, *srcpad2; + GstPad *sinkpad1, *sinkpad2; + GstSegment segment; + GstCaps *caps, *all_caps, *non_alpha_caps; + + all_caps = + gst_caps_from_string (GST_VIDEO_CAPS_MAKE + (" { AYUV, BGRA, ARGB, RGBA, ABGR, Y444, Y42B, YUY2, UYVY, " + " YVYU, I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, " + " RGBx, BGRx } ")); + non_alpha_caps = + gst_caps_from_string (GST_VIDEO_CAPS_MAKE + (" { Y444, Y42B, YUY2, UYVY, " + " YVYU, I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, " + " RGBx, BGRx } ")); + + compositor = gst_element_factory_make ("compositor", "compositor"); + capsfilter = gst_element_factory_make ("capsfilter", "out-cf"); + sink = gst_element_factory_make ("fakesink", "sink"); + pipeline = gst_pipeline_new ("test-pipeline"); + + gst_bin_add_many (GST_BIN (pipeline), compositor, capsfilter, sink, NULL); + res = gst_element_link (compositor, capsfilter); + fail_unless (res == TRUE, NULL); + res = gst_element_link (capsfilter, sink); + fail_unless (res == TRUE, NULL); + + sinkpad1 = gst_element_get_request_pad (compositor, "sink_%u"); + srcpad1 = gst_pad_new ("src1", GST_PAD_SRC); + fail_unless (gst_pad_link (srcpad1, sinkpad1) == GST_PAD_LINK_OK); + gst_pad_set_active (srcpad1, TRUE); + + state_res = gst_element_set_state (pipeline, GST_STATE_PLAYING); + fail_if (state_res == GST_STATE_CHANGE_FAILURE); + + if (output_allowed_caps) + g_object_set (capsfilter, "caps", output_allowed_caps, NULL); + + gst_segment_init (&segment, GST_FORMAT_TIME); + fail_unless (gst_pad_push_event (srcpad1, + gst_event_new_stream_start ("test-1"))); + fail_unless (gst_pad_push_event (srcpad1, gst_event_new_caps (input_caps))); + fail_unless (gst_pad_push_event (srcpad1, gst_event_new_segment (&segment))); + fail_unless (gst_pad_push (srcpad1, + create_video_buffer (input_caps, 0)) == GST_FLOW_OK); + fail_unless (gst_pad_push (srcpad1, + create_video_buffer (input_caps, 1)) == GST_FLOW_OK); + + /* now comes the second pad */ + sinkpad2 = gst_element_get_request_pad (compositor, "sink_%u"); + srcpad2 = gst_pad_new ("src2", GST_PAD_SRC); + fail_unless (gst_pad_link (srcpad2, sinkpad2) == GST_PAD_LINK_OK); + gst_pad_set_active (srcpad2, TRUE); + fail_unless (gst_pad_push_event (srcpad2, + gst_event_new_stream_start ("test-2"))); + + caps = gst_pad_peer_query_caps (srcpad2, NULL); + fail_unless (gst_caps_is_equal (caps, + expected_caps_mode == MODE_ALL ? all_caps : non_alpha_caps)); + gst_caps_unref (caps); + + gst_pad_set_active (srcpad1, FALSE); + gst_pad_set_active (srcpad2, FALSE); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_release_request_pad (compositor, sinkpad1); + gst_element_release_request_pad (compositor, sinkpad2); + gst_object_unref (sinkpad1); + gst_object_unref (sinkpad2); + gst_object_unref (pipeline); + gst_object_unref (srcpad1); + gst_object_unref (srcpad2); + gst_caps_unref (all_caps); + gst_caps_unref (non_alpha_caps); +} + +GST_START_TEST (test_late_caps_query) +{ + GstCaps *rgb_caps; + GstCaps *non_alpha_caps; + + rgb_caps = gst_caps_from_string ("video/x-raw, format=(string)RGB, " + "width=(int)100, height=(int)100, framerate=(fraction)1/1"); + + non_alpha_caps = gst_caps_from_string ("video/x-raw, format=(string)RGB"); + + /* check that a 2nd pad that is added late to compositor will be able to + * negotiate to formats that depend only on downstream caps and not on what + * the other pads have already negotiated */ + run_late_caps_query_test (rgb_caps, NULL, MODE_ALL); + run_late_caps_query_test (rgb_caps, non_alpha_caps, MODE_NON_ALPHA); + + gst_caps_unref (non_alpha_caps); + gst_caps_unref (rgb_caps); +} + +GST_END_TEST; + static guint play_count = 0; static GstEvent *play_seek_event = NULL; @@ -1660,6 +1870,7 @@ compositor_suite (void) suite_add_tcase (s, tc_chain); tcase_add_test (tc_chain, test_caps); tcase_add_test (tc_chain, test_event); + tcase_add_test (tc_chain, test_late_caps_query); tcase_add_test (tc_chain, test_play_twice); tcase_add_test (tc_chain, test_play_twice_then_add_and_play_again); tcase_add_test (tc_chain, test_add_pad);