{"id":23666,"url":"https://patchwork.libcamera.org/api/patches/23666/?format=json","web_url":"https://patchwork.libcamera.org/patch/23666/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/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":"<20250626101017.3358487-1-giacomo.cappellini.87@gmail.com>","date":"2025-06-26T10:08:38","name":"[v2] gstreamer: Adding static rotation property to GstLibcameraDevice and mutable orientation property to GstLibcameraSrc","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"39c815a313b151fc1da814ddf5da0660c26453d4","submitter":{"id":231,"url":"https://patchwork.libcamera.org/api/people/231/?format=json","name":"Giacomo Cappellini","email":"giacomo.cappellini.87@gmail.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/23666/mbox/","series":[{"id":5252,"url":"https://patchwork.libcamera.org/api/series/5252/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5252","date":"2025-06-26T10:08:38","name":"[v2] gstreamer: Adding static rotation property to GstLibcameraDevice and mutable orientation property to GstLibcameraSrc","version":2,"mbox":"https://patchwork.libcamera.org/series/5252/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/23666/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/23666/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 EC6B0C3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 26 Jun 2025 10:10:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D04F168DF3;\n\tThu, 26 Jun 2025 12:10:24 +0200 (CEST)","from mail-ej1-x636.google.com (mail-ej1-x636.google.com\n\t[IPv6:2a00:1450:4864:20::636])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id DE6B468DE8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 26 Jun 2025 12:10:22 +0200 (CEST)","by mail-ej1-x636.google.com with SMTP id\n\ta640c23a62f3a-ae0d935020eso87803466b.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 26 Jun 2025 03:10:22 -0700 (PDT)","from jasus.ad.servtec.it\n\t(host-95-251-230-143.retail.telecomitalia.it. [95.251.230.143])\n\tby smtp.gmail.com with ESMTPSA id\n\ta640c23a62f3a-ae0542110f8sm1188425866b.182.2025.06.26.03.10.20\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 26 Jun 2025 03:10:21 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"RTGk9UQV\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=gmail.com; s=20230601; t=1750932622; x=1751537422;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:mime-version:message-id:date:subject:cc\n\t:to:from:from:to:cc:subject:date:message-id:reply-to;\n\tbh=u3SS2GCqEPz4/ckzz3DpcY0JlTQS7a5pyGdjXcFNizo=;\n\tb=RTGk9UQV0Rnh7edjz4k8FT1IiL7/QM/gVno6f6U1524snzIGjX3qQsukxvr9wSwSq5\n\t38+9AE9HG8YPYGfnRk4BSb4zQ1/BN2oce7q9CaEPPDxL8srevoLVfCpvuti15tOkuBEX\n\tNhpjkkQMOmbjlS+7XtEQb0oSzCkN9/eTkaymYemL1Ta8ZIW5axEGpHr89uZ2qi19jnmK\n\tVqH6K+dCnohFo2qRMd6qXKjrLWHQvE7P2/hTSUKk3ZcxNQ5FuRUTKyUcxBpyVCtpBMzU\n\tAZLZy0+a7kuVVFpL6PuGxccZjBc9RRO1jVu5nabdaT+oDsQ4J1IADav8CLTiyNIRE8xx\n\t8kyg==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1750932622; x=1751537422;\n\th=content-transfer-encoding:mime-version:message-id:date:subject:cc\n\t:to:from:x-gm-message-state:from:to:cc:subject:date:message-id\n\t:reply-to;\n\tbh=u3SS2GCqEPz4/ckzz3DpcY0JlTQS7a5pyGdjXcFNizo=;\n\tb=XMz+cjqOIyatjzZ57s7lHFtFDG0h43q0UhGg5ZJoEStEuSEGDs01o8gofegfhrYUUG\n\trBscIqQ5l7EMAur9WR7bHgLtrBWccat5KMqDlLhe9aSm5GfdKCoWYhJ+TfNfpdLgy/Rv\n\tsmbY+8+KKZW50xKp5GTxbQRbXeRShR9x7Ns7h2QMqDoaT/QwQPwNDUafb0UxxogTW6AL\n\tGWtuRJmmh//em/XX0nBx+N/hij96ECFRddrV0SxoZZsC3mPQtOv6982hoZF3aP0f1QPH\n\ttl/O1RIx2/fNI5+OLbKQwfrD0ua1uPuknRNCC7ZegW0RwcpE7/OoZiUGXw23hQmEJcTP\n\ta78Q==","X-Gm-Message-State":"AOJu0YzMS6bt+esnewsL2cEDtnr5pp7unKe9FBMeIwK3gGGAae2NWqcp\n\tb8gR5PiZ0TRAlEMPBPOnz8cBuBQ51xI9N5GWufHSR2Wj2g5xE0CSD9GI/LQRT0Am","X-Gm-Gg":"ASbGnctA6h1GwkXZ7RJr+Uh9pu6lCKm4bzaVU+cOhnrMk2xY/mIVbO53lM8if18ysFC\n\tjPnU1Fae+F29eJks4chkdJwsZOtGXW4UwbWzpG0YS5Fj2mmxcldJzvf+BhpYAcxlPoeBvYs34ln\n\t3bfbg1DIOy8ife3GQ1ZC0ZHKlhgU8ErtFnb3wXUOyF57VJvRTvKIEHBHa9mIwXWI69b2xk9iFys\n\tJ3eUh8w21VLtyhC7GMQqhE+hFTISKJxRLtUQ2Dkj8ET8ZcTa/qww3xYUUvovsf1ozLnxkmg4EGf\n\twVZIHYi8zviVtgIx9QBygy3rBcHFBg/cmAvZNCLiOkHJiohA14FMHCe6E1xLGk9mYGKtCWerYaH\n\tZ4opHAvLDuv5A19HVnatXo0J96qBtEA9HXrxKWg6t4TEgCpApySWuZs6tUrjilg==","X-Google-Smtp-Source":"AGHT+IHFIdeuEhlwbeb2FtJTHh1LhFXqBuJvvbByphWv3Duj7vYgQ62q4Y0qhpf2SGkWQIkaHBcqdQ==","X-Received":"by 2002:a17:907:310d:b0:ae0:a912:f366 with SMTP id\n\ta640c23a62f3a-ae0bee7bd49mr493908466b.54.1750932621848; \n\tThu, 26 Jun 2025 03:10:21 -0700 (PDT)","From":"Giacomo Cappellini <giacomo.cappellini.87@gmail.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"Giacomo Cappellini <giacomo.cappellini.87@gmail.com>","Subject":"[PATCH v2] gstreamer: Adding static rotation property to\n\tGstLibcameraDevice and mutable orientation property to\n\tGstLibcameraSrc","Date":"Thu, 26 Jun 2025 12:08:38 +0200","Message-ID":"<20250626101017.3358487-1-giacomo.cappellini.87@gmail.com>","X-Mailer":"git-send-email 2.43.0","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","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>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"- rotation: is a static camera property reported\n  through Camera::properties().\n  This is exposed in GstLibcameraDevice as an extra property.\n  You can read it with gst-device-monitor-1.0\n  or GstDevice::gst_device_get_properties.\n\n- orientation: is a configurable parameter of the camera that only\n  concerns with the actual images rotation.\n  This is exposed as a GST_PARAM_MUTABLE_READY parameter of\n  GstLibcameraSrc.\n  It gets validated via CameraConfiguation::validate().\n  If the camera doesn't support it and CameraConfiguration::Adjusted\n  is returned the adjusted configuration will be\n  logged as a warning.\n---\n src/gstreamer/gstlibcameraprovider.cpp |  5 ++\n src/gstreamer/gstlibcamerasrc.cpp      | 98 +++++++++++++++++++++++++-\n 2 files changed, 102 insertions(+), 1 deletion(-)","diff":"diff --git a/src/gstreamer/gstlibcameraprovider.cpp b/src/gstreamer/gstlibcameraprovider.cpp\nindex 5da96ea3..d3384c57 100644\n--- a/src/gstreamer/gstlibcameraprovider.cpp\n+++ b/src/gstreamer/gstlibcameraprovider.cpp\n@@ -10,6 +10,7 @@\n \n #include \"gstlibcameraprovider.h\"\n \n+#include <libcamera/libcamera.h>\n #include <libcamera/camera.h>\n #include <libcamera/camera_manager.h>\n \n@@ -131,6 +132,7 @@ gst_libcamera_device_new(const std::shared_ptr<Camera> &camera)\n \tstatic const std::array roles{ StreamRole::VideoRecording };\n \tg_autoptr(GstCaps) caps = gst_caps_new_empty();\n \tconst gchar *name = camera->id().c_str();\n+\tconst int32_t rotation = camera->properties().get(libcamera::properties::Rotation).value_or(0);\n \n \tstd::unique_ptr<CameraConfiguration> config = camera->generateConfiguration(roles);\n \tif (!config || config->size() != roles.size()) {\n@@ -150,6 +152,9 @@ gst_libcamera_device_new(const std::shared_ptr<Camera> &camera)\n \t\t\t\t       \"display-name\", name,\n \t\t\t\t       \"caps\", caps,\n \t\t\t\t       \"device-class\", \"Source/Video\",\n+\t\t\t\t\t   \"properties\", gst_structure_new(\"device-properties\",\n+\t\t\t\t\t\t\t\"rotation\", G_TYPE_INT, rotation,\n+\t\t\t\t\t\t\tNULL),\n \t\t\t\t       nullptr));\n }\n \ndiff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\nindex 3aca4eed..fe3249ff 100644\n--- a/src/gstreamer/gstlibcamerasrc.cpp\n+++ b/src/gstreamer/gstlibcamerasrc.cpp\n@@ -32,6 +32,7 @@\n #include <tuple>\n #include <utility>\n #include <vector>\n+#include <sstream>\n \n #include <libcamera/camera.h>\n #include <libcamera/camera_manager.h>\n@@ -146,6 +147,7 @@ struct _GstLibcameraSrc {\n \tGstTask *task;\n \n \tgchar *camera_name;\n+\tOrientation orientation;\n \n \tstd::atomic<GstEvent *> pending_eos;\n \n@@ -157,6 +159,7 @@ struct _GstLibcameraSrc {\n enum {\n \tPROP_0,\n \tPROP_CAMERA_NAME,\n+\tPROP_ORIENTATION,\n \tPROP_LAST\n };\n \n@@ -616,9 +619,37 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)\n \t\tgst_libcamera_get_framerate_from_caps(caps, element_caps);\n \t}\n \n+\t/* Set orientation control. */\n+\tstate->config_->orientation = self->orientation;\n+\n \t/* Validate the configuration. */\n-\tif (state->config_->validate() == CameraConfiguration::Invalid)\n+\tswitch(state->config_->validate()) {\n+\tcase CameraConfiguration::Valid:\n+\t\tGST_DEBUG_OBJECT(self, \"Camera configuration is valid\");\n+\t\tbreak;\n+\tcase CameraConfiguration::Adjusted:\n+\t{\n+\t\t/*\n+\t\t * The configuration has been adjusted and is now valid.\n+\t\t * Parameters may have changed for any stream, and stream configurations may have been removed.\n+\t\t * Log the adjusted configuration.\n+\t\t */\n+\t\tfor (gsize i = 0; i < state->config_->size(); i++) {\n+\t\t\tconst StreamConfiguration &cfg = state->config_->at(i);\n+\t\t\tGST_WARNING_OBJECT(self, \"Camera configuration adjusted for stream %zu: %s\", i, cfg.toString().c_str());\n+\t\t}\n+\t\tstd::ostringstream oss;\n+\t\toss << state->config_->orientation;\n+\t\tGST_WARNING_OBJECT(self, \"Camera configuration adjusted orientation: %s\", oss.str().c_str());\n+\t\tself->orientation = state->config_->orientation;\n+\t\tbreak;\n+\t}\n+\tcase CameraConfiguration::Invalid:\n+\t\tGST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,\n+\t\t\t\t  (\"Camera configuration is not supported\"),\n+\t\t\t\t  (\"CameraConfiguration::validate() returned Invalid\"));\n \t\treturn false;\n+\t}\n \n \tint ret = state->cam_->configure(state->config_.get());\n \tif (ret) {\n@@ -926,6 +957,9 @@ 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_ORIENTATION:\n+\t\tself->orientation = (libcamera::Orientation)g_value_get_enum(value);\n+\t\tbreak;\n \tdefault:\n \t\tif (!state->controls_.setProperty(prop_id - PROP_LAST, value, pspec))\n \t\t\tG_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n@@ -945,6 +979,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+\tcase PROP_ORIENTATION:\n+\t\tg_value_set_enum(value, (gint)self->orientation);\n+\t\tbreak;\n \tdefault:\n \t\tif (!state->controls_.getProperty(prop_id - PROP_LAST, value, pspec))\n \t\t\tG_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n@@ -1120,6 +1157,53 @@ gst_libcamera_src_release_pad(GstElement *element, GstPad *pad)\n \tgst_element_remove_pad(element, pad);\n }\n \n+static GType\n+gst_libcamera_orientation_get_type()\n+{\n+\tstatic GType type = 0;\n+\tstatic const GEnumValue values[] = {\n+\t\t{\n+\t\t\tstatic_cast<gint>(libcamera::Orientation::Rotate0),\n+\t\t\t\"libcamera::Orientation::Rotate0\",\n+\t\t\t\"rotate-0\",\n+\t\t}, {\n+\t\t\tstatic_cast<gint>(libcamera::Orientation::Rotate0Mirror),\n+\t\t\t\"libcamera::Orientation::Rotate0Mirror\",\n+\t\t\t\"rotate-0-mirror\",\n+\t\t}, {\n+\t\t\tstatic_cast<gint>(libcamera::Orientation::Rotate180),\n+\t\t\t\"libcamera::Orientation::Rotate180\",\n+\t\t\t\"rotate-180\",\n+\t\t}, {\n+\t\t\tstatic_cast<gint>(libcamera::Orientation::Rotate180Mirror),\n+\t\t\t\"libcamera::Orientation::Rotate180Mirror\",\n+\t\t\t\"rotate-180-mirror\",\n+\t\t}, {\n+\t\t\tstatic_cast<gint>(libcamera::Orientation::Rotate90Mirror),\n+\t\t\t\"libcamera::Orientation::Rotate90Mirror\",\n+\t\t\t\"rotate-90-mirror\",\n+\t\t}, {\n+\t\t\tstatic_cast<gint>(libcamera::Orientation::Rotate270),\n+\t\t\t\"libcamera::Orientation::Rotate270\",\n+\t\t\t\"rotate-270\",\n+\t\t}, {\n+\t\t\tstatic_cast<gint>(libcamera::Orientation::Rotate270Mirror),\n+\t\t\t\"libcamera::Orientation::Rotate270Mirror\",\n+\t\t\t\"rotate-270-mirror\",\n+\t\t}, {\n+\t\t\tstatic_cast<gint>(libcamera::Orientation::Rotate90),\n+\t\t\t\"libcamera::Orientation::Rotate90\",\n+\t\t\t\"rotate-90\",\n+\t\t},\n+\t\t{ 0, nullptr, nullptr }\n+\t};\n+\n+\tif (!type)\n+\t\ttype = g_enum_register_static(\"GstLibcameraOrientation\", values);\n+\n+\treturn type;\n+}\n+\n static void\n gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)\n {\n@@ -1154,6 +1238,18 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)\n \t\t\t\t\t\t\t     | G_PARAM_STATIC_STRINGS));\n \tg_object_class_install_property(object_class, PROP_CAMERA_NAME, spec);\n \n+\t/* Register the orientation enum type. */\n+\tGType orientation_type = gst_libcamera_orientation_get_type();\n+\tspec = g_param_spec_enum(\"orientation\", \"Orientation\",\n+\t\t\t\t\t       \"Select the orientation of the camera.\",\n+\t\t\t\t\t       orientation_type,\n+\t\t\t\t\t       static_cast<gint>(libcamera::Orientation::Rotate0),\n+\t\t\t\t\t       (GParamFlags)(GST_PARAM_MUTABLE_READY\n+\t\t\t\t\t\t\t     | G_PARAM_CONSTRUCT\n+\t\t\t\t\t\t\t     | G_PARAM_READWRITE\n+\t\t\t\t\t\t\t     | G_PARAM_STATIC_STRINGS));\n+\tg_object_class_install_property(object_class, PROP_ORIENTATION, spec);\n+\n \tGstCameraControls::installProperties(object_class, PROP_LAST);\n }\n \n","prefixes":["v2"]}