smartencoder: Fix renegotiating when reencoding parts of the stream with vpx
authorThibault Saunier <tsaunier@igalia.com>
Tue, 5 Oct 2021 13:46:26 +0000 (10:46 -0300)
committerThibault Saunier <tsaunier@igalia.com>
Mon, 18 Oct 2021 13:10:24 +0000 (10:10 -0300)
In the encoded streams we might not have all the information about the
raw video stream, but when reencoding they end up being specified, even
if those are default values.

As vp8 decoders always output frames in some YUV color space we can
ensure that when upstream doesn't specify any value in its caps we
use the default one which is what we end up doing when decoding/reencoding
anyway, so this way downstream (matroskamux in that case) doesn't need
to be able to renegotiate (which it doesn't).

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

subprojects/gst-plugins-base/gst/encoding/gstsmartencoder.c

index 1824597..ea1a8a9 100644 (file)
@@ -23,6 +23,7 @@
 #endif
 
 #include <string.h>
+#include <gst/video/video.h>
 #include "gstsmartencoder.h"
 
 GST_DEBUG_CATEGORY_STATIC (smart_encoder_debug);
@@ -497,6 +498,66 @@ beach:
   return res;
 }
 
+static GstCaps *
+smart_encoder_get_caps (GstSmartEncoder * self, GstCaps * original_caps)
+{
+  gint i;
+  GstCaps *caps, *outcaps;
+  GstStructure *original_struct = gst_caps_get_structure (original_caps, 0);
+  GstStructure *out_struct, *_struct;
+  GstVideoInfo info;
+  static const gchar *default_fields[] = {
+    "pixel-aspect-ratio",
+    "framerate",
+    "interlace-mode",
+    "colorimetry",
+    "chroma-site",
+    "multiview-mode",
+    "multiview-flags",
+  };
+
+  if (!gst_structure_has_name (original_struct, "video/x-vp8")) {
+
+    return gst_caps_ref (original_caps);
+  }
+
+  /* VP8 is always decoded into YUV colorspaces and we support VP9 profiles
+   * where only YUV is supported (0 and 2) so we ensure that all the
+   * default fields for video/x-raw are set on the caps if none provided by
+   * upstream. This allows us to allow renegotiating new caps downstream when
+   * switching from no reencoding to reencoding making sure all the fields are
+   * defined all the time
+   */
+  caps = gst_caps_copy (original_caps);
+  _struct = gst_caps_get_structure (caps, 0);
+  gst_structure_set_name (_struct, "video/x-raw");
+  gst_structure_set (_struct,
+      "format", G_TYPE_STRING, "I420",
+      "multiview-mode", G_TYPE_STRING, "mono",
+      "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
+      GST_VIDEO_MULTIVIEW_FLAGS_NONE, GST_FLAG_SET_MASK_EXACT, NULL);
+
+  gst_video_info_from_caps (&info, caps);
+  gst_caps_unref (caps);
+  caps = gst_video_info_to_caps (&info);
+  _struct = gst_caps_get_structure (caps, 0);
+
+  outcaps = gst_caps_copy (original_caps);
+  out_struct = gst_caps_get_structure (outcaps, 0);
+  for (i = 0; i < G_N_ELEMENTS (default_fields); i++) {
+    const gchar *field = default_fields[i];
+
+    if (!gst_structure_has_field (original_struct, field)) {
+      const GValue *v = gst_structure_get_value (_struct, field);
+      g_assert (v);
+      gst_structure_set_value (out_struct, field, v);
+    }
+  }
+  gst_caps_unref (caps);
+
+  return outcaps;
+}
+
 static gboolean
 smart_encoder_sink_event (GstPad * pad, GstObject * ghostpad, GstEvent * event)
 {
@@ -515,7 +576,8 @@ smart_encoder_sink_event (GstPad * pad, GstObject * ghostpad, GstEvent * event)
       if (self->original_caps)
         gst_caps_unref (self->original_caps);
 
-      self->original_caps = gst_caps_ref (caps);
+
+      self->original_caps = smart_encoder_get_caps (self, caps);
       self->push_original_caps = TRUE;
       gst_clear_event (&event);
       break;