{"id":18733,"url":"https://patchwork.libcamera.org/api/1.1/patches/18733/?format=json","web_url":"https://patchwork.libcamera.org/patch/18733/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20230613103519.91370-3-robert.mader@collabora.com>","date":"2023-06-13T10:34:50","name":"[libcamera-devel,2/2] gstreamer: src: Add transform property","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"5beca49e526d92b6153be5c6871dc663656319e4","submitter":{"id":140,"url":"https://patchwork.libcamera.org/api/1.1/people/140/?format=json","name":"Robert Mader","email":"robert.mader@collabora.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/18733/mbox/","series":[{"id":3921,"url":"https://patchwork.libcamera.org/api/1.1/series/3921/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=3921","date":"2023-06-13T10:34:48","name":"gstreamer: src: Add transform property","version":1,"mbox":"https://patchwork.libcamera.org/series/3921/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/18733/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/18733/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 9D6E5C3226\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 13 Jun 2023 10:36:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F18E461E59;\n\tTue, 13 Jun 2023 12:36:30 +0200 (CEST)","from madras.collabora.co.uk (madras.collabora.co.uk\n\t[IPv6:2a00:1098:0:82:1000:25:2eeb:e5ab])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 732AF61E4E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jun 2023 12:36:29 +0200 (CEST)","from thinkpad-t460p.fritz.box (unknown\n\t[IPv6:2001:4090:a243:8015:93c:83c3:533:d433])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\tkey-exchange X25519 server-signature RSA-PSS (4096 bits)\n\tserver-digest SHA256)\n\t(No client certificate requested) (Authenticated sender: rmader)\n\tby madras.collabora.co.uk (Postfix) with ESMTPSA id 1D7A16606F02;\n\tTue, 13 Jun 2023 11:36:29 +0100 (BST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1686652591;\n\tbh=GUNI6TWGFRgy73Jg68alWddqXmN0nn6soGClbbcehZY=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=o1ZuIxXHZdcrW+gyWCfb9SS1KUy5J7vmbRDxlYDGPhRytd7DvNM++87mOp8PFj9jc\n\tEpfF+5yEhzNhO/Qwesg3HeWOu2OO+xDRPDu090a6OZY+iQxdmrLbJTC3xBdcZvEhIq\n\tp82y39k22+kASv+sW50PdEtJ9LCpVTCp3QqL8odElQmzZoT2schax0BGkrIwCdh5Nt\n\tR3eZABqVr57i9kh/fPomhDHPvrG0hvnBBIUU5STJsw+GGK9o7swThRc1pKTRh1Kmtp\n\tLAk0gzTzo4jotYHZBhkd6uIduSDwYMCy6karo4oahGfm6xr+8jup5mIiY58hN6tvi0\n\tRnRDjdCmdHC8A==","v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com;\n\ts=mail; t=1686652589;\n\tbh=GUNI6TWGFRgy73Jg68alWddqXmN0nn6soGClbbcehZY=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=Fq48OVOEmYc0FnV71vHC4RiRo2esYAiMGKi15rN0oXBBGm8Oom/YOAB2i5ROgkuAc\n\tBH6YQLjEK9TDTYDK5exhDs4jneZM/jnT4xP5XvXOp5giy00oNVY369hFs6XYRzZVxH\n\taI79bFyqX0Ya7EJMRCRmR1CMYEmtJKO8KDoZdmroavi0TXhVRRrbACy+XMw8QQAyKc\n\tPHj7QZHRxeEbey5NhfjL/K0OufhpIiUaNGBexJJi2C0l8ZLTS1wQA2KMR/ay/OTK7T\n\t8VCBOWsdtJGHYEjh/B5VLjQ+b+IyNo3F2g8AA+VkzAo5B6DaJrTzPfHHLAYYrrhgt1\n\trdNdWPdZJnRsw=="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=collabora.com\n\theader.i=@collabora.com\n\theader.b=\"Fq48OVOE\"; dkim-atps=neutral","To":"libcamera-devel@lists.libcamera.org","Date":"Tue, 13 Jun 2023 12:34:50 +0200","Message-ID":"<20230613103519.91370-3-robert.mader@collabora.com>","X-Mailer":"git-send-email 2.41.0","In-Reply-To":"<20230613103519.91370-1-robert.mader@collabora.com>","References":"<20230613103519.91370-1-robert.mader@collabora.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH 2/2] gstreamer: src: Add transform property","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Robert Mader via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Robert Mader <robert.mader@collabora.com>","Cc":"Robert Mader <robert.mader@collabora.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"This allows users to request a transform using the Gstreamer\nequivalent. If the combined transform of the requested one and a\npossible rotation from the camera properties is not fully supported by\nthe sensor, the remaining transform will be passed down to downstream\nelements as tag.\n\nThe later is common for 90/270 degree rotations. Thus, a side effect of\nthis feature is that libcamerasrc now behaves similar to pipewiresrc in\nregards to rotated cameras in e.g. phones, allowing apps to compensate\naccordingly.\n\nSigned-off-by: Robert Mader <robert.mader@collabora.com>\n---\n src/gstreamer/gstlibcamera-utils.cpp | 72 ++++++++++++++++++++++++++++\n src/gstreamer/gstlibcamera-utils.h   |  5 ++\n src/gstreamer/gstlibcamerasrc.cpp    | 41 +++++++++++++++-\n 3 files changed, 117 insertions(+), 1 deletion(-)","diff":"diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\nindex 750ec351..43ce75cb 100644\n--- a/src/gstreamer/gstlibcamera-utils.cpp\n+++ b/src/gstreamer/gstlibcamera-utils.cpp\n@@ -553,3 +553,75 @@ gst_libcamera_get_camera_manager(int &ret)\n \n \treturn cm;\n }\n+\n+libcamera::Transform\n+gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation)\n+{\n+\tswitch (orientation) {\n+\tcase GST_VIDEO_ORIENTATION_90R:\n+\t\treturn Transform::Rot90;\n+\tcase GST_VIDEO_ORIENTATION_180:\n+\t\treturn Transform::Rot180;\n+\tcase GST_VIDEO_ORIENTATION_90L:\n+\t\treturn Transform::Rot270;\n+\tcase GST_VIDEO_ORIENTATION_HORIZ:\n+\t\treturn Transform::HFlip;\n+\tcase GST_VIDEO_ORIENTATION_VERT:\n+\t\treturn Transform::VFlip;\n+\tcase GST_VIDEO_ORIENTATION_UL_LR:\n+\t\treturn Transform::Transpose;\n+\tcase GST_VIDEO_ORIENTATION_UR_LL:\n+\t\treturn Transform::Rot180Transpose;\n+\tcase GST_VIDEO_ORIENTATION_IDENTITY:\n+\tdefault:\n+\t\treturn Transform::Identity;\n+\t}\n+}\n+\n+GstVideoOrientationMethod\n+gst_libcamera_transform_to_orientation(libcamera::Transform transform)\n+{\n+\tswitch (transform) {\n+\tcase Transform::Rot90:\n+\t\treturn GST_VIDEO_ORIENTATION_90R;\n+\tcase Transform::Rot180:\n+\t\treturn GST_VIDEO_ORIENTATION_180;\n+\tcase Transform::Rot270:\n+\t\treturn GST_VIDEO_ORIENTATION_90L;\n+\tcase Transform::HFlip:\n+\t\treturn GST_VIDEO_ORIENTATION_HORIZ;\n+\tcase Transform::VFlip:\n+\t\treturn GST_VIDEO_ORIENTATION_VERT;\n+\tcase Transform::Transpose:\n+\t\treturn GST_VIDEO_ORIENTATION_UL_LR;\n+\tcase Transform::Rot180Transpose:\n+\t\treturn GST_VIDEO_ORIENTATION_UR_LL;\n+\tcase Transform::Identity:\n+\tdefault:\n+\t\treturn GST_VIDEO_ORIENTATION_IDENTITY;\n+\t}\n+}\n+\n+const char *\n+gst_libcamera_transform_to_tag_string(libcamera::Transform transform)\n+{\n+\tswitch (transform) {\n+\tcase Transform::Rot90:\n+\t\treturn \"rotate-90\";\n+\tcase Transform::Rot180:\n+\t\treturn \"rotate-180\";\n+\tcase Transform::Rot270:\n+\t\treturn \"rotate-270\";\n+\tcase Transform::HFlip:\n+\t\treturn \"flip-rotate-0\";\n+\tcase Transform::VFlip:\n+\t\treturn \"flip-rotate-180\";\n+\tcase Transform::Transpose:\n+\t\treturn \"flip-rotate-270\";\n+\tcase Transform::Rot180Transpose:\n+\t\treturn \"flip-rotate-90\";\n+\tcase Transform::Identity:\n+\tdefault:\n+\t\treturn \"rotate-0\";\n+\t}\n+}\ndiff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h\nindex fd304a8b..84d26c47 100644\n--- a/src/gstreamer/gstlibcamera-utils.h\n+++ b/src/gstreamer/gstlibcamera-utils.h\n@@ -11,6 +11,7 @@\n #include <libcamera/camera_manager.h>\n #include <libcamera/controls.h>\n #include <libcamera/stream.h>\n+#include <libcamera/transform.h>\n \n #include <gst/gst.h>\n #include <gst/video/video.h>\n@@ -30,6 +31,10 @@ gboolean gst_task_resume(GstTask *task);\n #endif\n std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);\n \n+libcamera::Transform gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation);\n+GstVideoOrientationMethod gst_libcamera_transform_to_orientation(libcamera::Transform transform);\n+const char *gst_libcamera_transform_to_tag_string(libcamera::Transform transform);\n+\n /**\n  * \\class GLibLocker\n  * \\brief A simple scoped mutex locker for GMutex\ndiff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\nindex 721b35c2..9d9437d0 100644\n--- a/src/gstreamer/gstlibcamerasrc.cpp\n+++ b/src/gstreamer/gstlibcamerasrc.cpp\n@@ -147,6 +147,8 @@ struct _GstLibcameraSrc {\n \n \tgchar *camera_name;\n \n+\tGstVideoOrientationMethod transform;\n+\n \tGstLibcameraSrcState *state;\n \tGstLibcameraAllocator *allocator;\n \tGstFlowCombiner *flow_combiner;\n@@ -154,7 +156,9 @@ struct _GstLibcameraSrc {\n \n enum {\n \tPROP_0,\n-\tPROP_CAMERA_NAME\n+\tPROP_CAMERA_NAME,\n+\tPROP_TRANSFORM,\n+\tPROP_LAST\n };\n \n G_DEFINE_TYPE_WITH_CODE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT,\n@@ -461,6 +465,8 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n \tGLibRecLocker lock(&self->stream_lock);\n \tGstLibcameraSrcState *state = self->state;\n \tGstFlowReturn flow_ret = GST_FLOW_OK;\n+\tlibcamera::Transform tag_transform;\n+\tconst char* tag_string;\n \tgint ret;\n \n \tg_autoptr(GstStructure) element_caps = gst_structure_new_empty(\"caps\");\n@@ -513,12 +519,27 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n \tif (flow_ret != GST_FLOW_OK)\n \t\tgoto done;\n \n+\tstate->config_->transform =\n+\t\tgst_libcamera_orientation_to_transform (self->transform);\n+\n \t/* Validate the configuration. */\n \tif (state->config_->validate() == CameraConfiguration::Invalid) {\n \t\tflow_ret = GST_FLOW_NOT_NEGOTIATED;\n \t\tgoto done;\n \t}\n \n+\ttag_transform = (gst_libcamera_orientation_to_transform (self->transform) ^\n+\t\t\t state->config_->transform);\n+\ttag_string = gst_libcamera_transform_to_tag_string(tag_transform);\n+\tfor (gsize i = 0; i < state->srcpads_.size(); i++) {\n+\t\tGstPad *srcpad = state->srcpads_[i];\n+\t\tGstEvent *tag_event;\n+\n+\t\ttag_event = gst_event_new_tag(gst_tag_list_new(GST_TAG_IMAGE_ORIENTATION,\n+\t\t\t\t\t\t\t       tag_string, NULL));\n+\t\tgst_pad_push_event (srcpad, tag_event);\n+\t}\n+\n \tret = state->cam_->configure(state->config_.get());\n \tif (ret) {\n \t\tGST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,\n@@ -659,6 +680,11 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id,\n \t\tg_free(self->camera_name);\n \t\tself->camera_name = g_value_dup_string(value);\n \t\tbreak;\n+\tcase PROP_TRANSFORM:\n+\t\tself->transform =\n+\t\t\tstatic_cast<GstVideoOrientationMethod>(\n+\t\t\t\tg_value_get_enum(value));\n+\t\tbreak;\n \tdefault:\n \t\tG_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n \t\tbreak;\n@@ -676,6 +702,9 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value,\n \tcase PROP_CAMERA_NAME:\n \t\tg_value_set_string(value, self->camera_name);\n \t\tbreak;\n+        case PROP_TRANSFORM:\n+                g_value_set_enum(value, self->transform);\n+                break;\n \tdefault:\n \t\tG_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n \t\tbreak;\n@@ -845,4 +874,14 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)\n \t\t\t\t\t\t  | G_PARAM_CONSTRUCT\n \t\t\t\t\t\t  | G_PARAM_READWRITE\n \t\t\t\t\t\t  | G_PARAM_STATIC_STRINGS)));\n+\n+\tg_object_class_install_property (object_class, PROP_TRANSFORM,\n+\t\tg_param_spec_enum (\"transform\", \"Transform\",\n+\t\t\t\t   \"Request a transform (rotation and/or flip).\",\n+\t\t\t\t   GST_TYPE_VIDEO_ORIENTATION_METHOD,\n+\t\t\t\t   GST_VIDEO_ORIENTATION_IDENTITY,\n+\t\t\t\t   (GParamFlags)(GST_PARAM_MUTABLE_READY\n+\t\t\t\t\t\t | G_PARAM_CONSTRUCT\n+\t\t\t\t\t\t | G_PARAM_READWRITE\n+\t\t\t\t\t\t | G_PARAM_STATIC_STRINGS)));\n }\n","prefixes":["libcamera-devel","2/2"]}