[libcamera-devel] [PATCH] libcamera: pipeline: Add Simple pipeline

Martijn Braam martijn at brixit.nl
Fri Oct 4 21:45:34 CEST 2019


This add a generic pipeline handler for simple pipelines. It currently
handles pipelines where there's only a PHY node and a sensor node and
the kernel sets up the graph.

The current implementation can deal with sunxi sun6i-csi pipelines but
other simple pipelines can be added to the infos array.

Signed-off-by: Martijn Braam <martijn at brixit.nl>
---
 src/libcamera/pipeline/meson.build |   1 +
 src/libcamera/pipeline/simple.cpp  | 457 +++++++++++++++++++++++++++++
 2 files changed, 458 insertions(+)
 create mode 100644 src/libcamera/pipeline/simple.cpp

diff --git a/src/libcamera/pipeline/meson.build b/src/libcamera/pipeline/meson.build
index 0d46622..df95a43 100644
--- a/src/libcamera/pipeline/meson.build
+++ b/src/libcamera/pipeline/meson.build
@@ -1,6 +1,7 @@
 libcamera_sources += files([
     'uvcvideo.cpp',
     'vimc.cpp',
+    'simple.cpp'
 ])
 
 subdir('ipu3')
diff --git a/src/libcamera/pipeline/simple.cpp b/src/libcamera/pipeline/simple.cpp
new file mode 100644
index 0000000..f150d7e
--- /dev/null
+++ b/src/libcamera/pipeline/simple.cpp
@@ -0,0 +1,457 @@
+
+#include <algorithm>
+#include <array>
+#include <iomanip>
+#include <memory>
+#include <vector>
+
+#include <linux/media-bus-format.h>
+
+#include <libcamera/camera.h>
+#include <libcamera/request.h>
+#include <libcamera/stream.h>
+
+#include "camera_sensor.h"
+#include "device_enumerator.h"
+#include "log.h"
+#include "media_device.h"
+#include "pipeline_handler.h"
+#include "utils.h"
+#include "v4l2_subdevice.h"
+#include "v4l2_videodevice.h"
+
+namespace libcamera {
+LOG_DEFINE_CATEGORY(Simple)
+
+struct SimplePipelineInfo {
+	std::string driverName;
+	std::string phyName;
+	std::string v4l2Name;
+	unsigned int v4l2PixFmt;
+	unsigned int mediaBusFmt;
+	unsigned int maxWidth;
+	unsigned int maxHeight;
+};
+
+class SimpleCameraData : public CameraData
+{
+public:
+	SimpleCameraData(PipelineHandler *pipe)
+		: CameraData(pipe), sensor_(nullptr)
+	{
+	}
+
+	~SimpleCameraData()
+	{
+		delete sensor_;
+	}
+
+	Stream stream_;
+	CameraSensor *sensor_;
+};
+
+class SimpleCameraConfiguration : public CameraConfiguration
+{
+public:
+	SimpleCameraConfiguration(Camera *camera, SimpleCameraData *data, const SimplePipelineInfo *pipelineInfo);
+
+	Status validate() override;
+
+	const V4L2SubdeviceFormat &sensorFormat() { return sensorFormat_; }
+
+private:
+	/*
+         * The SimpleCameraData instance is guaranteed to be valid as long as the
+         * corresponding Camera instance is valid. In order to borrow a
+         * reference to the camera data, store a new reference to the camera.
+         */
+	std::shared_ptr<Camera> camera_;
+	const SimpleCameraData *data_;
+
+	V4L2SubdeviceFormat sensorFormat_;
+
+	const SimplePipelineInfo *pipelineInfo_;
+};
+
+class PipelineHandlerSimple : public PipelineHandler
+{
+public:
+	PipelineHandlerSimple(CameraManager *manager);
+
+	~PipelineHandlerSimple();
+
+	CameraConfiguration *generateConfiguration(Camera *camera,
+						   const StreamRoles &roles) override;
+
+	int configure(Camera *camera, CameraConfiguration *config) override;
+
+	int allocateBuffers(Camera *camera,
+			    const std::set<Stream *> &streams) override;
+
+	int freeBuffers(Camera *camera,
+			const std::set<Stream *> &streams) override;
+
+	int start(Camera *camera) override;
+
+	void stop(Camera *camera) override;
+
+	int queueRequest(Camera *camera, Request *request) override;
+
+	bool match(DeviceEnumerator *enumerator) override;
+
+private:
+	SimpleCameraData *cameraData(const Camera *camera)
+	{
+		return static_cast<SimpleCameraData *>(
+			PipelineHandler::cameraData(camera));
+	}
+
+	int initLinks();
+
+	int createCamera(MediaEntity *sensor);
+
+	void bufferReady(Buffer *buffer);
+
+	MediaDevice *media_;
+	V4L2Subdevice *dphy_;
+	V4L2VideoDevice *video_;
+
+	Camera *activeCamera_;
+
+	const SimplePipelineInfo *pipelineInfo_;
+};
+
+SimpleCameraConfiguration::SimpleCameraConfiguration(Camera *camera,
+						     SimpleCameraData *data,
+						     const SimplePipelineInfo *pipelineInfo)
+	: CameraConfiguration()
+{
+	camera_ = camera->shared_from_this();
+	data_ = data;
+	pipelineInfo_ = pipelineInfo;
+}
+
+CameraConfiguration::Status SimpleCameraConfiguration::validate()
+{
+	const CameraSensor *sensor = data_->sensor_;
+	Status status = Valid;
+
+	if (config_.empty())
+		return Invalid;
+
+	/* Cap the number of entries to the available streams. */
+	if (config_.size() > 1) {
+		config_.resize(1);
+		status = Adjusted;
+	}
+
+	StreamConfiguration &cfg = config_[0];
+
+	/* Adjust the pixel format. */
+	if (cfg.pixelFormat != pipelineInfo_->v4l2PixFmt) {
+		LOG(Simple, Debug) << "Adjusting pixel format";
+		cfg.pixelFormat = pipelineInfo_->v4l2PixFmt;
+		status = Adjusted;
+	}
+
+	/* Select the sensor format. */
+	sensorFormat_ = sensor->getFormat({ pipelineInfo_->mediaBusFmt },
+					  cfg.size);
+	if (!sensorFormat_.size.width || !sensorFormat_.size.height)
+		sensorFormat_.size = sensor->resolution();
+
+	/*
+         * Provide a suitable default that matches the sensor aspect
+         * ratio and clamp the size to the hardware bounds.
+         *
+         * \todo: Check the hardware alignment constraints.
+         */
+	const Size size = cfg.size;
+
+	unsigned int pipelineMaxWidth = std::min(sensorFormat_.size.width, pipelineInfo_->maxWidth);
+	unsigned int pipelineMaxHeight = std::min(sensorFormat_.size.height, pipelineInfo_->maxHeight);
+
+	if (!cfg.size.width || !cfg.size.height) {
+		cfg.size.width = pipelineMaxWidth;
+		cfg.size.height = pipelineMaxWidth * sensorFormat_.size.height / sensorFormat_.size.width;
+	}
+
+	cfg.size.width = std::min(pipelineMaxWidth, cfg.size.width);
+	cfg.size.height = std::min(pipelineMaxHeight, cfg.size.height);
+
+	cfg.size.width = std::max(32U, std::min(4416U, cfg.size.width));
+	cfg.size.height = std::max(16U, std::min(3312U, cfg.size.height));
+
+	if (cfg.size != size) {
+		LOG(Simple, Debug)
+			<< "Adjusting size from " << size.toString()
+			<< " to " << cfg.size.toString();
+		status = Adjusted;
+	}
+
+	cfg.bufferCount = 3;
+
+	return status;
+}
+
+PipelineHandlerSimple::PipelineHandlerSimple(CameraManager *manager)
+	: PipelineHandler(manager), dphy_(nullptr), video_(nullptr)
+{
+}
+
+PipelineHandlerSimple::~PipelineHandlerSimple()
+{
+	delete video_;
+	delete dphy_;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline Operations
+ */
+
+CameraConfiguration *PipelineHandlerSimple::generateConfiguration(Camera *camera,
+								  const StreamRoles &roles)
+{
+	SimpleCameraData *data = cameraData(camera);
+	CameraConfiguration *config = new SimpleCameraConfiguration(camera, data, PipelineHandlerSimple::pipelineInfo_);
+
+	if (roles.empty())
+		return config;
+
+	StreamConfiguration cfg{};
+	cfg.pixelFormat = pipelineInfo_->v4l2PixFmt;
+	cfg.size = data->sensor_->resolution();
+
+	config->addConfiguration(cfg);
+
+	config->validate();
+
+	return config;
+}
+
+int PipelineHandlerSimple::configure(Camera *camera, CameraConfiguration *c)
+{
+	SimpleCameraConfiguration *config =
+		static_cast<SimpleCameraConfiguration *>(c);
+	SimpleCameraData *data = cameraData(camera);
+	StreamConfiguration &cfg = config->at(0);
+	CameraSensor *sensor = data->sensor_;
+	int ret;
+
+	/*
+         * Configure the sensor links: enable the link corresponding to this
+         * camera and disable all the other sensor links.
+         */
+	const MediaPad *pad = dphy_->entity()->getPadByIndex(0);
+
+	for (MediaLink *link : pad->links()) {
+		bool enable = link->source()->entity() == sensor->entity();
+
+		if (!!(link->flags() & MEDIA_LNK_FL_ENABLED) == enable)
+			continue;
+
+		LOG(Simple, Debug)
+			<< (enable ? "Enabling" : "Disabling")
+			<< " link from sensor '"
+			<< link->source()->entity()->name()
+			<< "' to CSI-2 receiver";
+
+		ret = link->setEnabled(enable);
+		if (ret < 0)
+			return ret;
+	}
+
+	/*
+         * Configure the format on the sensor output and propagate it through
+         * the pipeline.
+         */
+	V4L2SubdeviceFormat format = config->sensorFormat();
+	LOG(Simple, Debug) << "Configuring sensor with " << format.toString();
+
+	ret = sensor->setFormat(&format);
+	if (ret < 0)
+		return ret;
+
+	LOG(Simple, Debug) << "Sensor configured with " << format.toString();
+
+	V4L2DeviceFormat outputFormat = {};
+	outputFormat.fourcc = cfg.pixelFormat;
+	outputFormat.size = cfg.size;
+	outputFormat.planesCount = 2;
+
+	ret = video_->setFormat(&outputFormat);
+	if (ret)
+		return ret;
+
+	if (outputFormat.size != cfg.size ||
+	    outputFormat.fourcc != cfg.pixelFormat) {
+		LOG(Simple, Error)
+			<< "Unable to configure capture in " << cfg.toString();
+		return -EINVAL;
+	}
+
+	cfg.setStream(&data->stream_);
+
+	return 0;
+}
+
+int PipelineHandlerSimple::allocateBuffers(Camera *camera,
+					   const std::set<Stream *> &streams)
+{
+	Stream *stream = *streams.begin();
+
+	if (stream->memoryType() == InternalMemory)
+		return video_->exportBuffers(&stream->bufferPool());
+	else
+		return video_->importBuffers(&stream->bufferPool());
+}
+
+int PipelineHandlerSimple::freeBuffers(Camera *camera,
+				       const std::set<Stream *> &streams)
+{
+	if (video_->releaseBuffers())
+		LOG(Simple, Error) << "Failed to release buffers";
+
+	return 0;
+}
+
+int PipelineHandlerSimple::start(Camera *camera)
+{
+	int ret;
+
+	ret = video_->streamOn();
+	if (ret)
+		LOG(Simple, Error)
+			<< "Failed to start camera " << camera->name();
+
+	activeCamera_ = camera;
+
+	return ret;
+}
+
+void PipelineHandlerSimple::stop(Camera *camera)
+{
+	int ret;
+
+	ret = video_->streamOff();
+	if (ret)
+		LOG(Simple, Warning)
+			<< "Failed to stop camera " << camera->name();
+
+	activeCamera_ = nullptr;
+}
+
+int PipelineHandlerSimple::queueRequest(Camera *camera, Request *request)
+{
+	SimpleCameraData *data = cameraData(camera);
+	Stream *stream = &data->stream_;
+
+	Buffer *buffer = request->findBuffer(stream);
+	if (!buffer) {
+		LOG(Simple, Error)
+			<< "Attempt to queue request with invalid stream";
+		return -ENOENT;
+	}
+
+	int ret = video_->queueBuffer(buffer);
+	if (ret < 0)
+		return ret;
+
+	PipelineHandler::queueRequest(camera, request);
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Match and Setup
+ */
+
+int PipelineHandlerSimple::createCamera(MediaEntity *sensor)
+{
+	int ret;
+
+	std::unique_ptr<SimpleCameraData> data =
+		utils::make_unique<SimpleCameraData>(this);
+
+	data->sensor_ = new CameraSensor(sensor);
+	ret = data->sensor_->init();
+	if (ret)
+		return ret;
+
+	std::set<Stream *> streams{ &data->stream_ };
+	std::shared_ptr<Camera> camera =
+		Camera::create(this, sensor->name(), streams);
+	registerCamera(std::move(camera), std::move(data));
+
+	return 0;
+}
+
+bool PipelineHandlerSimple::match(DeviceEnumerator *enumerator)
+{
+	const MediaPad *pad;
+
+	static const SimplePipelineInfo infos[1] = {
+		{ .driverName = "sun6i-csi",
+		  .phyName = "sun6i-csi",
+		  .v4l2Name = "sun6i-csi",
+		  .v4l2PixFmt = V4L2_PIX_FMT_UYVY,
+		  .mediaBusFmt = MEDIA_BUS_FMT_UYVY8_2X8,
+		  .maxWidth = 1280,
+		  .maxHeight = 720 }
+	};
+
+	const SimplePipelineInfo *ptr = infos;
+	for (int i = 0; i < 1; i++, ptr++) {
+		DeviceMatch dm(ptr->driverName);
+		dm.add(ptr->phyName);
+
+		media_ = acquireMediaDevice(enumerator, dm);
+		if (!media_)
+			continue;
+
+		PipelineHandlerSimple::pipelineInfo_ = ptr;
+
+		/* Create the V4L2 subdevices we will need. */
+		dphy_ = V4L2Subdevice::fromEntityName(media_, ptr->phyName);
+		if (dphy_->open() < 0)
+			return false;
+
+		/* Locate and open the capture video node. */
+		video_ = V4L2VideoDevice::fromEntityName(media_, ptr->v4l2Name);
+		if (video_->open() < 0)
+			return false;
+
+		video_->bufferReady.connect(this, &PipelineHandlerSimple::bufferReady);
+
+		/*
+             * Enumerate all sensors connected to the CSI-2 receiver and create one
+             * camera instance for each of them.
+             */
+		pad = dphy_->entity()->getPadByIndex(0);
+		if (!pad)
+			return false;
+
+		for (MediaLink *link : pad->links())
+			createCamera(link->source()->entity());
+
+		return true;
+	}
+	return false;
+}
+
+/* -----------------------------------------------------------------------------
+ * Buffer Handling
+ */
+
+void PipelineHandlerSimple::bufferReady(Buffer *buffer)
+{
+	ASSERT(activeCamera_);
+	LOG(Simple, Debug) << "bufferReady";
+	Request *request = buffer->request();
+	completeBuffer(activeCamera_, request, buffer);
+	completeRequest(activeCamera_, request);
+}
+
+REGISTER_PIPELINE_HANDLER(PipelineHandlerSimple);
+
+} // namespace libcamera
-- 
2.23.0



More information about the libcamera-devel mailing list