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.
This commit is contained in:
Nicolas Dufresne 2020-02-17 18:08:48 -05:00
parent 6494d7b056
commit 390cbe1f00
4 changed files with 124 additions and 10 deletions

View file

@ -138,6 +138,16 @@ gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder)
{ {
GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder); GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder);
GstH264Decoder *h264dec = GST_H264_DECODER (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. */ /* Ignore downstream renegotiation request. */
if (!self->need_negotiation) if (!self->need_negotiation)
@ -164,18 +174,29 @@ gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder)
return FALSE; return FALSE;
} }
/* TODO set sequence parameter control, this is needed to negotiate a if (!gst_v4l2_decoder_set_controls (self->decoder, NULL, control,
* format with the help of the driver */ 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, GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
("Unsupported bitdepth/chroma format"), ("Unsupported bitdepth/chroma format"),
("No support for %ux%u %ubit chroma IDC %i", self->coded_width, ("No support for %ux%u %ubit chroma IDC %i", self->coded_width,
self->coded_height, self->bitdepth, self->chroma_format_idc)); self->coded_height, self->bitdepth, self->chroma_format_idc));
gst_caps_unref (caps);
return FALSE; return FALSE;
} }
gst_caps_unref (caps);
/* TODO some decoders supports color convertion and scaling */
if (self->output_state) if (self->output_state)
gst_video_codec_state_unref (self->output_state); gst_video_codec_state_unref (self->output_state);

View file

@ -254,19 +254,108 @@ gst_v4l2_decoder_set_sink_fmt (GstV4l2Decoder * self, guint32 pix_fmt,
return TRUE; return TRUE;
} }
gboolean GstCaps *
gst_v4l2_decoder_select_src_format (GstV4l2Decoder * self, GstVideoInfo * info) gst_v4l2_decoder_enum_src_formats (GstV4l2Decoder * self)
{ {
gint ret; gint ret;
struct v4l2_format fmt = { struct v4l2_format fmt = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, .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); 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, 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) { if (ret < 0) {
GST_ERROR_OBJECT (self, "VIDIOC_S_FMT failed: %s", g_strerror (errno)); GST_ERROR_OBJECT (self, "VIDIOC_S_FMT failed: %s", g_strerror (errno));
return FALSE; return FALSE;
} }
}
if (!gst_v4l2_format_to_video_info (&fmt, info)) { if (!gst_v4l2_format_to_video_info (&fmt, info)) {
GST_ERROR_OBJECT (self, "Unsupported V4L2 pixelformat %" GST_FOURCC_FORMAT, GST_ERROR_OBJECT (self, "Unsupported V4L2 pixelformat %" GST_FOURCC_FORMAT,
@ -466,8 +555,8 @@ gst_v4l2_decoder_set_controls (GstV4l2Decoder * self, GstV4l2Request * request,
struct v4l2_ext_controls controls = { struct v4l2_ext_controls controls = {
.controls = control, .controls = control,
.count = count, .count = count,
.request_fd = request->fd, .request_fd = request ? request->fd : 0,
.which = V4L2_CTRL_WHICH_REQUEST_VAL, .which = request ? V4L2_CTRL_WHICH_REQUEST_VAL : 0,
}; };
ret = ioctl (self->video_fd, VIDIOC_S_EXT_CTRLS, &controls); ret = ioctl (self->video_fd, VIDIOC_S_EXT_CTRLS, &controls);

View 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, gboolean gst_v4l2_decoder_set_sink_fmt (GstV4l2Decoder * self, guint32 fmt,
gint width, gint height); gint width, gint height);
GstCaps * gst_v4l2_decoder_enum_src_formats (GstV4l2Decoder * self);
gboolean gst_v4l2_decoder_select_src_format (GstV4l2Decoder * self, gboolean gst_v4l2_decoder_select_src_format (GstV4l2Decoder * self,
GstCaps * caps,
GstVideoInfo * info); GstVideoInfo * info);
gint gst_v4l2_decoder_request_buffers (GstV4l2Decoder * self, gint gst_v4l2_decoder_request_buffers (GstV4l2Decoder * self,

View file

@ -33,6 +33,7 @@ struct FormatEntry
static struct FormatEntry format_map[] = { static struct FormatEntry format_map[] = {
{V4L2_PIX_FMT_NV12, 1, GST_VIDEO_FORMAT_NV12, 8, 420}, {V4L2_PIX_FMT_NV12, 1, GST_VIDEO_FORMAT_NV12, 8, 420},
{V4L2_PIX_FMT_YUYV, 1, GST_VIDEO_FORMAT_YUY2, 8, 422},
{0,} {0,}
}; };