249 lines
8.9 KiB
Diff
249 lines
8.9 KiB
Diff
From 6d9de5b60a9f26822ae28bb9d5fa788a80963ccd Mon Sep 17 00:00:00 2001
|
|
From: Hugues Fruchet <hugues.fruchet@foss.st.com>
|
|
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
|
|
|