From 7ae57631d46db35a4394084cf5bc7acbe06a63a7 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Mon, 10 Feb 2020 18:05:39 -0500 Subject: [PATCH] v4l2codecs: Implement H264 format negotiation --- sys/v4l2codecs/gstv4l2codech264dec.c | 100 +++++++++++++++++++++++++-- sys/v4l2codecs/gstv4l2decoder.c | 80 +++++++++++++++++++-- sys/v4l2codecs/gstv4l2decoder.h | 8 +++ 3 files changed, 178 insertions(+), 10 deletions(-) diff --git a/sys/v4l2codecs/gstv4l2codech264dec.c b/sys/v4l2codecs/gstv4l2codech264dec.c index 9a3428d780..a2ac27612c 100644 --- a/sys/v4l2codecs/gstv4l2codech264dec.c +++ b/sys/v4l2codecs/gstv4l2codech264dec.c @@ -22,6 +22,7 @@ #endif #include "gstv4l2codech264dec.h" +#include "linux/h264-ctrls.h" GST_DEBUG_CATEGORY_STATIC (v4l2_h264dec_debug); #define GST_CAT_DEFAULT v4l2_h264dec_debug @@ -49,9 +50,13 @@ struct _GstV4l2CodecH264Dec GstH264Decoder parent; GstV4l2Decoder *decoder; GstVideoCodecState *output_state; - GstVideoFormat out_format; - gint width; - gint height; + GstVideoInfo vinfo; + gint display_width; + gint display_height; + gint coded_width; + gint coded_height; + guint bitdepth; + guint chroma_format_idc; }; G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstV4l2CodecH264Dec, @@ -63,12 +68,23 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstV4l2CodecH264Dec, static gboolean gst_v4l2_codec_h264_dec_open (GstVideoDecoder * decoder) { + GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder); + + if (!gst_v4l2_decoder_open (self->decoder)) { + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Failed to open H264 decoder"), + ("gst_v4l2_decoder_open() failed: %s", g_strerror (errno))); + return FALSE; + } + return TRUE; } static gboolean gst_v4l2_codec_h264_dec_close (GstVideoDecoder * decoder) { + GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder); + gst_v4l2_decoder_close (self->decoder); return TRUE; } @@ -90,14 +106,39 @@ gst_v4l2_codec_h264_dec_negotiate (GstVideoDecoder * decoder) GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder); GstH264Decoder *h264dec = GST_H264_DECODER (decoder); - GST_DEBUG_OBJECT (self, "negotiate"); + GST_DEBUG_OBJECT (self, "Negotiate"); + + /* TODO drain / reset */ + + if (!gst_v4l2_decoder_set_sink_fmt (self->decoder, V4L2_PIX_FMT_H264_SLICE, + self->coded_width, self->coded_height)) { + GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, + ("Failed to configure H264 decoder"), + ("gst_v4l2_decoder_set_sink_fmt() failed: %s", g_strerror (errno))); + gst_v4l2_decoder_close (self->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_select_src_format (self->decoder, &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)); + return FALSE; + } + + /* TODO some decoders supports color convertion and scaling */ if (self->output_state) gst_video_codec_state_unref (self->output_state); self->output_state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self), - self->out_format, self->width, self->height, h264dec->input_state); + self->vinfo.finfo->format, self->display_width, + self->display_height, h264dec->input_state); self->output_state->caps = gst_video_info_to_caps (&self->output_state->info); @@ -116,7 +157,51 @@ static gboolean gst_v4l2_codec_h264_dec_new_sequence (GstH264Decoder * decoder, const GstH264SPS * sps, gint max_dpb_size) { - /* TODO check if we need to setup a new format */ + GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder); + gint crop_width = sps->width; + gint crop_height = sps->height; + gboolean negotiation_needed = FALSE; + + if (self->vinfo.finfo->format == GST_VIDEO_FORMAT_UNKNOWN) + negotiation_needed = TRUE; + + if (sps->frame_cropping_flag) { + crop_width = sps->crop_rect_width; + crop_height = sps->crop_rect_height; + } + + if (self->display_width != crop_width || self->display_height != crop_height + || self->coded_width != sps->width || self->coded_height != sps->height) { + self->display_width = crop_width; + self->display_height = crop_height; + self->coded_width = sps->width; + self->coded_height = sps->height; + negotiation_needed = TRUE; + GST_INFO_OBJECT (self, "Resolution changed to %dx%d (%ix%i)", + self->display_width, self->display_height, + self->coded_width, self->coded_height); + } + + if (self->bitdepth != sps->bit_depth_luma_minus8 + 8) { + self->bitdepth = sps->bit_depth_luma_minus8 + 8; + negotiation_needed = TRUE; + GST_INFO_OBJECT (self, "Bitdepth changed to %u", self->bitdepth); + } + + if (self->chroma_format_idc != sps->chroma_format_idc) { + self->chroma_format_idc = sps->chroma_format_idc; + negotiation_needed = TRUE; + GST_INFO_OBJECT (self, "Chroma format changed to %i", + self->chroma_format_idc); + } + + if (negotiation_needed) { + if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) { + GST_ERROR_OBJECT (self, "Failed to negotiate with downstream"); + return FALSE; + } + } + return TRUE; } @@ -124,6 +209,7 @@ static gboolean gst_v4l2_codec_h264_dec_start_picture (GstH264Decoder * decoder, GstH264Picture * picture, GstH264Slice * slice, GstH264Dpb * dpb) { + /* Fill v4l2_ctrl_h264_decode_params */ return FALSE; } @@ -131,6 +217,7 @@ static GstFlowReturn gst_v4l2_codec_h264_dec_output_picture (GstH264Decoder * decoder, GstH264Picture * picture) { + /* Fill vl2_ctrl_h264_slice_params */ return GST_FLOW_ERROR; } @@ -186,6 +273,7 @@ gst_v4l2_codec_h264_dec_subinit (GstV4l2CodecH264Dec * self, GstV4l2CodecH264DecClass * klass) { self->decoder = gst_v4l2_decoder_new (klass->device); + gst_video_info_init (&self->vinfo); } static void diff --git a/sys/v4l2codecs/gstv4l2decoder.c b/sys/v4l2codecs/gstv4l2decoder.c index 2771e8c034..4aa61ef70f 100644 --- a/sys/v4l2codecs/gstv4l2decoder.c +++ b/sys/v4l2codecs/gstv4l2decoder.c @@ -22,6 +22,7 @@ #endif #include "gstv4l2decoder.h" +#include "gstv4l2format.h" #include "linux/media.h" #include "linux/videodev2.h" @@ -62,10 +63,7 @@ gst_v4l2_decoder_finalize (GObject * obj) { GstV4l2Decoder *self = GST_V4L2_DECODER (obj); - if (self->media_fd) - close (self->media_fd); - if (self->video_fd) - close (self->media_fd); + gst_v4l2_decoder_close (self); g_free (self->media_device); g_free (self->video_device); @@ -127,6 +125,21 @@ gst_v4l2_decoder_open (GstV4l2Decoder * self) return TRUE; } +gboolean +gst_v4l2_decoder_close (GstV4l2Decoder * self) +{ + if (self->media_fd) + close (self->media_fd); + if (self->video_fd) + close (self->media_fd); + + self->media_fd = 0; + self->video_fd = 0; + self->opened = FALSE; + + return TRUE; +} + gboolean gst_v4l2_decoder_enum_sink_fmt (GstV4l2Decoder * self, gint i, guint32 * out_fmt) @@ -150,6 +163,65 @@ gst_v4l2_decoder_enum_sink_fmt (GstV4l2Decoder * self, gint i, return TRUE; } +gboolean +gst_v4l2_decoder_set_sink_fmt (GstV4l2Decoder * self, guint32 pix_fmt, + gint width, gint height) +{ + struct v4l2_format format = (struct v4l2_format) { + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .fmt.pix_mp = (struct v4l2_pix_format_mplane) { + .pixelformat = pix_fmt, + .width = width, + .height = height, + }, + }; + gint ret; + + ret = ioctl (self->video_fd, VIDIOC_S_FMT, &format); + if (ret < 0) { + GST_ERROR_OBJECT (self, "VIDIOC_S_FMT failed: %s", g_strerror (errno)); + return FALSE; + } + + if (format.fmt.pix_mp.pixelformat != pix_fmt + || format.fmt.pix_mp.width != width + || format.fmt.pix_mp.height != height) { + GST_WARNING_OBJECT (self, "Failed to set sink format to %" + GST_FOURCC_FORMAT " %ix%i", GST_FOURCC_ARGS (pix_fmt), width, height); + errno = EINVAL; + return FALSE; + } + + return TRUE; +} + +gboolean +gst_v4l2_decoder_select_src_format (GstV4l2Decoder * self, GstVideoInfo * info) +{ + gint ret; + struct v4l2_format fmt = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }; + + ret = ioctl (self->video_fd, VIDIOC_G_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)); + return FALSE; + } + + GST_INFO_OBJECT (self, "Selected format %s %ix%i", + gst_video_format_to_string (info->finfo->format), + info->width, info->height); + + return TRUE; +} + void gst_v4l2_decoder_install_properties (GObjectClass * gobject_class, gint prop_offset, GstV4l2CodecDevice * device) diff --git a/sys/v4l2codecs/gstv4l2decoder.h b/sys/v4l2codecs/gstv4l2decoder.h index 4475595902..e9c05f18bc 100644 --- a/sys/v4l2codecs/gstv4l2decoder.h +++ b/sys/v4l2codecs/gstv4l2decoder.h @@ -34,9 +34,17 @@ GstV4l2Decoder * gst_v4l2_decoder_new (GstV4l2CodecDevice * device); gboolean gst_v4l2_decoder_open (GstV4l2Decoder * decoder); +gboolean gst_v4l2_decoder_close (GstV4l2Decoder * decoder); + gboolean gst_v4l2_decoder_enum_sink_fmt (GstV4l2Decoder * self, gint i, guint32 * out_fmt); +gboolean gst_v4l2_decoder_set_sink_fmt (GstV4l2Decoder * self, guint32 fmt, + gint width, gint height); + +gboolean gst_v4l2_decoder_select_src_format (GstV4l2Decoder * self, + GstVideoInfo * info); + void gst_v4l2_decoder_install_properties (GObjectClass * gobject_class, gint prop_offset, GstV4l2CodecDevice * device);