videoaggregator: properly handle interlace-mode restrictions
authorThiago Santos <thiagoss@osg.samsung.com>
Mon, 28 Mar 2016 18:44:27 +0000 (15:44 -0300)
committerThiago Santos <thiagoss@osg.samsung.com>
Mon, 18 Apr 2016 16:46:48 +0000 (13:46 -0300)
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
tests/check/elements/compositor.c

index f9232e8..b7161ba 100644 (file)
@@ -820,6 +820,26 @@ done:
 }
 
 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) {
index 6e7fe22..258d1d9 100644 (file)
@@ -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);