[libcamera-devel] [PATCH 2/2] gstreamer: src: Add transform property

Robert Mader robert.mader at collabora.com
Tue Jun 13 12:34:50 CEST 2023


This allows users to request a transform using the Gstreamer
equivalent. If the combined transform of the requested one and a
possible rotation from the camera properties is not fully supported by
the sensor, the remaining transform will be passed down to downstream
elements as tag.

The later is common for 90/270 degree rotations. Thus, a side effect of
this feature is that libcamerasrc now behaves similar to pipewiresrc in
regards to rotated cameras in e.g. phones, allowing apps to compensate
accordingly.

Signed-off-by: Robert Mader <robert.mader at collabora.com>
---
 src/gstreamer/gstlibcamera-utils.cpp | 72 ++++++++++++++++++++++++++++
 src/gstreamer/gstlibcamera-utils.h   |  5 ++
 src/gstreamer/gstlibcamerasrc.cpp    | 41 +++++++++++++++-
 3 files changed, 117 insertions(+), 1 deletion(-)

diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
index 750ec351..43ce75cb 100644
--- a/src/gstreamer/gstlibcamera-utils.cpp
+++ b/src/gstreamer/gstlibcamera-utils.cpp
@@ -553,3 +553,75 @@ gst_libcamera_get_camera_manager(int &ret)
 
 	return cm;
 }
+
+libcamera::Transform
+gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation)
+{
+	switch (orientation) {
+	case GST_VIDEO_ORIENTATION_90R:
+		return Transform::Rot90;
+	case GST_VIDEO_ORIENTATION_180:
+		return Transform::Rot180;
+	case GST_VIDEO_ORIENTATION_90L:
+		return Transform::Rot270;
+	case GST_VIDEO_ORIENTATION_HORIZ:
+		return Transform::HFlip;
+	case GST_VIDEO_ORIENTATION_VERT:
+		return Transform::VFlip;
+	case GST_VIDEO_ORIENTATION_UL_LR:
+		return Transform::Transpose;
+	case GST_VIDEO_ORIENTATION_UR_LL:
+		return Transform::Rot180Transpose;
+	case GST_VIDEO_ORIENTATION_IDENTITY:
+	default:
+		return Transform::Identity;
+	}
+}
+
+GstVideoOrientationMethod
+gst_libcamera_transform_to_orientation(libcamera::Transform transform)
+{
+	switch (transform) {
+	case Transform::Rot90:
+		return GST_VIDEO_ORIENTATION_90R;
+	case Transform::Rot180:
+		return GST_VIDEO_ORIENTATION_180;
+	case Transform::Rot270:
+		return GST_VIDEO_ORIENTATION_90L;
+	case Transform::HFlip:
+		return GST_VIDEO_ORIENTATION_HORIZ;
+	case Transform::VFlip:
+		return GST_VIDEO_ORIENTATION_VERT;
+	case Transform::Transpose:
+		return GST_VIDEO_ORIENTATION_UL_LR;
+	case Transform::Rot180Transpose:
+		return GST_VIDEO_ORIENTATION_UR_LL;
+	case Transform::Identity:
+	default:
+		return GST_VIDEO_ORIENTATION_IDENTITY;
+	}
+}
+
+const char *
+gst_libcamera_transform_to_tag_string(libcamera::Transform transform)
+{
+	switch (transform) {
+	case Transform::Rot90:
+		return "rotate-90";
+	case Transform::Rot180:
+		return "rotate-180";
+	case Transform::Rot270:
+		return "rotate-270";
+	case Transform::HFlip:
+		return "flip-rotate-0";
+	case Transform::VFlip:
+		return "flip-rotate-180";
+	case Transform::Transpose:
+		return "flip-rotate-270";
+	case Transform::Rot180Transpose:
+		return "flip-rotate-90";
+	case Transform::Identity:
+	default:
+		return "rotate-0";
+	}
+}
diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h
index fd304a8b..84d26c47 100644
--- a/src/gstreamer/gstlibcamera-utils.h
+++ b/src/gstreamer/gstlibcamera-utils.h
@@ -11,6 +11,7 @@
 #include <libcamera/camera_manager.h>
 #include <libcamera/controls.h>
 #include <libcamera/stream.h>
