v4l2slh264dec: Add output format negotiation
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Mon, 17 Feb 2020 23:08:48 +0000 (18:08 -0500)
committerNicolas Dufresne <nicolas.dufresne@collabora.com>
Tue, 31 Mar 2020 13:34:05 +0000 (09:34 -0400)
This allow negotiating the output format through caps. Some drivers can
pipeline the decoder buffer through an image processor. This only support
colorspace conversion for now.

sys/v4l2codecs/gstv4l2codech264dec.c
sys/v4l2codecs/gstv4l2decoder.c
sys/v4l2codecs/gstv4l2decoder.h
sys/v4l2codecs/gstv4l2format.c

index 7422aa8..135a755 100644 (file)
@@ -138,6 +138,16 @@ gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder)
 {
   GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder);
   GstH264Decoder *h264dec = GST_H264_DECODER (decoder);
+  /* *INDENT-OFF* */
+  struct v4l2_ext_control control[] = {
+    {
+      .id = V4L2_CID_MPEG_VIDEO_H264_SPS,
+      .ptr = &self->sps,
+      .size = sizeof (self->sps),
+    },
+  };
+  /* *INDENT-ON* */
+  GstCaps *filter, *caps;
 
   /* Ignore downstream renegotiation request. */
   if (!self->need_negotiation)
@@ -164,18 +174,29 @@ gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder)
     return FALSE;
   }
 
-  /* TODO set sequence parameter control, this is needed to negotiate a
-   * format with the help of the driver */
+  if (!gst_v4l2_decoder_set_controls (self->decoder, NULL, control,
+          G_N_ELEMENTS (control))) {
+    GST_ELEMENT_ERROR (decoder, RESOURCE, WRITE,
+        ("Driver does not support the selected stream."), (NULL));
+    return FALSE;
+  }
+
+  filter = gst_v4l2_decoder_enum_src_formats (self->decoder);
+  GST_DEBUG_OBJECT (self, "Supported output formats: %" GST_PTR_FORMAT, filter);
 
-  if (!gst_v4l2_decoder_select_src_format (self->decoder, &self->vinfo)) {
+  caps = gst_pad_peer_query_caps (decoder->srcpad, filter);
+  gst_caps_unref (filter);
+  GST_DEBUG_OBJECT (self, "Peer supported formats: %" GST_PTR_FORMAT, caps);
+
+  if (!gst_v4l2_decoder_select_src_format (self->decoder, caps, &self->vinfo)) {
     GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
         ("Unsupported bitdepth/chroma format"),
         ("No support for %ux%u %ubit chroma IDC %i", self->coded_width,
             self->coded_height, self->bitdepth, self->chroma_format_idc));
+    gst_caps_unref (caps);
     return FALSE;
   }
-
-  /* TODO some decoders supports color convertion and scaling */
+  gst_caps_unref (caps);
 
   if (self->output_state)
     gst_video_codec_state_unref (self->output_state);
index 1ab52ea..d80f7c7 100644 (file)
@@ -254,20 +254,109 @@ gst_v4l2_decoder_set_sink_fmt (GstV4l2Decoder * self, guint32 pix_fmt,
   return TRUE;
 }
 
