From patchwork Fri Jul 25 11:30:46 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Giacomo Cappellini X-Patchwork-Id: 23965 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 53B5DBDCC1 for ; Fri, 25 Jul 2025 11:57:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0EF10690CD; Fri, 25 Jul 2025 13:57:37 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Nw78qbvj"; dkim-atps=neutral Received: from mail-wm1-x32a.google.com (mail-wm1-x32a.google.com [IPv6:2a00:1450:4864:20::32a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 040A4690A6 for ; Fri, 25 Jul 2025 13:57:36 +0200 (CEST) Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-455b00339c8so14035695e9.3 for ; Fri, 25 Jul 2025 04:57:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1753444655; x=1754049455; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7+e4eFM5E7v0+yh0b04JNMHsjJumQN+ND2VuUhfIZiQ=; b=Nw78qbvjiHCbQ6ezmo8QgLNojukn8QJj0koYON46NXhS4nYBDgwhLVZt8nCC5011ej lYzj8nhpOWysGrEMLiH6w1HpHjqVnGaF9MNtX+CLmnlDvA5eSkI+0dkri079YTjnXVQe rWqWfxUw8+PiMNvCK4mpc3NEt5h4Dqsx/FfDbxUFYvx8zU3udMbMZrYJ04bSWG8XVnE9 j/acx2lPPm/TDZeqjDltYRmfXMtiVtNNlHEWN7dwwLGKygkTaiSVLFYXbcTL00jr7TZP KwJW4x8u9tQQ/p2n4iiMSX/nB5LbbaAxJgn0yHzgrRc5az0ohpyw3mt5ViWr/bZlimBb 2R6g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753444655; x=1754049455; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7+e4eFM5E7v0+yh0b04JNMHsjJumQN+ND2VuUhfIZiQ=; b=ePYCEdqpE4cwp6FWHY2eohxl/jEhLQf8T0iB2KgOEXAxrNtvVTMI7vspeejWkR7h1o +yTAsuYQUQ+ellgm794XQQcHoqGVtupdK3O08wz3Bx35Lugtm5sLp9rrEunAiUoq5txq DVlwHB5g6EhDunYXFynzqQ0WVtT1BWw2CFpXu6hFy9tGq334y+RfY5n8/wywRBdrY6Yw jeK3pf1LP60qTX43y9cXQceZhpZmr07p2v8XrnSFT4snmtarUKUAza8FS0tKOd7zlwBO P+LkND2sKYhu+UnDSFRTQxEuNlUqh9ucPctT5UqpwMk4w1YXUGwA8lFEMkRxHOzD76R6 BzlQ== X-Gm-Message-State: AOJu0Yygf15Q9tlfP32o/Kw8uLg22RZj193FGAwpImWEYNxQjNgLaJLq KYAlxmckyxwe8NROb6wKBmr4p8bFl527AtJkKtXW9SKO43lEkotbeW6a0Wj/TZnZ X-Gm-Gg: ASbGnctyWqcVca33yRh1aLR/os0y1Xu9ZR55E3/l4djBkdgBltPiG4Mn24XZNnlOHUt kEBw0apdSgDgAF4CUETZaI9HRqKgGei8ejMnhGG2hWtGKsw3TbST0O6NC4k2hcDs6tObkfk6kLQ 9yeRaKMrToAKOyy4pizwIFh2Sz1F6nflK/b8AyJJlNxMOESUY9//Wv39q7er4ZUQaOzkBBE+fXH BSsbydjIUZVZ6UBmur/yh8FGW+6fyGXZY7NKtp/iF9VI4y3FWErAaag6/354OygFa014GZjMKHR a24a2vSisPWXH4bK0JXMjRYo5u9ZqywwB8+Fwa2GPFh15ZWD/MEDv7jVIErL4WS/mNpTVF8Jdmy kki+Py0gGYm6LeyBnM14MwAvZHaTpRU9iN2nXOZkMTPiHOnSihSyRfDpkSBEANzXk5Pfs+ysjGE YbETrtOVylJqHoyvPkQ5PQRg== X-Google-Smtp-Source: AGHT+IEJVTRmFEtA1QcnxoLdI5t/CqTko8WEIMnygsJrzg/foyExqmOK0GICgVhNAtEPu+HnneuW4Q== X-Received: by 2002:a05:600c:45c6:b0:450:ceb2:67dd with SMTP id 5b1f17b1804b1-4587665dc28mr12657325e9.33.1753444655038; Fri, 25 Jul 2025 04:57:35 -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 5b1f17b1804b1-45870532af4sm52130805e9.4.2025.07.25.04.57.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 25 Jul 2025 04:57:34 -0700 (PDT) From: Giacomo Cappellini To: libcamera-devel@lists.libcamera.org Cc: Giacomo Cappellini Subject: [PATCH v4 1/1] gstreamer: Add support for Orientation Date: Fri, 25 Jul 2025 13:30:46 +0200 Message-ID: <20250725115602.1477743-2-giacomo.cappellini.87@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250725115602.1477743-1-giacomo.cappellini.87@gmail.com> References: <20250725115602.1477743-1-giacomo.cappellini.87@gmail.com> 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" libcamera allows to control the images orientation through the CameraConfiguration::orientation field, expose a GST_PARAM_MUTABLE_READY parameter of type GstVideoOrientationMethod in GstLibcameraSrc to control it. Parameter is mapped internally to libcamera::Orientation via new gstlibcamera-utils functions: - gst_video_orientation_to_libcamera_orientation - libcamera_orientation_to_gst_video_orientation Signed-off-by: Giacomo Cappellini --- src/gstreamer/gstlibcamera-utils.cpp | 58 ++++++++++++++++++++++------ src/gstreamer/gstlibcamera-utils.h | 4 ++ src/gstreamer/gstlibcamerasrc.cpp | 46 +++++++++++++++++----- 3 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp index a548b0c1..15069f98 100644 --- a/src/gstreamer/gstlibcamera-utils.cpp +++ b/src/gstreamer/gstlibcamera-utils.cpp @@ -10,6 +10,9 @@ #include #include +#include + +#include using namespace libcamera; @@ -20,7 +23,7 @@ static struct { /* Compressed */ { GST_VIDEO_FORMAT_ENCODED, formats::MJPEG }, - /* Bayer formats, gstreamer only supports 8-bit */ + /* Bayer formats */ { GST_VIDEO_FORMAT_ENCODED, formats::SBGGR8 }, { GST_VIDEO_FORMAT_ENCODED, formats::SGBRG8 }, { GST_VIDEO_FORMAT_ENCODED, formats::SGRBG8 }, @@ -317,20 +320,15 @@ bare_structure_from_format(const PixelFormat &format) return gst_structure_new("video/x-raw", "format", G_TYPE_STRING, gst_video_format_to_string(gst_format), nullptr); - switch (format) { - case formats::MJPEG: + if (format == formats::MJPEG) return gst_structure_new_empty("image/jpeg"); - case formats::SBGGR8: - case formats::SGBRG8: - case formats::SGRBG8: - case formats::SRGGB8: - return gst_structure_new("video/x-bayer", "format", G_TYPE_STRING, - bayer_format_to_string(format), nullptr); - - default: + const gchar *s = bayer_format_to_string(format); + if (s) + return gst_structure_new("video/x-bayer", "format", + G_TYPE_STRING, s, nullptr); + else return nullptr; - } } GstCaps * @@ -659,3 +657,39 @@ gst_libcamera_get_camera_manager(int &ret) return cm; } + +static const struct { + Orientation orientation; + GstVideoOrientationMethod method; +} orientation_map[]{ + { Orientation::Rotate0, GST_VIDEO_ORIENTATION_IDENTITY }, + { Orientation::Rotate90, GST_VIDEO_ORIENTATION_90R }, + { Orientation::Rotate180, GST_VIDEO_ORIENTATION_180 }, + { Orientation::Rotate270, GST_VIDEO_ORIENTATION_90L }, + { Orientation::Rotate0Mirror, GST_VIDEO_ORIENTATION_HORIZ }, + { Orientation::Rotate180Mirror, GST_VIDEO_ORIENTATION_VERT }, + { Orientation::Rotate90Mirror, GST_VIDEO_ORIENTATION_UL_LR }, + { Orientation::Rotate270Mirror, GST_VIDEO_ORIENTATION_UR_LL }, +}; + +Orientation +gst_video_orientation_to_libcamera_orientation(GstVideoOrientationMethod method) +{ + for (auto &b : orientation_map) { + if (b.method == method) + return b.orientation; + } + + return Orientation::Rotate0; +} + +GstVideoOrientationMethod +libcamera_orientation_to_gst_video_orientation(Orientation orientation) +{ + for (auto &a : orientation_map) { + if (a.orientation == orientation) + return a.method; + } + + return GST_VIDEO_ORIENTATION_IDENTITY; +} diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h index 5f4e8a0f..bbbd33db 100644 --- a/src/gstreamer/gstlibcamera-utils.h +++ b/src/gstreamer/gstlibcamera-utils.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -92,3 +93,6 @@ public: private: GRecMutex *mutex_; }; + +libcamera::Orientation gst_video_orientation_to_libcamera_orientation(GstVideoOrientationMethod method); +GstVideoOrientationMethod libcamera_orientation_to_gst_video_orientation(libcamera::Orientation orientation); diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index 3aca4eed..85add936 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include +#include #include "gstlibcamera-controls.h" #include "gstlibcamera-utils.h" @@ -146,6 +148,7 @@ struct _GstLibcameraSrc { GstTask *task; gchar *camera_name; + GstVideoOrientationMethod orientation; std::atomic pending_eos; @@ -157,6 +160,7 @@ struct _GstLibcameraSrc { enum { PROP_0, PROP_CAMERA_NAME, + PROP_ORIENTATION, PROP_LAST }; @@ -166,8 +170,8 @@ static void gst_libcamera_src_child_proxy_init(gpointer g_iface, G_DEFINE_TYPE_WITH_CODE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT, G_IMPLEMENT_INTERFACE(GST_TYPE_CHILD_PROXY, gst_libcamera_src_child_proxy_init) - GST_DEBUG_CATEGORY_INIT(source_debug, "libcamerasrc", 0, - "libcamera Source")) + GST_DEBUG_CATEGORY_INIT(source_debug, "libcamerasrc", 0, + "libcamera Source")) #define TEMPLATE_CAPS GST_STATIC_CAPS("video/x-raw; image/jpeg; video/x-bayer") @@ -225,8 +229,7 @@ int GstLibcameraSrcState::queueRequest() return 0; } -void -GstLibcameraSrcState::requestCompleted(Request *request) +void GstLibcameraSrcState::requestCompleted(Request *request) { GST_DEBUG_OBJECT(src_, "buffers are ready"); @@ -616,9 +619,17 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self) gst_libcamera_get_framerate_from_caps(caps, element_caps); } + /* Set orientation control. */ + state->config_->orientation = gst_video_orientation_to_libcamera_orientation(self->orientation); + /* Validate the configuration. */ - if (state->config_->validate() == CameraConfiguration::Invalid) + CameraConfiguration::Status status = state->config_->validate(); + if (status == CameraConfiguration::Invalid) return false; + else if (status == CameraConfiguration::Adjusted) + GST_ELEMENT_INFO(self, RESOURCE, SETTINGS, + ("Configuration was adjusted"), + ("CameraConfiguration::validate() returned CameraConfiguration::Adjusted")); int ret = state->cam_->configure(state->config_.get()); if (ret) { @@ -643,6 +654,10 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self) g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg, transfer[i]); gst_libcamera_framerate_to_caps(caps, element_caps); + if (status == CameraConfiguration::Adjusted && + !gst_pad_peer_query_accept_caps(srcpad, caps)) + return false; + if (!gst_pad_push_event(srcpad, gst_event_new_caps(caps))) return false; } @@ -730,7 +745,8 @@ gst_libcamera_src_task_run(gpointer user_data) if (gst_pad_check_reconfigure(srcpad)) { /* Check if the caps even need changing. */ g_autoptr(GstCaps) caps = gst_pad_get_current_caps(srcpad); - if (!gst_pad_peer_query_accept_caps(srcpad, caps)) { + g_autoptr(GstCaps) peercaps = gst_pad_peer_query_caps(srcpad, caps); + if (gst_caps_is_empty(peercaps)) { reconfigure = true; break; } @@ -926,6 +942,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 = (GstVideoOrientationMethod)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 +964,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); @@ -1148,12 +1170,16 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass) GParamSpec *spec = g_param_spec_string("camera-name", "Camera Name", "Select by name which camera to use.", nullptr, - (GParamFlags)(GST_PARAM_MUTABLE_READY - | G_PARAM_CONSTRUCT - | G_PARAM_READWRITE - | G_PARAM_STATIC_STRINGS)); + (GParamFlags)(GST_PARAM_MUTABLE_READY | G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property(object_class, PROP_CAMERA_NAME, spec); + /* Register the orientation enum type. */ + spec = g_param_spec_enum("orientation", "Orientation", + "Select the orientation of the camera.", + GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY, + (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); }