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
This commit is contained in:
Thiago Santos 2016-02-18 10:57:51 -03:00
parent a8862ac440
commit 86d21e9455
2 changed files with 212 additions and 9 deletions

View file

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

View file

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