[RFC,05/12] libcamera: sensor: Add CameraSensorMemory class
diff mbox series

Message ID 20250827090739.86955-6-david.plowman@raspberrypi.com
State New
Headers show
Series
  • Bayer Re-Processing
Related show

Commit Message

David Plowman Aug. 27, 2025, 9:07 a.m. UTC
Representation of a "camera" that actually takes its input from
a memoery buffer.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
---
 include/libcamera/internal/camera_sensor.h    |   2 +
 .../libcamera/internal/camera_sensor_memory.h | 109 ++++++++
 include/libcamera/internal/meson.build        |   1 +
 src/libcamera/sensor/camera_sensor_memory.cpp | 237 ++++++++++++++++++
 src/libcamera/sensor/meson.build              |   1 +
 5 files changed, 350 insertions(+)
 create mode 100644 include/libcamera/internal/camera_sensor_memory.h
 create mode 100644 src/libcamera/sensor/camera_sensor_memory.cpp

Patch
diff mbox series

diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
index 13048f32..2b719d8c 100644
--- a/include/libcamera/internal/camera_sensor.h
+++ b/include/libcamera/internal/camera_sensor.h
@@ -48,6 +48,8 @@  public:
 
 	virtual CameraLens *focusLens() = 0;
 
+	virtual bool isMemory() const { return false; }
+
 	virtual const std::vector<unsigned int> &mbusCodes() const = 0;
 	virtual std::vector<Size> sizes(unsigned int mbusCode) const = 0;
 	virtual Size resolution() const = 0;
