From cf6a36721bd9ab4a12eab664c99e4af7c27fc059 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Mon, 28 Mar 2016 15:44:27 -0300 Subject: [PATCH] videoaggregator: properly handle interlace-mode restrictions videoaggregator can't handle interlace-mode changes so it must always restrict itself to the first interlacing mode it receives. Tests included https://bugzilla.gnome.org/show_bug.cgi?id=754495 --- gst-libs/gst/video/gstvideoaggregator.c | 59 +++++++-- tests/check/elements/compositor.c | 167 +++++++++++++++++++++++- 2 files changed, 217 insertions(+), 9 deletions(-) diff --git a/gst-libs/gst/video/gstvideoaggregator.c b/gst-libs/gst/video/gstvideoaggregator.c index f9232e8dda..b7161ba182 100644 --- a/gst-libs/gst/video/gstvideoaggregator.c +++ b/gst-libs/gst/video/gstvideoaggregator.c @@ -819,6 +819,26 @@ done: return ret; } +static gboolean +gst_videoaggregator_get_sinkpads_interlace_mode (GstVideoAggregator * vagg, + GstVideoAggregatorPad * skip_pad, GstVideoInterlaceMode * mode) +{ + GList *walk; + + for (walk = GST_ELEMENT (vagg)->sinkpads; walk; walk = g_list_next (walk)) { + GstVideoAggregatorPad *vaggpad = walk->data; + + if (skip_pad && vaggpad == skip_pad) + continue; + if (vaggpad->info.finfo + && GST_VIDEO_INFO_FORMAT (&vaggpad->info) != GST_VIDEO_FORMAT_UNKNOWN) { + *mode = GST_VIDEO_INFO_INTERLACE_MODE (&vaggpad->info); + return TRUE; + } + } + return FALSE; +} + static gboolean gst_videoaggregator_pad_sink_setcaps (GstPad * pad, GstObject * parent, GstCaps * caps) @@ -839,14 +859,28 @@ gst_videoaggregator_pad_sink_setcaps (GstPad * pad, GstObject * parent, } GST_VIDEO_AGGREGATOR_LOCK (vagg); - if (GST_VIDEO_INFO_FORMAT (&vagg->info) != GST_VIDEO_FORMAT_UNKNOWN) { - if (GST_VIDEO_INFO_INTERLACE_MODE (&vagg->info) != - GST_VIDEO_INFO_INTERLACE_MODE (&info)) { - GST_ERROR_OBJECT (pad, - "got input caps %" GST_PTR_FORMAT ", but " "current caps are %" - GST_PTR_FORMAT, caps, vagg->priv->current_caps); - GST_VIDEO_AGGREGATOR_UNLOCK (vagg); - return FALSE; + { + GstVideoInterlaceMode pads_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; + gboolean has_mode = FALSE; + + /* get the current output setting or fallback to other pads settings */ + if (GST_VIDEO_INFO_FORMAT (&vagg->info) != GST_VIDEO_FORMAT_UNKNOWN) { + pads_mode = GST_VIDEO_INFO_INTERLACE_MODE (&vagg->info); + has_mode = TRUE; + } else { + has_mode = + gst_videoaggregator_get_sinkpads_interlace_mode (vagg, vaggpad, + &pads_mode); + } + + if (has_mode) { + if (pads_mode != GST_VIDEO_INFO_INTERLACE_MODE (&info)) { + GST_ERROR_OBJECT (pad, + "got input caps %" GST_PTR_FORMAT ", but current caps are %" + GST_PTR_FORMAT, caps, vagg->priv->current_caps); + GST_VIDEO_AGGREGATOR_UNLOCK (vagg); + return FALSE; + } } } @@ -918,6 +952,8 @@ gst_videoaggregator_pad_sink_getcaps (GstPad * pad, GstVideoAggregator * vagg, GstAggregator *agg = GST_AGGREGATOR (vagg); GstPad *srcpad = GST_PAD (agg->srcpad); gboolean has_alpha; + GstVideoInterlaceMode interlace_mode; + gboolean has_interlace_mode; template_caps = gst_pad_get_pad_template_caps (srcpad); @@ -927,6 +963,10 @@ gst_videoaggregator_pad_sink_getcaps (GstPad * pad, GstVideoAggregator * vagg, srccaps = gst_caps_make_writable (srccaps); has_alpha = gst_videoaggregator_caps_has_alpha (srccaps); + has_interlace_mode = + gst_videoaggregator_get_sinkpads_interlace_mode (vagg, NULL, + &interlace_mode); + n = gst_caps_get_size (srccaps); for (i = 0; i < n; i++) { s = gst_caps_get_structure (srccaps, i); @@ -936,6 +976,9 @@ gst_videoaggregator_pad_sink_getcaps (GstPad * pad, GstVideoAggregator * vagg, gst_structure_remove_fields (s, "colorimetry", "chroma-site", "format", "pixel-aspect-ratio", NULL); + if (has_interlace_mode) + gst_structure_set (s, "interlace-mode", G_TYPE_STRING, + gst_video_interlace_mode_to_string (interlace_mode), NULL); } if (filter) { diff --git a/tests/check/elements/compositor.c b/tests/check/elements/compositor.c index 6e7fe22d73..258d1d96cd 100644 --- a/tests/check/elements/compositor.c +++ b/tests/check/elements/compositor.c @@ -355,6 +355,15 @@ GST_START_TEST (test_caps_query) gst_caps_unref (caps); gst_caps_unref (restriction_caps); + /* check that compositor proxies downstream interlaced-mode */ + restriction_caps = + gst_caps_from_string ("video/x-raw, interlace-mode=(string)interleaved"); + g_object_set (capsfilter, "caps", restriction_caps, NULL); + caps = gst_pad_query_caps (sinkpad, NULL); + fail_unless (gst_caps_is_subset (caps, restriction_caps)); + gst_caps_unref (caps); + gst_caps_unref (restriction_caps); + gst_element_set_state (pipeline, GST_STATE_NULL); gst_element_release_request_pad (compositor, sinkpad); gst_object_unref (sinkpad); @@ -365,6 +374,82 @@ GST_START_TEST (test_caps_query) GST_END_TEST; + +GST_START_TEST (test_caps_query_interlaced) +{ + GstElement *compositor, *sink; + GstElement *pipeline; + gboolean res; + GstStateChangeReturn state_res; + GstPad *sinkpad; + GstCaps *caps; + GstCaps *caps_mixed, *caps_progressive, *caps_interleaved; + GstEvent *caps_event; + + caps_interleaved = + gst_caps_from_string ("video/x-raw, interlace-mode=interleaved"); + caps_mixed = gst_caps_from_string ("video/x-raw, interlace-mode=mixed"); + caps_progressive = + gst_caps_from_string ("video/x-raw, interlace-mode=progressive"); + + /* initial setup */ + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + pipeline = gst_pipeline_new ("test-pipeline"); + + gst_bin_add_many (GST_BIN (pipeline), compositor, sink, NULL); + res = gst_element_link (compositor, sink); + fail_unless (res == TRUE, NULL); + sinkpad = gst_element_get_request_pad (compositor, "sink_%u"); + + state_res = gst_element_set_state (pipeline, GST_STATE_PLAYING); + fail_if (state_res == GST_STATE_CHANGE_FAILURE); + + /* try an unrestricted caps query, should be compatible with all formats */ + caps = gst_pad_query_caps (sinkpad, NULL); + fail_unless (gst_caps_can_intersect (caps, caps_interleaved)); + fail_unless (gst_caps_can_intersect (caps, caps_progressive)); + fail_unless (gst_caps_can_intersect (caps, caps_mixed)); + gst_caps_unref (caps); + + /* now set caps on the pad, it should restrict the interlaced-mode for + * future caps */ + caps = gst_caps_from_string ("video/x-raw, width=100, height=100, " + "format=RGB, framerate=1/1, interlace-mode=progressive"); + caps_event = gst_event_new_caps (caps); + gst_caps_unref (caps); + fail_unless (gst_pad_send_event (sinkpad, caps_event)); + + /* now recheck the interlace-mode */ + gst_object_unref (sinkpad); + sinkpad = gst_element_get_request_pad (compositor, "sink_%u"); + caps = gst_pad_query_caps (sinkpad, NULL); + fail_if (gst_caps_can_intersect (caps, caps_interleaved)); + fail_unless (gst_caps_can_intersect (caps, caps_progressive)); + fail_if (gst_caps_can_intersect (caps, caps_mixed)); + gst_object_unref (sinkpad); + gst_caps_unref (caps); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + gst_caps_unref (caps_interleaved); + gst_caps_unref (caps_mixed); + gst_caps_unref (caps_progressive); +} + +GST_END_TEST; + +static void +add_interlaced_mode_to_caps (GstCaps * caps, const gchar * mode) +{ + GstStructure *s; + + for (gint i = 0; i < gst_caps_get_size (caps); i++) { + s = gst_caps_get_structure (caps, i); + gst_structure_set (s, "interlace-mode", G_TYPE_STRING, mode, NULL); + } +} + #define MODE_ALL 1 #define MODE_NON_ALPHA 2 @@ -384,6 +469,11 @@ run_late_caps_query_test (GstCaps * input_caps, GstCaps * output_allowed_caps, all_caps = _compositor_get_all_supported_caps (); non_alpha_caps = _compositor_get_non_alpha_supported_caps (); + /* add progressive mode as it is what is used in the test, otherwise + * is_equal checks would fail */ + add_interlaced_mode_to_caps (all_caps, "progressive"); + add_interlaced_mode_to_caps (non_alpha_caps, "progressive"); + compositor = gst_element_factory_make ("compositor", "compositor"); capsfilter = gst_element_factory_make ("capsfilter", "out-cf"); sink = gst_element_factory_make ("fakesink", "sink"); @@ -450,7 +540,6 @@ GST_START_TEST (test_late_caps_query) 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 @@ -465,6 +554,80 @@ GST_START_TEST (test_late_caps_query) GST_END_TEST; +static void +run_late_caps_set_test (GstCaps * first_caps, GstCaps * expected_query_caps, + GstCaps * second_caps, gboolean accept_caps) +{ + GstElement *capsfilter_1; + GstElement *compositor; + GstElement *pipeline; + GstStateChangeReturn state_res; + GstPad *sinkpad_2; + GstCaps *caps; + GstEvent *caps_event; + GstBus *bus; + GstMessage *msg; + + pipeline = + gst_parse_launch ("videotestsrc num-buffers=10 ! capsfilter name=cf1 !" + " compositor name=c ! fakesink sync=true", NULL); + fail_unless (pipeline != NULL); + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + + compositor = gst_bin_get_by_name (GST_BIN (pipeline), "c"); + capsfilter_1 = gst_bin_get_by_name (GST_BIN (pipeline), "cf1"); + + g_object_set (capsfilter_1, "caps", first_caps, NULL); + + state_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); + fail_if (state_res == GST_STATE_CHANGE_FAILURE); + + /* wait for pipeline to get to paused */ + msg = + gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, + GST_MESSAGE_ASYNC_DONE); + fail_unless (msg != NULL); + fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ASYNC_DONE); + gst_message_unref (msg); + + /* try to set the second caps */ + sinkpad_2 = gst_element_get_request_pad (compositor, "sink_%u"); + caps = gst_pad_query_caps (sinkpad_2, NULL); + fail_unless (gst_caps_is_subset (expected_query_caps, caps)); + caps_event = gst_event_new_caps (second_caps); + fail_unless (gst_pad_send_event (sinkpad_2, caps_event) == accept_caps); + gst_caps_unref (caps); + gst_object_unref (sinkpad_2); + + gst_object_unref (bus); + gst_object_unref (compositor); + gst_object_unref (capsfilter_1); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_START_TEST (test_late_caps_different_interlaced) +{ + GstCaps *non_interlaced_caps; + GstCaps *interlaced_caps; + + non_interlaced_caps = + gst_caps_from_string ("video/x-raw, interlace-mode=progressive, " + "format=RGB, width=100, height=100, framerate=1/1"); + interlaced_caps = + gst_caps_from_string ("video/x-raw, interlace-mode=interleaved, " + "format=RGB, width=100, height=100, framerate=1/1"); + + run_late_caps_set_test (non_interlaced_caps, non_interlaced_caps, + interlaced_caps, FALSE); + + gst_caps_unref (non_interlaced_caps); + gst_caps_unref (interlaced_caps); +} + +GST_END_TEST; + static guint play_count = 0; static GstEvent *play_seek_event = NULL; @@ -1915,7 +2078,9 @@ compositor_suite (void) tcase_add_test (tc_chain, test_caps); tcase_add_test (tc_chain, test_event); tcase_add_test (tc_chain, test_caps_query); + tcase_add_test (tc_chain, test_caps_query_interlaced); tcase_add_test (tc_chain, test_late_caps_query); + tcase_add_test (tc_chain, test_late_caps_different_interlaced); 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);