msdkdec: Fix to not trigger SFC scaling when vpp at downstream
authorMengkejiergeli Ba <mengkejiergeli.ba@intel.com>
Wed, 2 Mar 2022 01:15:12 +0000 (09:15 +0800)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Mon, 7 Mar 2022 07:26:40 +0000 (07:26 +0000)
When there is vpp scaling downstream, we need to make sure SFC is not
triggered because vpp may fall into passthrough mode which causes
the decoder negotiation to create src caps with vpp scaled width/height.

This patch includes bitstream's original size in first query with
downstream in gst_msdkdec_src_caps, which is the same for what we do for
color format in this query. This is to ensure SFC scaling starts to
work only when downstream directly asks for a different size instead of
through vpp.

Note that here SFC scaling follows the same behavior as msdkvpp:
if user only changes width or height, e.g. dec ! video/x-raw,width=xx !,
the height will be modified to the value which fits the original DAR.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1838>

subprojects/gst-plugins-bad/sys/msdk/gstmsdkdec.c

index 841c60a..291a42e 100644 (file)
@@ -612,10 +612,13 @@ gst_msdkdec_set_src_caps (GstMsdkDec * thiz, gboolean need_allocation)
   guint width, height;
   guint alloc_w, alloc_h;
   int out_width = 0, out_height = 0;
+  gint dar_n = -1, dar_d = -1;
   const gchar *format_str;
   GstStructure *outs = NULL;
   const gchar *out_format;
   GValue v_format = G_VALUE_INIT;
+  GValue v_width = G_VALUE_INIT;
+  GValue v_height = G_VALUE_INIT;
 
   /* use display width and display height in output state, which
    * will be used for caps negotiation */
@@ -638,53 +641,73 @@ gst_msdkdec_set_src_caps (GstMsdkDec * thiz, gboolean need_allocation)
   /* SFC is triggered (for AVC and HEVC) when default output format is not
    * accepted by downstream or when downstream requests for a smaller
    * resolution (i.e. SFC supports down-scaling)
-   * For SFC csc, need to do the query twice: the first time uses default
-   * color format to query peer pad, empty caps means default format is
-   * not accepted by downstream; then we need the second query to decide
-   * src caps color format and let SFC work. */
+   * Here we need to do the query twice: the first time uses default color
+   * format and bitstream's original size to query peer pad, empty caps
+   * means default format and/or size are not accepted by downstream;
+   * then we need the second query to decide src caps' color format and size,
+   * and let SFC work. */
   if (thiz->param.mfx.CodecId == MFX_CODEC_AVC ||
       thiz->param.mfx.CodecId == MFX_CODEC_HEVC) {
     temp_caps = gst_pad_query_caps (GST_VIDEO_DECODER (thiz)->srcpad, NULL);
     temp_caps = gst_caps_make_writable (temp_caps);
 
     g_value_init (&v_format, G_TYPE_STRING);
+    g_value_init (&v_width, G_TYPE_INT);
+    g_value_init (&v_height, G_TYPE_INT);
+
     g_value_set_string (&v_format, gst_video_format_to_string (format));
+    g_value_set_int (&v_width, width);
+    g_value_set_int (&v_height, height);
+
     gst_caps_set_value (temp_caps, "format", &v_format);
+    gst_caps_set_value (temp_caps, "width", &v_width);
+    gst_caps_set_value (temp_caps, "height", &v_height);
 
     if (gst_caps_is_empty (gst_pad_peer_query_caps (GST_VIDEO_DECODER
                 (thiz)->srcpad, temp_caps))) {
+      if (!gst_util_fraction_multiply (width, height,
+              GST_VIDEO_INFO_PAR_N (&thiz->input_state->info),
+              GST_VIDEO_INFO_PAR_D (&thiz->input_state->info),
+              &dar_n, &dar_d)) {
+        GST_ERROR_OBJECT (thiz, "Error to calculate the output scaled size");
+        gst_caps_unref (temp_caps);
+        return FALSE;
+      }
+
       allowed_caps =
           gst_pad_get_allowed_caps (GST_VIDEO_DECODER (thiz)->srcpad);
       outs = gst_caps_get_structure (allowed_caps, 0);
       out_format = gst_structure_get_string (outs, "format");
+      gst_structure_get_int (outs, "width", &out_width);
+      gst_structure_get_int (outs, "height", &out_height);
+
       if (out_format) {
         format = gst_video_format_from_string (out_format);
         thiz->sfc = TRUE;
       }
-    }
-    gst_caps_unref (temp_caps);
 
-    /* SFC scaling, need to check if downstream asking for a different resolution */
-    if (!allowed_caps) {
-      allowed_caps =
-          gst_pad_get_allowed_caps (GST_VIDEO_DECODER (thiz)->srcpad);
-      outs = gst_caps_get_structure (allowed_caps, 0);
-    }
-
-    gst_structure_get_int (outs, "width", &out_width);
-    gst_structure_get_int (outs, "height", &out_height);
-    gst_caps_unref (allowed_caps);
-
-    if (out_width && out_height && (out_width != width || out_height != height)) {
-      if (out_width > width || out_height > height)
-        GST_WARNING_OBJECT (thiz, "Decoder SFC cannot do up-scaling");
-      else {
-        GST_LOG_OBJECT (thiz, "Decoder SFC is doing down-scaling");
-        thiz->sfc = TRUE;
-        width = out_width;
-        height = out_height;
+      if (!out_width && !out_height) {
+        out_width = width;
+        out_height = height;
+      } else {
+        /* When user does not set out_width, fill it to fit DAR */
+        if (!out_width)
+          out_width = gst_util_uint64_scale (out_height, dar_n, dar_d);
+        /* When user does not set out_height, fill it to fit DAR */
+        if (!out_height)
+          out_height = gst_util_uint64_scale (out_width, dar_d, dar_n);
+
+        if (out_width > width || out_height > height)
+          goto sfc_error;
+        else if (out_width < width || out_height < height) {
+          width = out_width;
+          height = out_height;
+          thiz->sfc = TRUE;
+        }
       }
+      gst_caps_unref (allowed_caps);
     }
+    gst_caps_unref (temp_caps);
   }
 #endif
 
@@ -746,6 +769,12 @@ gst_msdkdec_set_src_caps (GstMsdkDec * thiz, gboolean need_allocation)
   gst_video_codec_state_unref (output_state);
 
   return TRUE;
+
+sfc_error:
+  GST_ERROR_OBJECT (thiz, "Decoder SFC cannot do up-scaling");
+  gst_caps_unref (allowed_caps);
+  gst_caps_unref (temp_caps);
+  return FALSE;
 }
 
 static void