+#include <libcamera/transform.h>
 
 #include <gst/gst.h>
 #include <gst/video/video.h>
@@ -30,6 +31,10 @@ gboolean gst_task_resume(GstTask *task);
 #endif
 std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);
 
+libcamera::Transform gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation);
+GstVideoOrientationMethod gst_libcamera_transform_to_orientation(libcamera::Transform transform);
+const char *gst_libcamera_transform_to_tag_string(libcamera::Transform transform);
+
 /**
  * \class GLibLocker
  * \brief A simple scoped mutex locker for GMutex
diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
index 721b35c2..9d9437d0 100644
--- a/src/gstreamer/gstlibcamerasrc.cpp
+++ b/src/gstreamer/gstlibcamerasrc.cpp
@@ -147,6 +147,8 @@ struct _GstLibcameraSrc {
 
 	gchar *camera_name;
 
+	GstVideoOrientationMethod transform;
+
 	GstLibcameraSrcState *state;
 	GstLibcameraAllocator *allocator;
 	GstFlowCombiner *flow_combiner;
@@ -154,7 +156,9 @@ struct _GstLibcameraSrc {
 
 enum {
 	PROP_0,
-	PROP_CAMERA_NAME
+	PROP_CAMERA_NAME,
+	PROP_TRANSFORM,
+	PROP_LAST
 };
 
 G_DEFINE_TYPE_WITH_CODE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT,
@@ -461,6 +465,8 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
 	GLibRecLocker lock(&self->stream_lock);
 	GstLibcameraSrcState *state = self->state;
 	GstFlowReturn flow_ret = GST_FLOW_OK;
+	libcamera::Transform tag_transform;
+	const char* tag_string;
 	gint ret;
 
 	g_autoptr(GstStructure) element_caps = gst_structure_new_empty("caps");
@@ -513,12 +519,27 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
 	if (flow_ret != GST_FLOW_OK)
 		goto done;
 
+	state->config_->transform =
+		gst_libcamera_orientation_to_transform (self->transform);
+
 	/* Validate the configuration. */
 	if (state->config_->validate() == CameraConfiguration::Invalid) {
 		flow_ret = GST_FLOW_NOT_NEGOTIATED;
 		goto done;
 	}
 
+	tag_transform = (gst_libcamera_orientation_to_transform (self->transform) ^
+			 state->config_->transform);
+	tag_string = gst_libcamera_transform_to_tag_string(tag_transform);
+	for (gsize i = 0; i < state->srcpads_.size(); i++) {
+		GstPad *srcpad = state->srcpads_[i];
+		GstEvent *tag_event;
+
+		tag_event = gst_event_new_tag(gst_tag_list_new(GST_TAG_IMAGE_ORIENTATION,
+							       tag_string, NULL));
+		gst_pad_push_event (srcpad, tag_event);
+	}
+
 	ret = state->cam_->configure(state->config_.get());
 	if (ret) {
 		GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
@@ -659,6 +680,11 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id,
 		g_free(self->camera_name);
 		self->camera_name = g_value_dup_string(value);
 		break;
+	case PROP_TRANSFORM:
+		self->transform =
+			static_cast<GstVideoOrientationMethod>(
+				g_value_get_enum(value));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 		break;
@@ -676,6 +702,9 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value,
 	case PROP_CAMERA_NAME:
 		g_value_set_string(value, self->camera_name);
 		break;
+        case PROP_TRANSFORM:
+                g_value_set_enum(value, self->transform);
+                break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 		break;
@@ -845,4 +874,14 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)
 						  | G_PARAM_CONSTRUCT
 						  | G_PARAM_READWRITE
 						  | G_PARAM_STATIC_STRINGS)));
+
+	g_object_class_install_property (object_class, PROP_TRANSFORM,
+		g_param_spec_enum ("transform", "Transform",
+				   "Request a transform (rotation and/or flip).",
+				   GST_TYPE_VIDEO_ORIENTATION_METHOD,
+				   GST_VIDEO_ORIENTATION_IDENTITY,
+				   (GParamFlags)(GST_PARAM_MUTABLE_READY
+						 | G_PARAM_CONSTRUCT
+						 | G_PARAM_READWRITE
+						 | G_PARAM_STATIC_STRINGS)));
 }
-- 
2.41.0



More information about the libcamera-devel mailing list