From 6d9de5b60a9f26822ae28bb9d5fa788a80963ccd Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Tue, 12 Mar 2024 15:27:33 +0100 Subject: [PATCH] v4l2codecs: encoder: add rotation support Add rotation support based on V4L2_CID_ROTATE control. 90 and 270 degrees rotation requires swap of src width & height which must be take into account when allocating src pool. Upstream-Status: Pending --- sys/v4l2codecs/gstv4l2codech264enc.c | 15 ++++++---- sys/v4l2codecs/gstv4l2codecvp8enc.c | 15 ++++++---- sys/v4l2codecs/gstv4l2encoder.c | 41 ++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c index 0a195c8..921302a 100644 --- a/sys/v4l2codecs/gstv4l2codech264enc.c +++ b/sys/v4l2codecs/gstv4l2codech264enc.c @@ -76,6 +76,7 @@ struct _GstV4l2CodecH264Enc GstV4l2Encoder *encoder; GstVideoCodecState *output_state; GstVideoInfo vinfo; + GstVideoInfo sink_info; gint width; gint height; gint width_in_macroblocks; @@ -276,7 +277,8 @@ gst_v4l2_codec_h264_enc_propose_allocation (GstVideoEncoder * encoder, } static gboolean -gst_v4l2_codec_h264_enc_buffers_allocation (GstVideoEncoder * encoder) +gst_v4l2_codec_h264_enc_buffers_allocation (GstVideoEncoder * encoder, + GstVideoCodecState * state) { GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); @@ -306,7 +308,7 @@ gst_v4l2_codec_h264_enc_buffers_allocation (GstVideoEncoder * encoder) return FALSE; } - self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo); + self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &state->info); return TRUE; } @@ -705,6 +707,8 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, gst_v4l2_codec_h264_enc_reset_allocation (self); + self->sink_info = state->info; + if (!gst_v4l2_encoder_set_src_fmt (self->encoder, &state->info, V4L2_PIX_FMT_H264_SLICE)) { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported pixel format"), @@ -713,7 +717,7 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, return FALSE; } - if (!gst_v4l2_encoder_select_sink_format (self->encoder, &state->info, + if (!gst_v4l2_encoder_select_sink_format (self->encoder, &self->sink_info, &self->vinfo)) { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Failed to configure H264 encoder"), @@ -727,7 +731,8 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, self->height = state->info.height; GST_VIDEO_ENCODER_CLASS (parent_class)->set_format (encoder, state); - gst_v4l2_codec_h264_enc_buffers_allocation (encoder); + gst_v4l2_codec_h264_enc_buffers_allocation (encoder, state); + self->width_in_macroblocks = (self->width + 15) / 16; self->height_in_macroblocks = (self->height + 15) / 16; self->first_frame = TRUE; @@ -864,7 +869,7 @@ gst_v4l2_codec_h264_enc_copy_input_buffer (GstV4l2CodecH264Enc * self, GstFlowReturn flow_ret; gst_video_info_set_format (&src_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo), - self->width, self->height); + self->sink_info.width, self->sink_info.height); flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->sink_pool), &buffer, NULL); diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c index 42c2b1b..4ec141c 100644 --- a/sys/v4l2codecs/gstv4l2codecvp8enc.c +++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c @@ -72,6 +72,7 @@ struct _GstV4l2CodecVp8Enc GstV4l2Encoder *encoder; GstVideoCodecState *output_state; GstVideoInfo vinfo; + GstVideoInfo sink_info; gint width; gint height; guint qp_max, qp_min; @@ -249,7 +250,8 @@ gst_v4l2_codec_vp8_enc_propose_allocation (GstVideoEncoder * encoder, } static gboolean -gst_v4l2_codec_vp8_enc_buffers_allocation (GstVideoEncoder * encoder) +gst_v4l2_codec_vp8_enc_buffers_allocation (GstVideoEncoder * encoder, + GstVideoCodecState * state) { GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); @@ -279,8 +281,7 @@ gst_v4l2_codec_vp8_enc_buffers_allocation (GstVideoEncoder * encoder) return FALSE; } - self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo); - + self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &state->info); return TRUE; } @@ -298,6 +299,8 @@ gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder, gst_v4l2_codec_vp8_enc_reset_allocation (self); + self->sink_info = state->info; + if (!gst_v4l2_encoder_set_src_fmt (self->encoder, &state->info, V4L2_PIX_FMT_VP8_FRAME)) { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported pixel format"), @@ -306,7 +309,7 @@ gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder, return FALSE; } - if (!gst_v4l2_encoder_select_sink_format (self->encoder, &state->info, + if (!gst_v4l2_encoder_select_sink_format (self->encoder, &self->sink_info, &self->vinfo)) { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Failed to configure VP8 encoder"), @@ -318,7 +321,7 @@ gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder, self->width = state->info.width; self->height = state->info.height; - gst_v4l2_codec_vp8_enc_buffers_allocation (encoder); + gst_v4l2_codec_vp8_enc_buffers_allocation (encoder, state); if (self->output_state) gst_video_codec_state_unref (self->output_state); @@ -445,7 +448,7 @@ gst_v4l2_codec_vp8_enc_copy_input_buffer (GstV4l2CodecVp8Enc * self, GstFlowReturn flow_ret; gst_video_info_set_format (&src_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo), - self->width, self->height); + self->sink_info.width, self->sink_info.height); flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->sink_pool), &buffer, NULL); diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c index 27f03e9..2532dc3 100644 --- a/sys/v4l2codecs/gstv4l2encoder.c +++ b/sys/v4l2codecs/gstv4l2encoder.c @@ -44,6 +44,7 @@ enum PROP_0, PROP_MEDIA_DEVICE, PROP_VIDEO_DEVICE, + PROP_ROTATION, }; struct _GstV4l2Request @@ -95,6 +96,7 @@ struct _GstV4l2Encoder gchar *media_device; gchar *video_device; guint render_delay; + guint rotation; /* detected features */ gboolean supports_holding_capture; @@ -687,6 +689,29 @@ gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, gint width = info->width; gint height = info->height; + /* Apply rotation, this will affect src width/height */ + if (self->rotation) { + struct v4l2_control ctl = { 0, }; + + ctl.id = V4L2_CID_ROTATE; + ctl.value = self->rotation; + ret = ioctl (self->video_fd, VIDIOC_S_CTRL, &ctl); + if (ret < 0) { + GST_ERROR_OBJECT (self, "VIDIOC_S_CTRL failed: %s", g_strerror (errno)); + return FALSE; + } + + if (self->rotation == 90 || self->rotation == 270) { + /* + * Swap width & height, this will trigger a call + * to S_FMT to let driver know the final + * rotated resolution + */ + width = info->height; + height = info->width; + } + } + ret = ioctl (self->video_fd, VIDIOC_G_FMT, &fmt); if (ret < 0) { GST_ERROR_OBJECT (self, "VIDIOC_G_FMT failed: %s", g_strerror (errno)); @@ -718,6 +743,10 @@ gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, return FALSE; } + /* Stick to driver negotiated resolution */ + info->width = fmt.fmt.pix_mp.width; + info->height = fmt.fmt.pix_mp.height; + return TRUE; } @@ -1114,6 +1143,12 @@ gst_v4l2_encoder_install_properties (GObjectClass * gobject_class, g_param_spec_string ("video-device", "Video Device Path", "Path to the video device node", video_device_path, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ROTATION, + g_param_spec_uint ("rotation", "Rotation", + "Set rotation angle in degrees", + 0, 270, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); } void @@ -1131,6 +1166,9 @@ gst_v4l2_encoder_set_property (GObject * object, guint prop_id, g_free (self->video_device); self->video_device = g_value_dup_string (value); break; + case PROP_ROTATION: + self->rotation = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1150,6 +1188,9 @@ gst_v4l2_encoder_get_property (GObject * object, guint prop_id, case PROP_VIDEO_DEVICE: g_value_set_string (value, self->video_device); break; + case PROP_ROTATION: + g_value_set_uint (value, self->rotation); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; -- 2.25.1