From patchwork Wed Mar 25 09:47:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 26332 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 DE372BD87C for ; Wed, 25 Mar 2026 09:48:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D6DFA627A7; Wed, 25 Mar 2026 10:48:35 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="kMwmQZxl"; dkim-atps=neutral Received: from mail-wr1-x42a.google.com (mail-wr1-x42a.google.com [IPv6:2a00:1450:4864:20::42a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1829D62791 for ; Wed, 25 Mar 2026 10:48:34 +0100 (CET) Received: by mail-wr1-x42a.google.com with SMTP id ffacd0b85a97d-43b4915161fso4621260f8f.2 for ; Wed, 25 Mar 2026 02:48:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1774432113; x=1775036913; 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=x/iqV879zAFn8TLhOPPoOnXH1prlabfd3brBut4X4qc=; b=kMwmQZxlQ9Dm27gIYZQgcJlUMjX0u0OMBX4SbaVbTHOBk13kWjtfBdckDQWmFMvIRE nOg/0oNI/8FpnuSBiDcl4cNSx8KaSxlrq6WlA/WANDjwQKODiD6VFdB5xBwUMqpjWJ3O wv0Iq13PepmVHvgPDMJznLla8p3vzJ1G1OJXcCeoUWaOYbrAThLSj/GFu5Gd1qyILItG N5lIsRrmvaDRljkHNHhyByogwWm81UdiG9wP1zJSMXItqLzX5PmmFz5nBpBuVTJvpo9C +vNxXj0Sm5v7F2R6ti0qrIlgownIZDM39037Ncg0XAqhqkfywMmHUdkqJYvP0KJYZPNq rCUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774432113; x=1775036913; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=x/iqV879zAFn8TLhOPPoOnXH1prlabfd3brBut4X4qc=; b=qFewJ9nWmb1HMUVGqvl9UKyaw9apUd1383CH33WlGDFbs3hrbSeYdiusIahU77M5I0 GM0v6bVAgK5CSFBjNj2PO1We4F3lwsM8uh5MZMew1mXaifElUoHYBePQ009QsREwx+xY 5KDtrR+5QiVcdssvQjZZhDPuepY+ro1Xp2BFEdoHU6eI0QMVRnfqa6lKOv6cE6MLvCg+ GUqvxrLWXuaZj8WU8eS5yLlMQp7hTtpOJKpxCmWyE2BpNj8oBsr26ouaPTYiRzjUrI8J DL/MIhlH2QK+seDCfOQVyl/Pa1Fj/dNv7f3gZoRbPor2hphkrDfaXlninH8HZcz2CqMr VOWQ== X-Gm-Message-State: AOJu0YwfSVqyEGGHShHe7QZmsJ9BmQG+WYkWR8O3oCrPxSKMM+1DGf34 HrCKVQPIsS+WL1lwYf5oJe1dGySd+7F7GfwWINWUtNPlq5LI2bEZW0NHPZLg0gqSYAaSlcDFVw5 DdzZF X-Gm-Gg: ATEYQzwIyguxEgqWIJdNxoMC+H0uT3aq2Gw+pdn2s5Nq2ky88M3zDl02uJ232yxDYK7 43lLlTYCXyyQ+cxeXQA1gwq5KpxNyPSa826W+VLpC4zst8Atplx52Cko8GVEuk9LQfckZ5DjTw/ SIOi57yT2DYUbEoQjuehXkwzKXoRj6lcAg5wHic7pQl7s2oNppodgl4XWMbwPx+6S7ukPka6ZHB VmuUDiIp5BjK0Aw15pVy/Us9vxkkZJv/5EHR1fXzewh0FE2BpMAHTrcu8ZMY5MxYKeUAg+KhVLQ gA7UOiYeOXDGexI/7UR1M/Zhrg9U70iXnjxNQi00YhMZRr4awbjksDk/A/XZgQe1mq5ELpcivdY zekBc/i0+7oVE8XsXDj2dm4X8L+rB00hoKO2RLZo0LpQHnhpdcNQHLugRqJkMKhx49RxQ4fhYv+ tn11IfaIjASm/+bZD1NUMZvjndyyops8+VoFIGxKghzzELOkXnNEiInEAAyQQ8rh8FnupdCFxkp K3hogNUJ01ZwyA4p4yZYhaOqdAtKoPYqcd76OEGVg== X-Received: by 2002:a5d:64e6:0:b0:43b:4ae5:d7c8 with SMTP id ffacd0b85a97d-43b8899530fmr3792795f8f.2.1774432113164; Wed, 25 Mar 2026 02:48:33 -0700 (PDT) Received: from davidp-pi5.pitowers.org ([2a00:1098:3142:1f:88ea:c658:5b20:5e46]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43b644ae37dsm47651682f8f.2.2026.03.25.02.48.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 25 Mar 2026 02:48:32 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Cc: David Plowman , Nicolas Dufresne Subject: [PATCH v3] gstreamer: Add sensor-config property Date: Wed, 25 Mar 2026 09:47:54 +0000 Message-ID: <20260325094829.9119-1-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.47.3 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" The sensor-config property may optionally be specified to give the outputSize and bitDepth of the SensorConfiguration that is applied to the camera configuration. For example, use libcamerasrc sensor-config="sensor/config,width=2304,height=1296,depth=10" to request the 10-bit 2304x1296 mode of a sensor. All three parameters, width, height and bit depth, must be present, or it will issue a warning and revert to automatic mode selection (as if there were no sensor-config at all). If a mode is requested that doesn't exist then camera configuration will fail and the pipeline won't start. As future-proofing, the SensorConfiguration's binning and increment parameters may be specified, though libcamera currently ignores them. Signed-off-by: David Plowman Reviewed-by: Nicolas Dufresne Closes: https://gitlab.freedesktop.org/camera/libcamera/-/issues/300 --- src/gstreamer/gstlibcamerasrc.cpp | 61 +++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index a7241e9b..482b4761 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -141,6 +141,7 @@ struct _GstLibcameraSrc { GstTask *task; gchar *camera_name; + GstStructure *sensor_config; std::atomic pending_eos; @@ -152,6 +153,7 @@ struct _GstLibcameraSrc { enum { PROP_0, PROP_CAMERA_NAME, + PROP_SENSOR_CONFIG, PROP_LAST }; @@ -846,6 +848,44 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread, } g_assert(state->config_->size() == state->srcpads_.size()); + /* Apply optional sensor configuration. */ + if (self->sensor_config) { + gint w = 0, h = 0, depth = 0; + if (!gst_structure_get_int(self->sensor_config, "width", &w) || + !gst_structure_get_int(self->sensor_config, "height", &h) || + !gst_structure_get_int(self->sensor_config, "depth", &depth) || + w <= 0 || h <= 0 || depth <= 0) { + GST_ELEMENT_WARNING(self, RESOURCE, SETTINGS, + ("sensor-config requires non-zero width, height and depth" + " fields, ignoring"), + (nullptr)); + } else { + SensorConfiguration sensorCfg; + sensorCfg.outputSize = Size(w, h); + sensorCfg.bitDepth = depth; + + /* Optional binning (defaults to 1x1). */ + gint binX, binY; + if (gst_structure_get_int(self->sensor_config, "bin-x", &binX) && binX > 0) + sensorCfg.binning.binX = binX; + if (gst_structure_get_int(self->sensor_config, "bin-y", &binY) && binY > 0) + sensorCfg.binning.binY = binY; + + /* Optional skipping (defaults to 1 for all increments). */ + gint xOddInc, xEvenInc, yOddInc, yEvenInc; + if (gst_structure_get_int(self->sensor_config, "x-odd-inc", &xOddInc) && xOddInc > 0) + sensorCfg.skipping.xOddInc = xOddInc; + if (gst_structure_get_int(self->sensor_config, "x-even-inc", &xEvenInc) && xEvenInc > 0) + sensorCfg.skipping.xEvenInc = xEvenInc; + if (gst_structure_get_int(self->sensor_config, "y-odd-inc", &yOddInc) && yOddInc > 0) + sensorCfg.skipping.yOddInc = yOddInc; + if (gst_structure_get_int(self->sensor_config, "y-even-inc", &yEvenInc) && yEvenInc > 0) + sensorCfg.skipping.yEvenInc = yEvenInc; + + state->config_->sensorConfig = sensorCfg; + } + } + if (!gst_libcamera_src_negotiate(self)) { state->initControls_.clear(); GST_ELEMENT_FLOW_ERROR(self, GST_FLOW_NOT_NEGOTIATED); @@ -934,6 +974,10 @@ 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_SENSOR_CONFIG: + g_clear_pointer(&self->sensor_config, gst_structure_free); + self->sensor_config = (GstStructure *)g_value_dup_boxed(value); + break; default: if (!state->controls_.setProperty(prop_id - PROP_LAST, value, pspec)) G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -953,6 +997,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_SENSOR_CONFIG: + g_value_set_boxed(value, self->sensor_config); + break; default: if (!state->controls_.getProperty(prop_id - PROP_LAST, value, pspec)) G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -1040,6 +1087,7 @@ gst_libcamera_src_finalize(GObject *object) g_clear_object(&self->task); g_mutex_clear(&self->state->lock_); g_free(self->camera_name); + g_clear_pointer(&self->sensor_config, gst_structure_free); delete self->state; return klass->finalize(object); @@ -1162,6 +1210,19 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass) | G_PARAM_STATIC_STRINGS)); g_object_class_install_property(object_class, PROP_CAMERA_NAME, spec); + spec = g_param_spec_boxed("sensor-config", "Sensor Config", + "Desired sensor configuration as a GstStructure with mandatory " + "fields width, height and depth, and optional fields bin-x, bin-y, " + "x-odd-inc, x-even-inc, y-odd-inc, y-even-inc " + "(e.g. \"sensor/config,width=2304,height=1296,depth=10\"). " + "Leave unset to let the pipeline negotiate the sensor mode automatically.", + GST_TYPE_STRUCTURE, + (GParamFlags)(GST_PARAM_MUTABLE_READY + | G_PARAM_CONSTRUCT + | G_PARAM_READWRITE + | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property(object_class, PROP_SENSOR_CONFIG, spec); + GstCameraControls::installProperties(object_class, PROP_LAST); }