diff --git a/include/libcamera/internal/camera_sensor_memory.h b/include/libcamera/internal/camera_sensor_memory.h
new file mode 100644
index 00000000..4552bff7
--- /dev/null
+++ b/include/libcamera/internal/camera_sensor_memory.h
@@ -0,0 +1,109 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2025, Raspberry Pi plc
+ *
+ * camera_sensor_memory.h - A fake camera sensor for reading raw data from memory
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <libcamera/camera.h>
+
+#include "libcamera/internal/camera_sensor.h"
+
+namespace libcamera {
+
+class BayerFormat;
+class Camera;
+class CameraLens;
+class MediaEntity;
+class SensorConfiguration;
+
+struct CameraSensorProperties;
+
+enum class Orientation;
+
+LOG_DECLARE_CATEGORY(CameraSensor)
+
+class CameraSensorMemory : public CameraSensor, protected Loggable
+{
+public:
+	CameraSensorMemory(const StreamConfiguration &rawInput);
+	~CameraSensorMemory();
+
+	static std::variant<std::unique_ptr<CameraSensor>, int>
+	match(MediaEntity *entity);
+
+	const std::string &model() const override { return model_; }
+	const std::string &id() const override { return id_; }
+
+	const MediaEntity *entity() const override { return nullptr; }
+	V4L2Subdevice *device() override { return nullptr; }
+
+	CameraLens *focusLens() override { return nullptr; }
+
+	virtual bool isMemory() const override { return true; }
+
+	const std::vector<unsigned int> &mbusCodes() const override;
+	std::vector<Size> sizes(unsigned int mbusCode) const override;
+	Size resolution() const override;
+
+	V4L2SubdeviceFormat getFormat(const std::vector<unsigned int> &mbusCodes,
+				      const Size &size,
+				      const Size maxSize) const override;
+	int setFormat(V4L2SubdeviceFormat *format,
+		      Transform transform = Transform::Identity) override;
+	int tryFormat(V4L2SubdeviceFormat *format) const override;
+
+	int applyConfiguration(const SensorConfiguration &config,
+			       Transform transform = Transform::Identity,
+			       V4L2SubdeviceFormat *sensorFormat = nullptr) override;
+
+	V4L2Subdevice::Stream imageStream() const override;
+	std::optional<V4L2Subdevice::Stream> embeddedDataStream() const override;
+	V4L2SubdeviceFormat embeddedDataFormat() const override;
+	int setEmbeddedDataEnabled(bool enable) override;
+
+	const ControlList &properties() const override;
+	int sensorInfo(IPACameraSensorInfo *info) const override;
+	Transform computeTransform(Orientation *orientation) const override;
+	BayerFormat::Order bayerOrder(Transform t) const override;
+
+	const ControlInfoMap &controls() const override;
+	ControlList getControls(const std::vector<uint32_t> &ids) override;
+	int setControls(ControlList *ctrls) override;
+
+	const std::vector<controls::draft::TestPatternModeEnum> &
+	testPatternModes() const override { return testPatternModes_; }
+	int setTestPatternMode(controls::draft::TestPatternModeEnum mode) override;
+	const CameraSensorProperties::SensorDelays &sensorDelays() override;
+
+protected:
+	std::string logPrefix() const override;
+
+private:
+	LIBCAMERA_DISABLE_COPY(CameraSensorMemory)
+
+	StreamConfiguration rawInput_;
+
+	std::string model_;
+	std::string id_;
+
+	BayerFormat bayerFormat_;
+	std::vector<unsigned int> mbusCodes_;
+
+	V4L2SubdeviceFormat v4l2SubdeviceFormat_;
+
+	ControlInfoMap propertiesInfoMap_;
+	ControlInfoMap controlsInfoMap_;
+	ControlList properties_;
+	ControlList controls_;
+
+	std::vector<controls::draft::TestPatternModeEnum> testPatternModes_;
+};
+
+} /* namespace libcamera */
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
index 5c80a28c..bba9df4f 100644
--- a/include/libcamera/internal/meson.build
+++ b/include/libcamera/internal/meson.build
@@ -10,6 +10,7 @@  libcamera_internal_headers = files([
     'camera_lens.h',
     'camera_manager.h',
     'camera_sensor.h',
+    'camera_sensor_memory.h',
     'camera_sensor_properties.h',
     'clock_recovery.h',
     'control_serializer.h',
diff --git a/src/libcamera/sensor/camera_sensor_memory.cpp b/src/libcamera/sensor/camera_sensor_memory.cpp
new file mode 100644
index 00000000..dcb8679f
--- /dev/null
+++ b/src/libcamera/sensor/camera_sensor_memory.cpp
@@ -0,0 +1,237 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2025, Raspberry Pi plc
+ *
+ * camera_sensor_memory.cpp - A fake camera sensor for reading raw data from memory
+ */
+
+#include "libcamera/internal/camera_sensor_memory.h"
+
+#include <algorithm>
+#include <map>
+#include <sstream>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+#include <libcamera/geometry.h>
+#include <libcamera/orientation.h>
+#include <libcamera/property_ids.h>
+#include <libcamera/transform.h>
+
+#include <libcamera/ipa/core_ipa_interface.h>
+
+#include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/formats.h"
+#include "libcamera/internal/v4l2_subdevice.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(CameraSensor)
+
+static bool v4l2SubdeviceFormatEqual(const V4L2SubdeviceFormat &lhs, const V4L2SubdeviceFormat &rhs)
+{
+	return lhs.code == rhs.code && lhs.size == rhs.size && lhs.colorSpace == rhs.colorSpace;
+}
+
+CameraSensorMemory::CameraSensorMemory(const StreamConfiguration &rawInput)
+	: rawInput_(rawInput), properties_(propertiesInfoMap_), controls_(controlsInfoMap_)
+{
+	model_ = "memory";
+
+	std::ostringstream oss;
+	oss << &rawInput;
+	id_ = oss.str();
+
+	/* The "camera" must appear to return the format the raw input wants. */
+	bayerFormat_ = BayerFormat::fromPixelFormat(rawInput.pixelFormat);
+	unsigned int mbusCode = bayerFormat_.toMbusCode();
+	mbusCodes_ = { mbusCode };
+
+	v4l2SubdeviceFormat_ = V4L2SubdeviceFormat{
+		.code = mbusCode,
+		.size = rawInput.size,
+		.colorSpace = ColorSpace::Raw,
+	};
+}
+
+CameraSensorMemory::~CameraSensorMemory() = default;
+
+std::variant<std::unique_ptr<CameraSensor>, int>
+CameraSensorMemory::match([[maybe_unused]] MediaEntity *entity)
+{
+	return {};
+}
+
+const std::vector<unsigned int> &CameraSensorMemory::mbusCodes() const
+{
+	return mbusCodes_;
+}
+
+std::vector<Size> CameraSensorMemory::sizes(unsigned int mbusCode) const
+{
+	if (mbusCode == mbusCodes_[0])
+		return { rawInput_.size };
+	else
+		return {};
+}
+
+Size CameraSensorMemory::resolution() const
+{
+	return rawInput_.size;
+}
+
+V4L2SubdeviceFormat CameraSensorMemory::getFormat(const std::vector<unsigned int> &mbusCodes,
+						  [[maybe_unused]] const Size &size,
+						  const Size maxSize) const
+{
+	if (std::find(mbusCodes.begin(), mbusCodes.end(), mbusCodes_[0]) == mbusCodes.end())
+		return {};
+
+	if (maxSize.width < rawInput_.size.width || maxSize.height < rawInput_.size.height)
+		return {};
+
+	return v4l2SubdeviceFormat_;
+}
+
+int CameraSensorMemory::setFormat(V4L2SubdeviceFormat *format,
+				  Transform transform)
+{
+	if (v4l2SubdeviceFormatEqual(*format, v4l2SubdeviceFormat_) &&
+	    transform == Transform::Identity)
+		return 0;
+
+	return -EPERM;
+}
+
+int CameraSensorMemory::tryFormat(V4L2SubdeviceFormat *format) const
+{
+	if (v4l2SubdeviceFormatEqual(*format, v4l2SubdeviceFormat_))
+		return 0;
+
+	return -EPERM;
+}
+
+int CameraSensorMemory::applyConfiguration(const SensorConfiguration &config,
+					   Transform transform,
+					   V4L2SubdeviceFormat *sensorFormat)
+{
+	if (config.bitDepth != bayerFormat_.bitDepth ||
+	    config.outputSize != rawInput_.size ||
+	    config.binning.binX != 1 || config.binning.binY != 1 ||
+	    config.skipping.xOddInc != 1 || config.skipping.xEvenInc != 1 ||
+	    config.skipping.yOddInc != 1 || config.skipping.yEvenInc != 1 ||
+	    transform != Transform::Identity)
+		return -EPERM;
+
+	if (sensorFormat)
+		*sensorFormat = v4l2SubdeviceFormat_;
+
+	return 0;
+}
+
+V4L2Subdevice::Stream CameraSensorMemory::imageStream() const
+{
+	return V4L2Subdevice::Stream();
+}
+
+std::optional<V4L2Subdevice::Stream> CameraSensorMemory::embeddedDataStream() const
+{
+	return {};
+}
+
+V4L2SubdeviceFormat CameraSensorMemory::embeddedDataFormat() const
+{
+	return {};
+}
+
+int CameraSensorMemory::setEmbeddedDataEnabled(bool enable)
+{
+	return enable ? -ENOSTR : 0;
+}
+
+const ControlList &CameraSensorMemory::properties() const
+{
+	return properties_;
+}
+
+int CameraSensorMemory::sensorInfo([[maybe_unused]] IPACameraSensorInfo *info) const
+{
+	info->model = model();
+
+	info->bitsPerPixel = bayerFormat_.bitDepth;
+	info->cfaPattern = properties::draft::RGB;
+
+	info->activeAreaSize = rawInput_.size;
+	info->analogCrop = Rectangle(rawInput_.size);
+	info->outputSize = rawInput_.size;
+
+	/*
+	 * These are meaningless for us, fill with ones rather than zeros because the
+	 * code will divide by some of these numbers.
+	 */
+	info->pixelRate = 1;
+	info->minLineLength = 1;
+	info->maxLineLength = 1;
+	info->minFrameLength = 1;
+	info->maxFrameLength = 1;
+
+	return 0;
+}
+
+Transform CameraSensorMemory::computeTransform(Orientation *orientation) const
+{
+	*orientation = Orientation::Rotate0;
+	return Transform::Identity;
+}
+
+BayerFormat::Order CameraSensorMemory::bayerOrder([[maybe_unused]] Transform t) const
+{
+	return bayerFormat_.order;
+}
+
+const ControlInfoMap &CameraSensorMemory::controls() const
+{
+	return *controls_.infoMap();
+}
+
+ControlList CameraSensorMemory::getControls([[maybe_unused]] const std::vector<uint32_t> &ids)
+{
+	return ControlList();
+}
+
+int CameraSensorMemory::setControls([[maybe_unused]] ControlList *ctrls)
+{
+	return -EPERM;
+}
+
+int CameraSensorMemory::setTestPatternMode([[maybe_unused]] controls::draft::TestPatternModeEnum mode)
+{
+	return -EPERM;
+}
+
+const CameraSensorProperties::SensorDelays &CameraSensorMemory::sensorDelays()
+{
+	static constexpr CameraSensorProperties::SensorDelays defaultSensorDelays = {
+		.exposureDelay = 2,
+		.gainDelay = 1,
+		.vblankDelay = 2,
+		.hblankDelay = 2,
+	};
+
+	return defaultSensorDelays; /* but doesn't mean anything */
+}
+
+std::string CameraSensorMemory::logPrefix() const
+{
+	return "'memory'";
+}
+
+/*
+ * We're not going to register this camera sensor as it doesn't match media entities
+ * like other sensors. Pipeline handlers will have to call it explicitly.
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/sensor/meson.build b/src/libcamera/sensor/meson.build
index dce74ed6..b9b87612 100644
--- a/src/libcamera/sensor/meson.build
+++ b/src/libcamera/sensor/meson.build
@@ -3,6 +3,7 @@ 
 libcamera_internal_sources += files([
     'camera_sensor.cpp',
     'camera_sensor_legacy.cpp',
+    'camera_sensor_memory.cpp',
     'camera_sensor_properties.cpp',
     'camera_sensor_raw.cpp',
 ])