v4l2codecs: enumerate all possible formats
authorBenjamin Gaignard <benjamin.gaignard@collabora.com>
Tue, 22 Jun 2021 12:43:29 +0000 (14:43 +0200)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Wed, 29 Jun 2022 19:18:16 +0000 (19:18 +0000)
Some decoder may also allow to (down) scale the decoded video.
Use VIDIOC_ENUM_FRAMESIZES iotcl to enumerate the possible output
formats resolutions.

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

subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2decoder.c

index 7f7b3f9..4188f58 100644 (file)
@@ -356,6 +356,79 @@ gst_v4l2_decoder_set_sink_fmt (GstV4l2Decoder * self, guint32 pix_fmt,
   return TRUE;
 }
 
+static GstCaps *
+gst_v4l2_decoder_enum_size_for_format (GstV4l2Decoder * self,
+    guint32 pixelformat, gint index, gint unscaled_width, gint unscaled_height)
+{
+  struct v4l2_frmsizeenum size;
+  GstVideoFormat format;
+  gint ret;
+  gboolean res;
+
+  memset (&size, 0, sizeof (struct v4l2_frmsizeenum));
+  size.index = index;
+  size.pixel_format = pixelformat;
+
+  GST_DEBUG_OBJECT (self, "enumerate size index %d for %" GST_FOURCC_FORMAT,
+      index, GST_FOURCC_ARGS (pixelformat));
+
+  ret = ioctl (self->video_fd, VIDIOC_ENUM_FRAMESIZES, &size);
+
+  if (ret < 0)
+    return NULL;
+
+  if (size.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
+    GST_WARNING_OBJECT (self, "V4L2_FRMSIZE type not supported");
+    return NULL;
+  }
+
+  if (gst_util_fraction_compare (unscaled_width, unscaled_height,
+          size.discrete.width, size.discrete.height)) {
+    GST_DEBUG_OBJECT (self,
+        "Pixel ratio modification not supported %dx%d %dx%d (%d)",
+        unscaled_width, unscaled_height, size.discrete.width,
+        size.discrete.height, ret);
+    return NULL;
+  }
+
+  res = gst_v4l2_format_to_video_format (pixelformat, &format);
+  g_assert (res);
+
+  GST_DEBUG_OBJECT (self, "get size (%d x %d) index %d for %" GST_FOURCC_FORMAT,
+      size.discrete.width, size.discrete.height, index,
+      GST_FOURCC_ARGS (pixelformat));
+
+  return gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING,
+      gst_video_format_to_string (format),
+      "width", G_TYPE_INT, size.discrete.width,
+      "height", G_TYPE_INT, size.discrete.height, NULL);
+}
+
+static GstCaps *
+gst_v4l2_decoder_probe_caps_for_format (GstV4l2Decoder * self,
+    guint32 pixelformat, gint unscaled_width, gint unscaled_height)
+{
+  gint index = 0;
+  GstCaps *caps, *tmp;
+  GstVideoFormat format;
+
+  GST_DEBUG_OBJECT (self, "enumerate size for %" GST_FOURCC_FORMAT,
+      GST_FOURCC_ARGS (pixelformat));
+
+  if (!gst_v4l2_format_to_video_format (pixelformat, &format))
+    return gst_caps_new_empty ();
+
+  caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING,
+      gst_video_format_to_string (format), NULL);
+
+  while ((tmp = gst_v4l2_decoder_enum_size_for_format (self, pixelformat,
+              index++, unscaled_width, unscaled_height))) {
+    caps = gst_caps_merge (caps, tmp);
+  }
+
+  return caps;
+}
+
 GstCaps *
 gst_v4l2_decoder_enum_src_formats (GstV4l2Decoder * self)
 {
@@ -363,10 +436,7 @@ gst_v4l2_decoder_enum_src_formats (GstV4l2Decoder * self)
   struct v4l2_format fmt = {
     .type = self->src_buf_type,
   };
-  GstVideoFormat format;
   GstCaps *caps;
-  GValue list = G_VALUE_INIT;
-  GValue value = G_VALUE_INIT;
   gint i;
 
   g_return_val_if_fail (self->opened, FALSE);
@@ -377,20 +447,15 @@ gst_v4l2_decoder_enum_src_formats (GstV4l2Decoder * self)
     return FALSE;
   }
 
-  /* We first place a structure with the default pixel format */
-  if (gst_v4l2_format_to_video_format (fmt.fmt.pix_mp.pixelformat, &format))
-    caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING,
-        gst_video_format_to_string (format), NULL);
-  else
-    caps = gst_caps_new_empty ();
+  caps =
+      gst_v4l2_decoder_probe_caps_for_format (self,
+      fmt.fmt.pix_mp.pixelformat, fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height);
 
   /* And then enumerate other possible formats and place that as a second
    * structure in the caps */
-  g_value_init (&list, GST_TYPE_LIST);
-  g_value_init (&value, G_TYPE_STRING);
-
   for (i = 0; ret >= 0; i++) {
     struct v4l2_fmtdesc fmtdesc = { i, self->src_buf_type, };
+    GstCaps *tmp;
 
     ret = ioctl (self->video_fd, VIDIOC_ENUM_FMT, &fmtdesc);
     if (ret < 0) {
@@ -400,19 +465,9 @@ gst_v4l2_decoder_enum_src_formats (GstV4l2Decoder * self)
       continue;
     }
 
-    if (gst_v4l2_format_to_video_format (fmtdesc.pixelformat, &format)) {
-      g_value_set_static_string (&value, gst_video_format_to_string (format));
-      gst_value_list_append_value (&list, &value);
-    }
-  }
-  g_value_reset (&value);
-
-  if (gst_value_list_get_size (&list) > 0) {
-    GstStructure *str = gst_structure_new_empty ("video/x-raw");
-    gst_structure_take_value (str, "format", &list);
-    gst_caps_append_structure (caps, str);
-  } else {
-    g_value_reset (&list);
+    tmp = gst_v4l2_decoder_probe_caps_for_format (self, fmtdesc.pixelformat,
+        fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height);
+    caps = gst_caps_merge (caps, tmp);
   }
 
   return caps;