From patchwork Thu Jun 26 10:08:38 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Giacomo Cappellini X-Patchwork-Id: 23666 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id EC6B0C3237 for ; Thu, 26 Jun 2025 10:10:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D04F168DF3; Thu, 26 Jun 2025 12:10:24 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="RTGk9UQV"; dkim-atps=neutral Received: from mail-ej1-x636.google.com (mail-ej1-x636.google.com [IPv6:2a00:1450:4864:20::636]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DE6B468DE8 for ; Thu, 26 Jun 2025 12:10:22 +0200 (CEST) Received: by mail-ej1-x636.google.com with SMTP id a640c23a62f3a-ae0d935020eso87803466b.3 for ; Thu, 26 Jun 2025 03:10:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1750932622; x=1751537422; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=u3SS2GCqEPz4/ckzz3DpcY0JlTQS7a5pyGdjXcFNizo=; b=RTGk9UQV0Rnh7edjz4k8FT1IiL7/QM/gVno6f6U1524snzIGjX3qQsukxvr9wSwSq5 38+9AE9HG8YPYGfnRk4BSb4zQ1/BN2oce7q9CaEPPDxL8srevoLVfCpvuti15tOkuBEX NhpjkkQMOmbjlS+7XtEQb0oSzCkN9/eTkaymYemL1Ta8ZIW5axEGpHr89uZ2qi19jnmK VqH6K+dCnohFo2qRMd6qXKjrLWHQvE7P2/hTSUKk3ZcxNQ5FuRUTKyUcxBpyVCtpBMzU AZLZy0+a7kuVVFpL6PuGxccZjBc9RRO1jVu5nabdaT+oDsQ4J1IADav8CLTiyNIRE8xx 8kyg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750932622; x=1751537422; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=u3SS2GCqEPz4/ckzz3DpcY0JlTQS7a5pyGdjXcFNizo=; b=XMz+cjqOIyatjzZ57s7lHFtFDG0h43q0UhGg5ZJoEStEuSEGDs01o8gofegfhrYUUG rBscIqQ5l7EMAur9WR7bHgLtrBWccat5KMqDlLhe9aSm5GfdKCoWYhJ+TfNfpdLgy/Rv smbY+8+KKZW50xKp5GTxbQRbXeRShR9x7Ns7h2QMqDoaT/QwQPwNDUafb0UxxogTW6AL GWtuRJmmh//em/XX0nBx+N/hij96ECFRddrV0SxoZZsC3mPQtOv6982hoZF3aP0f1QPH tl/O1RIx2/fNI5+OLbKQwfrD0ua1uPuknRNCC7ZegW0RwcpE7/OoZiUGXw23hQmEJcTP a78Q== X-Gm-Message-State: AOJu0YzMS6bt+esnewsL2cEDtnr5pp7unKe9FBMeIwK3gGGAae2NWqcp b8gR5PiZ0TRAlEMPBPOnz8cBuBQ51xI9N5GWufHSR2Wj2g5xE0CSD9GI/LQRT0Am X-Gm-Gg: ASbGnctA6h1GwkXZ7RJr+Uh9pu6lCKm4bzaVU+cOhnrMk2xY/mIVbO53lM8if18ysFC jPnU1Fae+F29eJks4chkdJwsZOtGXW4UwbWzpG0YS5Fj2mmxcldJzvf+BhpYAcxlPoeBvYs34ln 3bfbg1DIOy8ife3GQ1ZC0ZHKlhgU8ErtFnb3wXUOyF57VJvRTvKIEHBHa9mIwXWI69b2xk9iFys J3eUh8w21VLtyhC7GMQqhE+hFTISKJxRLtUQ2Dkj8ET8ZcTa/qww3xYUUvovsf1ozLnxkmg4EGf wVZIHYi8zviVtgIx9QBygy3rBcHFBg/cmAvZNCLiOkHJiohA14FMHCe6E1xLGk9mYGKtCWerYaH Z4opHAvLDuv5A19HVnatXo0J96qBtEA9HXrxKWg6t4TEgCpApySWuZs6tUrjilg== X-Google-Smtp-Source: AGHT+IHFIdeuEhlwbeb2FtJTHh1LhFXqBuJvvbByphWv3Duj7vYgQ62q4Y0qhpf2SGkWQIkaHBcqdQ== X-Received: by 2002:a17:907:310d:b0:ae0:a912:f366 with SMTP id a640c23a62f3a-ae0bee7bd49mr493908466b.54.1750932621848; Thu, 26 Jun 2025 03:10:21 -0700 (PDT) Received: from jasus.ad.servtec.it (host-95-251-230-143.retail.telecomitalia.it. [95.251.230.143]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ae0542110f8sm1188425866b.182.2025.06.26.03.10.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 26 Jun 2025 03:10:21 -0700 (PDT) From: Giacomo Cappellini To: libcamera-devel@lists.libcamera.org Cc: Giacomo Cappellini Subject: [PATCH v2] gstreamer: Adding static rotation property to GstLibcameraDevice and mutable orientation property to GstLibcameraSrc 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 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" - rotation: is a static camera property reported through Camera::properties(). This is exposed in GstLibcameraDevice as an extra property. You can read it with gst-device-monitor-1.0 or GstDevice::gst_device_get_properties. - orientation: is a configurable parameter of the camera that only concerns with the actual images rotation. This is exposed as a GST_PARAM_MUTABLE_READY parameter of GstLibcameraSrc. It gets validated via CameraConfiguation::validate(). If the camera doesn't support it and CameraConfiguration::Adjusted is returned the adjusted configuration will be logged as a warning. --- src/gstreamer/gstlibcameraprovider.cpp | 5 ++ src/gstreamer/gstlibcamerasrc.cpp | 98 +++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/gstreamer/gstlibcameraprovider.cpp b/src/gstreamer/gstlibcameraprovider.cpp index 5da96ea3..d3384c57 100644 --- a/src/gstreamer/gstlibcameraprovider.cpp +++ b/src/gstreamer/gstlibcameraprovider.cpp @@ -10,6 +10,7 @@ #include "gstlibcameraprovider.h" +#include #include #include @@ -131,6 +132,7 @@ gst_libcamera_device_new(const std::shared_ptr &camera) static const std::array roles{ StreamRole::VideoRecording }; g_autoptr(GstCaps) caps = gst_caps_new_empty(); const gchar *name = camera->id().c_str(); + const int32_t rotation = camera->properties().get(libcamera::properties::Rotation).value_or(0); std::unique_ptr config = camera->generateConfiguration(roles); if (!config || config->size() != roles.size()) { @@ -150,6 +152,9 @@ gst_libcamera_device_new(const std::shared_ptr &camera) "display-name", name, "caps", caps, "device-class", "Source/Video", + "properties", gst_structure_new("device-properties", + "rotation", G_TYPE_INT, rotation, + NULL), nullptr)); } diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index 3aca4eed..fe3249ff 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -146,6 +147,7 @@ struct _GstLibcameraSrc { GstTask *task; gchar *camera_name; + Orientation orientation; std::atomic pending_eos; @@ -157,6 +159,7 @@ struct _GstLibcameraSrc { enum { PROP_0, PROP_CAMERA_NAME, + PROP_ORIENTATION, PROP_LAST }; @@ -616,9 +619,37 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self) gst_libcamera_get_framerate_from_caps(caps, element_caps); } + /* Set orientation control. */ + state->config_->orientation = self->orientation; + /* Validate the configuration. */ - if (state->config_->validate() == CameraConfiguration::Invalid) + switch(state->config_->validate()) { + case CameraConfiguration::Valid: + GST_DEBUG_OBJECT(self, "Camera configuration is valid"); + break; + case CameraConfiguration::Adjusted: + { + /* + * The configuration has been adjusted and is now valid. + * Parameters may have changed for any stream, and stream configurations may have been removed. + * Log the adjusted configuration. + */ + for (gsize i = 0; i < state->config_->size(); i++) { + const StreamConfiguration &cfg = state->config_->at(i); + GST_WARNING_OBJECT(self, "Camera configuration adjusted for stream %zu: %s", i, cfg.toString().c_str()); + } + std::ostringstream oss; + oss << state->config_->orientation; + GST_WARNING_OBJECT(self, "Camera configuration adjusted orientation: %s", oss.str().c_str()); + self->orientation = state->config_->orientation; + break; + } + case CameraConfiguration::Invalid: + GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, + ("Camera configuration is not supported"), + ("CameraConfiguration::validate() returned Invalid")); return false; + } int ret = state->cam_->configure(state->config_.get()); if (ret) { @@ -926,6 +957,9 @@ 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_ORIENTATION: + self->orientation = (libcamera::Orientation)g_value_get_enum(value); + break; default: if (!state->controls_.setProperty(prop_id - PROP_LAST, value, pspec)) G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -945,6 +979,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_ORIENTATION: + g_value_set_enum(value, (gint)self->orientation); + break; default: if (!state->controls_.getProperty(prop_id - PROP_LAST, value, pspec)) G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -1120,6 +1157,53 @@ gst_libcamera_src_release_pad(GstElement *element, GstPad *pad) gst_element_remove_pad(element, pad); } +static GType +gst_libcamera_orientation_get_type() +{ + static GType type = 0; + static const GEnumValue values[] = { + { + static_cast(libcamera::Orientation::Rotate0), + "libcamera::Orientation::Rotate0", + "rotate-0", + }, { + static_cast(libcamera::Orientation::Rotate0Mirror), + "libcamera::Orientation::Rotate0Mirror", + "rotate-0-mirror", + }, { + static_cast(libcamera::Orientation::Rotate180), + "libcamera::Orientation::Rotate180", + "rotate-180", + }, { + static_cast(libcamera::Orientation::Rotate180Mirror), + "libcamera::Orientation::Rotate180Mirror", + "rotate-180-mirror", + }, { + static_cast(libcamera::Orientation::Rotate90Mirror), + "libcamera::Orientation::Rotate90Mirror", + "rotate-90-mirror", + }, { + static_cast(libcamera::Orientation::Rotate270), + "libcamera::Orientation::Rotate270", + "rotate-270", + }, { + static_cast(libcamera::Orientation::Rotate270Mirror), + "libcamera::Orientation::Rotate270Mirror", + "rotate-270-mirror", + }, { + static_cast(libcamera::Orientation::Rotate90), + "libcamera::Orientation::Rotate90", + "rotate-90", + }, + { 0, nullptr, nullptr } + }; + + if (!type) + type = g_enum_register_static("GstLibcameraOrientation", values); + + return type; +} + static void gst_libcamera_src_class_init(GstLibcameraSrcClass *klass) { @@ -1154,6 +1238,18 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass) | G_PARAM_STATIC_STRINGS)); g_object_class_install_property(object_class, PROP_CAMERA_NAME, spec); + /* Register the orientation enum type. */ + GType orientation_type = gst_libcamera_orientation_get_type(); + spec = g_param_spec_enum("orientation", "Orientation", + "Select the orientation of the camera.", + orientation_type, + static_cast(libcamera::Orientation::Rotate0), + (GParamFlags)(GST_PARAM_MUTABLE_READY + | G_PARAM_CONSTRUCT + | G_PARAM_READWRITE + | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property(object_class, PROP_ORIENTATION, spec); + GstCameraControls::installProperties(object_class, PROP_LAST); }