+GstCaps *
+gst_v4l2_decoder_enum_src_formats (GstV4l2Decoder * self)
+{
+  gint ret;
+  struct v4l2_format fmt = {
+    .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+  };
+  GstVideoFormat format;
+  GstCaps *caps;
+  GValue list = G_VALUE_INIT;
+  GValue value = G_VALUE_INIT;
+  gint i;
+
+  g_return_val_if_fail (self->opened, FALSE);
+
+  ret = ioctl (self->video_fd, VIDIOC_G_FMT, &fmt);
+  if (ret < 0) {
+    GST_ERROR_OBJECT (self, "VIDIOC_G_FMT failed: %s", g_strerror (errno));
+    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 ();
+
+  /* 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, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, };
+
+    ret = ioctl (self->video_fd, VIDIOC_ENUM_FMT, &fmtdesc);
+    if (ret < 0) {
+      if (errno != EINVAL)
+        GST_ERROR_OBJECT (self, "VIDIOC_ENUM_FMT failed: %s",
+            g_strerror (errno));
+      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);
+  }
+
+  return caps;
+}
+
 gboolean
-gst_v4l2_decoder_select_src_format (GstV4l2Decoder * self, GstVideoInfo * info)
+gst_v4l2_decoder_select_src_format (GstV4l2Decoder * self, GstCaps * caps,
+    GstVideoInfo * info)
 {
   gint ret;
   struct v4l2_format fmt = {
     .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
   };
+  GstStructure *str;
+  const gchar *format_str;
+  GstVideoFormat format;
+  guint32 pix_fmt;
+
+  if (gst_caps_is_empty (caps))
+    return FALSE;
 
   ret = ioctl (self->video_fd, VIDIOC_G_FMT, &fmt);
   if (ret < 0) {
-    GST_ERROR_OBJECT (self, "VIDIOC_S_FMT failed: %s", g_strerror (errno));
+    GST_ERROR_OBJECT (self, "VIDIOC_G_FMT failed: %s", g_strerror (errno));
     return FALSE;
   }
 
+  caps = gst_caps_make_writable (caps);
+  str = gst_caps_get_structure (caps, 0);
+  gst_structure_fixate_field (str, "format");
+
+  format_str = gst_structure_get_string (str, "format");
+  format = gst_video_format_from_string (format_str);
+
+  if (gst_v4l2_format_from_video_format (format, &pix_fmt) &&
+      pix_fmt != fmt.fmt.pix_mp.pixelformat) {
+    GST_DEBUG_OBJECT (self, "Trying to use peer format: %s ", format_str);
+    fmt.fmt.pix_mp.pixelformat = pix_fmt;
+
+    ret = ioctl (self->video_fd, VIDIOC_S_FMT, &fmt);
+    if (ret < 0) {
+      GST_ERROR_OBJECT (self, "VIDIOC_S_FMT failed: %s", g_strerror (errno));
+      return FALSE;
+    }
+  }
+
   if (!gst_v4l2_format_to_video_info (&fmt, info)) {
     GST_ERROR_OBJECT (self, "Unsupported V4L2 pixelformat %" GST_FOURCC_FORMAT,
         GST_FOURCC_ARGS (fmt.fmt.pix_mp.pixelformat));
@@ -466,8 +555,8 @@ gst_v4l2_decoder_set_controls (GstV4l2Decoder * self, GstV4l2Request * request,
   struct v4l2_ext_controls controls = {
     .controls = control,
     .count = count,
-    .request_fd = request->fd,
-    .which = V4L2_CTRL_WHICH_REQUEST_VAL,
+    .request_fd = request ? request->fd : 0,
+    .which = request ? V4L2_CTRL_WHICH_REQUEST_VAL : 0,
   };
 
   ret = ioctl (self->video_fd, VIDIOC_S_EXT_CTRLS, &controls);
index 7335224..350d367 100644 (file)
@@ -51,7 +51,10 @@ gboolean          gst_v4l2_decoder_enum_sink_fmt (GstV4l2Decoder * self,
 gboolean          gst_v4l2_decoder_set_sink_fmt (GstV4l2Decoder * self, guint32 fmt,
                                                  gint width, gint height);
 
+GstCaps *         gst_v4l2_decoder_enum_src_formats (GstV4l2Decoder * self);
+
 gboolean          gst_v4l2_decoder_select_src_format (GstV4l2Decoder * self,
+                                                      GstCaps * caps,
                                                       GstVideoInfo * info);
 
 gint              gst_v4l2_decoder_request_buffers (GstV4l2Decoder * self,
index f07ac65..33945d1 100644 (file)
@@ -33,6 +33,7 @@ struct FormatEntry
 
 static struct FormatEntry format_map[] = {
   {V4L2_PIX_FMT_NV12, 1, GST_VIDEO_FORMAT_NV12, 8, 420},
+  {V4L2_PIX_FMT_YUYV, 1, GST_VIDEO_FORMAT_YUY2, 8, 422},
   {0,}
 };