{"id":23902,"url":"https://patchwork.libcamera.org/api/patches/23902/?format=json","web_url":"https://patchwork.libcamera.org/patch/23902/","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":"<20250723140801.648652-1-giacomo.cappellini.87@gmail.com>","date":"2025-07-23T14:07:55","name":"[v3] gstreamer: Add support for Orientation","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"544e4697b0c20f9f299b2b72d3b127056c281766","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/23902/mbox/","series":[{"id":5314,"url":"https://patchwork.libcamera.org/api/series/5314/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5314","date":"2025-07-23T14:07:55","name":"[v3] gstreamer: Add support for Orientation","version":3,"mbox":"https://patchwork.libcamera.org/series/5314/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/23902/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/23902/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 7FF97C3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 23 Jul 2025 14:08:13 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 380BC69057;\n\tWed, 23 Jul 2025 16:08:13 +0200 (CEST)","from mail-ed1-x529.google.com (mail-ed1-x529.google.com\n\t[IPv6:2a00:1450:4864:20::529])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id CB0AE6904F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 23 Jul 2025 16:08:11 +0200 (CEST)","by mail-ed1-x529.google.com with SMTP id\n\t4fb4d7f45d1cf-60c93c23b08so12763968a12.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 23 Jul 2025 07:08:11 -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\t4fb4d7f45d1cf-612c903fbd3sm8523859a12.36.2025.07.23.07.08.09\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 23 Jul 2025 07:08:10 -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=\"lOmIVvxu\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=gmail.com; s=20230601; t=1753279691; x=1753884491;\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=ouJizimE/r8DhMQJqObV/l0/z8FA1ewKVVko2ukHIdk=;\n\tb=lOmIVvxuiYSv6Jmag05qeFKMtgPHVJ14lPpvNKAPPkfc55mwDNCB3jdBFN+G8IeKYr\n\tPOTq/NhKNpkLUFFIMJSVDH6t5HqlzGM4ETUVcfoDbGz27PPrVnfXA6lvMX6uF9DOBnz6\n\tEGljC8YbCUwNnvO4rSA6RQ8SOtRh8hUUCXs2hpXsNscHvxD0nyn7Xz81OWRt0/HzMCVw\n\tPuWRZzuSfxoOb6/GlgQUpiNY+wtHEVmndA3yzX8nSl7jVmdgNqE06gUDHKymeorzsM8C\n\tpagyYPvuQ0y0XopeQmW8m2SftJCJdyV0YW2pdHpXxgixs8KVwew8Tb73mRn2tNfcuLlf\n\tCTgQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1753279691; x=1753884491;\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=ouJizimE/r8DhMQJqObV/l0/z8FA1ewKVVko2ukHIdk=;\n\tb=FYAAdNwp+6iQmIfuav95466qCWg5E/3gJFNe/W7aS9yzSU/cwgKfCC7Dnhy/GM/wWN\n\t6WUOqjuLaVJwm0TeTSnJu6ZZaNlIhrWCRurQ43T5k7oYPl27YlobNQ7bMijuiwlz5nnn\n\tMz/Oc3kbaqepyVAZFEwE4eQaEP55ZDfbo01sdnnCZJJvCl86DSoscXfZaYtGuvAYYup6\n\tBFPPmI/dl+lByfviecScwPmBawOJvwjA4Lp6asw6cEbbXm3wAvWqh+Bm9LHNbpBDNWQt\n\tzcJdW3abS0nwYs3gek3Si1i8B4jhh8q3dTl695q2GnCyIxQxzw5f7MyjH3VE9M5MMrcy\n\t2B2Q==","X-Gm-Message-State":"AOJu0YyCoTlwRw+Wu5pzSZE7h3e9XavRvUeJT5HaSje7qD9lnq8i1tNz\n\txXnwfgk3DE8bQ05BgoT/2LxlTvqttT7/9MuJBhVPg1HVp/u/hB2zFS2i2elEZB0O","X-Gm-Gg":"ASbGncupL7xvoWY1WYNZwOpguYWjMTnmE3YuQi2bOtWTptzKDpfuW934p+39E3FLjnq\n\t34tC3xdui10BduraacyU5ZaI1DHWIKsMLeqaTtSfrwuTRyC6NfkLVgMHlYV0FMKZ35RkIkvCJRc\n\t4gQ5XG6NazcuLRujUSjD28gY+dlgJ9KE8kPYbjowMFwdUzy70vX6XovmGVTQYDLAGanh9hphXn5\n\tUca9hDi0Gx4J8fXmS1Mj2NuxLJ3tCg+ESR4BbT3INXKvUd4TcvKJlyYZiEiG2jo3V3fNJOduEF/\n\tWAn8i1XEv40iTa23GNyVTomn2sc67z/DHZBvfyuB7qx5vU2NzezfgptVYLQZXO2YVbjwB4X0JHY\n\tRzxXBdRXZzbeleyIs+E0qfq4AOFhXJV4FGj99sHejtnL4u5ohRX46migyBfzSrlvnUMr88mKqp4\n\toM179pEy0NkQmrCFeOgC/WoQ==","X-Google-Smtp-Source":"AGHT+IHV1rjZUQr39BVje9lb9VbZ46DrOFMGd6fcorl0Bsmff3aZ1N2SUKK/B/tpsq71DDKwv9LrEg==","X-Received":"by 2002:a05:6402:270d:b0:614:9a3d:2e4c with SMTP id\n\t4fb4d7f45d1cf-6149b5a9e15mr2887573a12.30.1753279690951; \n\tWed, 23 Jul 2025 07:08:10 -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 v3] gstreamer: Add support for Orientation","Date":"Wed, 23 Jul 2025 16:07:55 +0200","Message-ID":"<20250723140801.648652-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":"libcamera allows to control the images orientation through the\nCameraConfiguration::orientation field, expose a GST_PARAM_MUTABLE_READY\nparameter of type GstVideoOrientationMethod in GstLibcameraSrc to\ncontrol it. Parameter is mapped internally to libcamera::Orientation via\nnew gstlibcamera-utils functions:\n- gst_video_orientation_to_libcamera_orientation\n- libcamera_orientation_to_gst_video_orientation\n\nUpdate CameraConfiguration::Adjusted case in negotiation to warn about\nchanges in StreamConfiguration and SensorConfiguration, as well as\nthe new Orientation parameter.\n\nSigned-off-by: Giacomo Cappellini <giacomo.cappellini.87@gmail.com>\n---\n src/gstreamer/gstlibcamera-utils.cpp |  39 ++++++++\n src/gstreamer/gstlibcamera-utils.h   |   4 +\n src/gstreamer/gstlibcamerasrc.cpp    | 132 +++++++++++++++++++++++++--\n 3 files changed, 166 insertions(+), 9 deletions(-)","diff":"diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\nindex a548b0c1..95d3813e 100644\n--- a/src/gstreamer/gstlibcamera-utils.cpp\n+++ b/src/gstreamer/gstlibcamera-utils.cpp\n@@ -10,6 +10,9 @@\n \n #include <libcamera/control_ids.h>\n #include <libcamera/formats.h>\n+#include <libcamera/orientation.h>\n+\n+#include <gst/video/video.h>\n \n using namespace libcamera;\n \n@@ -659,3 +662,39 @@ gst_libcamera_get_camera_manager(int &ret)\n \n \treturn cm;\n }\n+\n+static const struct {\n+\tOrientation orientation;\n+\tGstVideoOrientationMethod method;\n+} orientation_map[]{\n+\t{ Orientation::Rotate0, GST_VIDEO_ORIENTATION_IDENTITY },\n+\t{ Orientation::Rotate90, GST_VIDEO_ORIENTATION_90R },\n+\t{ Orientation::Rotate180, GST_VIDEO_ORIENTATION_180 },\n+\t{ Orientation::Rotate270, GST_VIDEO_ORIENTATION_90L },\n+\t{ Orientation::Rotate0Mirror, GST_VIDEO_ORIENTATION_HORIZ },\n+\t{ Orientation::Rotate180Mirror, GST_VIDEO_ORIENTATION_VERT },\n+\t{ Orientation::Rotate90Mirror, GST_VIDEO_ORIENTATION_UL_LR },\n+\t{ Orientation::Rotate270Mirror, GST_VIDEO_ORIENTATION_UR_LL },\n+};\n+\n+Orientation\n+gst_video_orientation_to_libcamera_orientation(GstVideoOrientationMethod method)\n+{\n+\tfor (auto &b : orientation_map) {\n+\t\tif (b.method == method)\n+\t\t\treturn b.orientation;\n+\t}\n+\n+\treturn Orientation::Rotate0;\n+}\n+\n+GstVideoOrientationMethod\n+libcamera_orientation_to_gst_video_orientation(Orientation orientation)\n+{\n+\tfor (auto &a : orientation_map) {\n+\t\tif (a.orientation == orientation)\n+\t\t\treturn a.method;\n+\t}\n+\n+\treturn GST_VIDEO_ORIENTATION_IDENTITY;\n+}\ndiff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h\nindex 5f4e8a0f..bbbd33db 100644\n--- a/src/gstreamer/gstlibcamera-utils.h\n+++ b/src/gstreamer/gstlibcamera-utils.h\n@@ -10,6 +10,7 @@\n \n #include <libcamera/camera_manager.h>\n #include <libcamera/controls.h>\n+#include <libcamera/orientation.h>\n #include <libcamera/stream.h>\n \n #include <gst/gst.h>\n@@ -92,3 +93,6 @@ public:\n private:\n \tGRecMutex *mutex_;\n };\n+\n+libcamera::Orientation gst_video_orientation_to_libcamera_orientation(GstVideoOrientationMethod method);\n+GstVideoOrientationMethod libcamera_orientation_to_gst_video_orientation(libcamera::Orientation orientation);\ndiff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\nindex 3aca4eed..5f483701 100644\n--- a/src/gstreamer/gstlibcamerasrc.cpp\n+++ b/src/gstreamer/gstlibcamerasrc.cpp\n@@ -29,6 +29,7 @@\n \n #include <atomic>\n #include <queue>\n+#include <sstream>\n #include <tuple>\n #include <utility>\n #include <vector>\n@@ -38,6 +39,7 @@\n #include <libcamera/control_ids.h>\n \n #include <gst/base/base.h>\n+#include <gst/video/video.h>\n \n #include \"gstlibcamera-controls.h\"\n #include \"gstlibcamera-utils.h\"\n@@ -146,6 +148,7 @@ struct _GstLibcameraSrc {\n \tGstTask *task;\n \n \tgchar *camera_name;\n+\tGstVideoOrientationMethod orientation;\n \n \tstd::atomic<GstEvent *> pending_eos;\n \n@@ -157,6 +160,7 @@ struct _GstLibcameraSrc {\n enum {\n \tPROP_0,\n \tPROP_CAMERA_NAME,\n+\tPROP_ORIENTATION,\n \tPROP_LAST\n };\n \n@@ -166,8 +170,8 @@ static void gst_libcamera_src_child_proxy_init(gpointer g_iface,\n G_DEFINE_TYPE_WITH_CODE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT,\n \t\t\tG_IMPLEMENT_INTERFACE(GST_TYPE_CHILD_PROXY,\n \t\t\t\t\t      gst_libcamera_src_child_proxy_init)\n-\t\t\tGST_DEBUG_CATEGORY_INIT(source_debug, \"libcamerasrc\", 0,\n-\t\t\t\t\t\t\"libcamera Source\"))\n+\t\t\t\tGST_DEBUG_CATEGORY_INIT(source_debug, \"libcamerasrc\", 0,\n+\t\t\t\t\t\t\t\"libcamera Source\"))\n \n #define TEMPLATE_CAPS GST_STATIC_CAPS(\"video/x-raw; image/jpeg; video/x-bayer\")\n \n@@ -225,8 +229,7 @@ int GstLibcameraSrcState::queueRequest()\n \treturn 0;\n }\n \n-void\n-GstLibcameraSrcState::requestCompleted(Request *request)\n+void GstLibcameraSrcState::requestCompleted(Request *request)\n {\n \tGST_DEBUG_OBJECT(src_, \"buffers are ready\");\n \n@@ -616,9 +619,109 @@ 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 = gst_video_orientation_to_libcamera_orientation(self->orientation);\n+\n+\t/* Save original configuration for comparison after validation */\n+\tstd::vector<StreamConfiguration> orig_stream_cfgs;\n+\tfor (gsize i = 0; i < state->config_->size(); i++)\n+\t\torig_stream_cfgs.push_back(state->config_->at(i));\n+\tstd::optional<SensorConfiguration> orig_sensor_cfg = state->config_->sensorConfig;\n+\tOrientation orig_orientation = state->config_->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\tbool warned = false;\n+\t\t// Warn if number of StreamConfigurations changed\n+\t\tif (orig_stream_cfgs.size() != state->config_->size()) {\n+\t\t\tGST_WARNING_OBJECT(self, \"Number of StreamConfiguration elements changed: requested=%zu, actual=%zu\",\n+\t\t\t\t\t   orig_stream_cfgs.size(), state->config_->size());\n+\t\t\twarned = true;\n+\t\t}\n+\t\t// Warn about changes in each StreamConfiguration\n+\t\t// TODO implement diffing in StreamConfiguration\n+\t\tfor (gsize i = 0; i < std::min(orig_stream_cfgs.size(), state->config_->size()); i++) {\n+\t\t\tif (orig_stream_cfgs[i].toString() != state->config_->at(i).toString()) {\n+\t\t\t\tGST_WARNING_OBJECT(self, \"StreamConfiguration %zu changed: %s -> %s\",\n+\t\t\t\t\t\t   i, orig_stream_cfgs[i].toString().c_str(),\n+\t\t\t\t\t\t   state->config_->at(i).toString().c_str());\n+\t\t\t\twarned = true;\n+\t\t\t}\n+\t\t}\n+\t\t// Warn about SensorConfiguration changes\n+\t\t// TODO implement diffing in SensorConfiguration\n+\t\tif (orig_sensor_cfg.has_value() || state->config_->sensorConfig.has_value()) {\n+\t\t\tconst SensorConfiguration *orig = orig_sensor_cfg.has_value() ? &orig_sensor_cfg.value() : nullptr;\n+\t\t\tconst SensorConfiguration *curr = state->config_->sensorConfig.has_value() ? &state->config_->sensorConfig.value() : nullptr;\n+\t\t\tbool sensor_changed = false;\n+\t\t\tstd::ostringstream diff;\n+\t\t\tif ((orig == nullptr) != (curr == nullptr)) {\n+\t\t\t\tdiff << \"SensorConfiguration presence changed: \"\n+\t\t\t\t     << (orig ? \"was present\" : \"was absent\")\n+\t\t\t\t     << \" -> \"\n+\t\t\t\t     << (curr ? \"present\" : \"absent\");\n+\t\t\t\tsensor_changed = true;\n+\t\t\t} else if (orig && curr) {\n+\t\t\t\tif (orig->bitDepth != curr->bitDepth) {\n+\t\t\t\t\tdiff << \"bitDepth: \" << orig->bitDepth << \" -> \" << curr->bitDepth << \"; \";\n+\t\t\t\t\tsensor_changed = true;\n+\t\t\t\t}\n+\t\t\t\tif (orig->analogCrop != curr->analogCrop) {\n+\t\t\t\t\tdiff << \"analogCrop: \" << orig->analogCrop.toString() << \" -> \" << curr->analogCrop.toString() << \"; \";\n+\t\t\t\t\tsensor_changed = true;\n+\t\t\t\t}\n+\t\t\t\tif (orig->binning.binX != curr->binning.binX ||\n+\t\t\t\t    orig->binning.binY != curr->binning.binY) {\n+\t\t\t\t\tdiff << \"binning: (\" << orig->binning.binX << \",\" << orig->binning.binY << \") -> (\"\n+\t\t\t\t\t     << curr->binning.binX << \",\" << curr->binning.binY << \"); \";\n+\t\t\t\t\tsensor_changed = true;\n+\t\t\t\t}\n+\t\t\t\tif (orig->skipping.xOddInc != curr->skipping.xOddInc ||\n+\t\t\t\t    orig->skipping.xEvenInc != curr->skipping.xEvenInc ||\n+\t\t\t\t    orig->skipping.yOddInc != curr->skipping.yOddInc ||\n+\t\t\t\t    orig->skipping.yEvenInc != curr->skipping.yEvenInc) {\n+\t\t\t\t\tdiff << \"skipping: (\"\n+\t\t\t\t\t     << orig->skipping.xOddInc << \",\" << orig->skipping.xEvenInc << \",\"\n+\t\t\t\t\t     << orig->skipping.yOddInc << \",\" << orig->skipping.yEvenInc << \") -> (\"\n+\t\t\t\t\t     << curr->skipping.xOddInc << \",\" << curr->skipping.xEvenInc << \",\"\n+\t\t\t\t\t     << curr->skipping.yOddInc << \",\" << curr->skipping.yEvenInc << \"); \";\n+\t\t\t\t\tsensor_changed = true;\n+\t\t\t\t}\n+\t\t\t\tif (orig->outputSize != curr->outputSize) {\n+\t\t\t\t\tdiff << \"outputSize: \" << orig->outputSize.toString() << \" -> \" << curr->outputSize.toString() << \"; \";\n+\t\t\t\t\tsensor_changed = true;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (sensor_changed) {\n+\t\t\t\tGST_WARNING_OBJECT(self, \"SensorConfiguration changed: %s\", diff.str().c_str());\n+\t\t\t\twarned = true;\n+\t\t\t}\n+\t\t}\n+\t\t// Warn about orientation change\n+\t\tif (orig_orientation != state->config_->orientation) {\n+\t\t\tGEnumClass *enum_class = (GEnumClass *)g_type_class_ref(GST_TYPE_VIDEO_ORIENTATION_METHOD);\n+\t\t\tconst char *orig_orientation_str = g_enum_get_value(enum_class, libcamera_orientation_to_gst_video_orientation(orig_orientation))->value_nick;\n+\t\t\tconst char *new_orientation_str = g_enum_get_value(enum_class, libcamera_orientation_to_gst_video_orientation(state->config_->orientation))->value_nick;\n+\t\t\tGST_WARNING_OBJECT(self, \"Orientation changed: %s -> %s\", orig_orientation_str, new_orientation_str);\n+\t\t\twarned = true;\n+\t\t}\n+\t\tif (!warned) {\n+\t\t\tGST_DEBUG_OBJECT(self, \"Camera configuration adjusted, but no significant changes detected.\");\n+\t\t}\n+\t\t// Update Gst orientation property to match adjusted config\n+\t\tself->orientation = libcamera_orientation_to_gst_video_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 +1029,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 = (GstVideoOrientationMethod)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 +1051,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@@ -1148,12 +1257,17 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)\n \n \tGParamSpec *spec = g_param_spec_string(\"camera-name\", \"Camera Name\",\n \t\t\t\t\t       \"Select by name which camera to use.\", nullptr,\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+\t\t\t\t\t       (GParamFlags)(GST_PARAM_MUTABLE_READY | G_PARAM_CONSTRUCT | G_PARAM_READWRITE | 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+\tspec = g_param_spec_enum(\"orientation\", \"Orientation\",\n+\t\t\t\t \"Select the orientation of the camera.\",\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 | G_PARAM_CONSTRUCT | G_PARAM_READWRITE | 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":["v3"]}