From 390cbe1f0016870dbd15b8704ca4ccee973106e8 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Mon, 17 Feb 2020 18:08:48 -0500 Subject: [PATCH] v4l2slh264dec: Add output format negotiation 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 | 31 +++++++-- sys/v4l2codecs/gstv4l2decoder.c | 99 ++++++++++++++++++++++++++-- sys/v4l2codecs/gstv4l2decoder.h | 3 + sys/v4l2codecs/gstv4l2format.c | 1 + 4 files changed, 124 insertions(+), 10 deletions(-) diff --git a/sys/v4l2codecs/gstv4l2codech264dec.c b/sys/v4l2codecs/gstv4l2codech264dec.c index 7422aa883b..135a755639 100644 --- a/sys/v4l2codecs/gstv4l2codech264dec.c +++ b/sys/v4l2codecs/gstv4l2codech264dec.c @@ -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; + } - if (!gst_v4l2_decoder_select_src_format (self->decoder, &self->vinfo)) { + filter = gst_v4l2_decoder_enum_src_formats (self->decoder); + GST_DEBUG_OBJECT (self, "Supported output formats: %" GST_PTR_FORMAT, filter); + + 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); diff --git a/sys/v4l2codecs/gstv4l2decoder.c b/sys/v4l2codecs/gstv4l2decoder.c index 1ab52eaff5..d80f7c7d58 100644 --- a/sys/v4l2codecs/gstv4l2decoder.c +++ b/sys/v4l2codecs/gstv4l2decoder.c @@ -254,20 +254,109 @@ gst_v4l2_decoder_set_sink_fmt (GstV4l2Decoder * self, guint32 pix_fmt, return TRUE; } -gboolean -gst_v4l2_decoder_select_src_format (GstV4l2Decoder * self, GstVideoInfo * info) +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_S_FMT failed: %s", g_strerror (errno)); + 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, 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_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); diff --git a/sys/v4l2codecs/gstv4l2decoder.h b/sys/v4l2codecs/gstv4l2decoder.h index 7335224f7b..350d367661 100644 --- a/sys/v4l2codecs/gstv4l2decoder.h +++ b/sys/v4l2codecs/gstv4l2decoder.h @@ -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, diff --git a/sys/v4l2codecs/gstv4l2format.c b/sys/v4l2codecs/gstv4l2format.c index f07ac65ade..33945d1aef 100644 --- a/sys/v4l2codecs/gstv4l2format.c +++ b/sys/v4l2codecs/gstv4l2format.c @@ -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,} };