linuxOS_AP06/buildroot/package/libcamera/0002-HACK-pipeline-Support-custom-pipeline.patch
2025-06-03 12:28:32 +08:00

515 lines
14 KiB
Diff

From ab991e03cc84bb92faa72b92263f5de48c8bee79 Mon Sep 17 00:00:00 2001
From: Jeffy Chen <jeffy.chen@rock-chips.com>
Date: Tue, 29 Nov 2022 17:18:23 +0800
Subject: [PATCH 2/2] HACK: pipeline: Support custom pipeline
Tested on RK3588 EVB with:
export LIBCAMERA_CUSTOM_DRIVERS=has:rkisp
export LIBCAMERA_CUSTOM_DEFAULT=has:mainpath
export LIBCAMERA_CUSTOM_FORMAT=NV12
export LIBCAMERA_CUSTOM_BUF_CNT=4
gst-launch-1.0 libcamerasrc ! waylandsink
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
meson.build | 1 +
meson_options.txt | 3 +-
src/libcamera/device_enumerator.cpp | 7 +-
src/libcamera/pipeline/custom/custom.cpp | 427 ++++++++++++++++++++++
src/libcamera/pipeline/custom/meson.build | 5 +
5 files changed, 440 insertions(+), 3 deletions(-)
create mode 100644 src/libcamera/pipeline/custom/custom.cpp
create mode 100644 src/libcamera/pipeline/custom/meson.build
diff --git a/meson.build b/meson.build
index cb6b666..0c23874 100644
--- a/meson.build
+++ b/meson.build
@@ -201,6 +201,7 @@ pipelines_support = {
'rkisp1': arch_arm,
'rpi/vc4': arch_arm,
'simple': arch_arm,
+ 'custom': arch_arm,
'uvcvideo': ['any'],
'vimc': ['test'],
}
diff --git a/meson_options.txt b/meson_options.txt
index 5fdc7be..ebe926c 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -47,7 +47,8 @@ option('pipelines',
'rpi/vc4',
'simple',
'uvcvideo',
- 'vimc'
+ 'vimc',
+ 'custom',
],
description : 'Select which pipeline handlers to build. If this is set to "auto", all the pipelines applicable to the target architecture will be built. If this is set to "all", all the pipelines will be built. If both are selected then "all" will take precedence.')
diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp
index 42b5ba6..3f3401b 100644
--- a/src/libcamera/device_enumerator.cpp
+++ b/src/libcamera/device_enumerator.cpp
@@ -93,8 +93,11 @@ void DeviceMatch::add(const std::string &entity)
*/
bool DeviceMatch::match(const MediaDevice *device) const
{
- if (driver_ != device->driver())
- return false;
+ const std::string driver = device->driver();
+ if (driver_ != driver &&
+ (driver_.find("has:") != 0 ||
+ (driver.find(driver_.substr(4)) == std::string::npos)))
+ return false;
for (const std::string &name : entities_) {
bool found = false;
diff --git a/src/libcamera/pipeline/custom/custom.cpp b/src/libcamera/pipeline/custom/custom.cpp
new file mode 100644
index 0000000..c8989c3
--- /dev/null
+++ b/src/libcamera/pipeline/custom/custom.cpp
@@ -0,0 +1,427 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ * Copyright (C) 2022, Rockchip Electronics Co., Ltd
+ *
+ * Based on src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+ *
+ * custom.cpp - Pipeline handler for custom devices
+ */
+
+#include <iostream>
+#include <string>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+#include <libcamera/camera.h>
+#include <libcamera/control_ids.h>
+#include <libcamera/formats.h>
+#include <libcamera/property_ids.h>
+#include <libcamera/request.h>
+#include <libcamera/stream.h>
+
+#include "libcamera/internal/camera.h"
+#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/media_device.h"
+#include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/v4l2_videodevice.h"
+
+#define CUSTOM_DRIVERS_ENV "LIBCAMERA_CUSTOM_DRIVERS"
+#define CUSTOM_DEFAULT_ENV "LIBCAMERA_CUSTOM_DEFAULT"
+#define CUSTOM_BUF_CNT_ENV "LIBCAMERA_CUSTOM_BUF_CNT"
+#define CUSTOM_FORMAT_ENV "LIBCAMERA_CUSTOM_FORMAT"
+#define CUSTOM_FORMAT_NONE formats::R8
+
+using namespace std;
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Custom)
+
+class CustomCameraData : public Camera::Private
+{
+public:
+ CustomCameraData(PipelineHandler *pipe)
+ : Camera::Private(pipe)
+ {
+ }
+
+ MediaEntity *getEntity(MediaDevice *media);
+ int init(MediaDevice *media);
+ void bufferReady(FrameBuffer *buffer);
+
+ std::unique_ptr<V4L2VideoDevice> video_;
+ Stream stream_;
+ std::map<PixelFormat, std::vector<SizeRange>> formats_;
+};
+
+class CustomCameraConfiguration : public CameraConfiguration
+{
+public:
+ CustomCameraConfiguration(CustomCameraData *data);
+
+ Status validate() override;
+
+private:
+ CustomCameraData *data_;
+};
+
+class PipelineHandlerCustom : public PipelineHandler
+{
+public:
+ PipelineHandlerCustom(CameraManager *manager);
+
+ std::unique_ptr<CameraConfiguration> generateConfiguration(Camera *camera,
+ Span<const StreamRole> roles) override;
+ int configure(Camera *camera, CameraConfiguration *config) override;
+
+ int exportFrameBuffers(Camera *camera, Stream *stream,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
+
+ int start(Camera *camera, const ControlList *controls) override;
+ void stopDevice(Camera *camera) override;
+
+ int queueRequestDevice(Camera *camera, Request *request) override;
+
+ bool match(DeviceEnumerator *enumerator) override;
+
+private:
+ CustomCameraData *cameraData(Camera *camera)
+ {
+ return static_cast<CustomCameraData *>(camera->_d());
+ }
+
+ int bufferCount_;
+ PixelFormat pixelFormat_;
+};
+
+CustomCameraConfiguration::CustomCameraConfiguration(CustomCameraData *data)
+ : CameraConfiguration(), data_(data)
+{
+}
+
+CameraConfiguration::Status CustomCameraConfiguration::validate()
+{
+ Status status = Valid;
+
+ if (config_.empty())
+ return Invalid;
+
+ if (orientation != Orientation::Rotate0) {
+ orientation = Orientation::Rotate0;
+ status = Adjusted;
+ }
+
+ /* Cap the number of entries to the available streams. */
+ if (config_.size() > 1) {
+ config_.resize(1);
+ status = Adjusted;
+ }
+
+ StreamConfiguration &cfg = config_[0];
+ const StreamFormats &formats = cfg.formats();
+ const PixelFormat pixelFormat = cfg.pixelFormat;
+ const Size size = cfg.size;
+
+ const std::vector<PixelFormat> pixelFormats = formats.pixelformats();
+ auto iter = std::find(pixelFormats.begin(), pixelFormats.end(), pixelFormat);
+ if (iter == pixelFormats.end()) {
+ cfg.pixelFormat = pixelFormats.front();
+ LOG(Custom, Debug)
+ << "Adjusting pixel format from " << pixelFormat
+ << " to " << cfg.pixelFormat;
+ status = Adjusted;
+ }
+
+ const std::vector<Size> &formatSizes = formats.sizes(cfg.pixelFormat);
+ cfg.size = formatSizes.front();
+ for (const Size &formatsSize : formatSizes) {
+ if (formatsSize > size)
+ break;
+
+ cfg.size = formatsSize;
+ }
+
+ if (cfg.size != size) {
+ LOG(Custom, Debug)
+ << "Adjusting size from " << size << " to " << cfg.size;
+ status = Adjusted;
+ }
+
+ V4L2DeviceFormat format;
+ format.fourcc = data_->video_->toV4L2PixelFormat(cfg.pixelFormat);
+ format.size = cfg.size;
+
+ int ret = data_->video_->tryFormat(&format);
+ if (ret)
+ return Invalid;
+
+ cfg.stride = format.planes[0].bpl;
+ cfg.frameSize = format.planes[0].size;
+
+ if (cfg.colorSpace != format.colorSpace) {
+ cfg.colorSpace = format.colorSpace;
+ status = Adjusted;
+ }
+
+ return status;
+}
+
+PipelineHandlerCustom::PipelineHandlerCustom(CameraManager *manager)
+ : PipelineHandler(manager), bufferCount_(4),
+ pixelFormat_(CUSTOM_FORMAT_NONE)
+{
+ const char *bufferCount = utils::secure_getenv(CUSTOM_BUF_CNT_ENV);
+ if (bufferCount)
+ bufferCount_ = atoi(bufferCount);
+
+ const char *pixelFormat = utils::secure_getenv(CUSTOM_FORMAT_ENV);
+ if (pixelFormat) {
+ if (!strcmp(pixelFormat, "NV12"))
+ pixelFormat_ = formats::NV12;
+ else if (!strcmp(pixelFormat, "YUV420"))
+ pixelFormat_ = formats::YUV420;
+ else if (!strcmp(pixelFormat, "NV16"))
+ pixelFormat_ = formats::NV16;
+ else if (!strcmp(pixelFormat, "YUYV"))
+ pixelFormat_ = formats::YUYV;
+ }
+}
+
+std::unique_ptr<CameraConfiguration>
+PipelineHandlerCustom::generateConfiguration(Camera *camera,
+ Span<const StreamRole> roles)
+{
+ CustomCameraData *data = cameraData(camera);
+ std::unique_ptr<CameraConfiguration> config =
+ std::make_unique<CustomCameraConfiguration>(data);
+
+ if (roles.empty())
+ return config;
+
+ StreamFormats formats(data->formats_);
+ StreamConfiguration cfg(formats);
+
+ if (pixelFormat_ != CUSTOM_FORMAT_NONE)
+ cfg.pixelFormat = pixelFormat_;
+ else
+ cfg.pixelFormat = formats::NV12;
+
+ cfg.size = formats.sizes(cfg.pixelFormat).back();
+ cfg.bufferCount = bufferCount_;
+
+ config->addConfiguration(cfg);
+
+ config->validate();
+
+ return config;
+}
+
+int PipelineHandlerCustom::configure(Camera *camera, CameraConfiguration *config)
+{
+ CustomCameraData *data = cameraData(camera);
+ StreamConfiguration &cfg = config->at(0);
+ int ret;
+
+ V4L2DeviceFormat format;
+ format.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat);
+ format.size = cfg.size;
+
+ ret = data->video_->setFormat(&format);
+ if (ret)
+ return ret;
+
+ if (format.size != cfg.size ||
+ format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat))
+ return -EINVAL;
+
+ cfg.setStream(&data->stream_);
+
+ return 0;
+}
+
+int PipelineHandlerCustom::exportFrameBuffers(Camera *camera, Stream *stream,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+{
+ CustomCameraData *data = cameraData(camera);
+ unsigned int count = stream->configuration().bufferCount;
+
+ return data->video_->exportBuffers(count, buffers);
+}
+
+int PipelineHandlerCustom::start(Camera *camera, [[maybe_unused]] const ControlList *controls)
+{
+ CustomCameraData *data = cameraData(camera);
+ unsigned int count = data->stream_.configuration().bufferCount;
+
+ int ret = data->video_->importBuffers(count);
+ if (ret < 0)
+ return ret;
+
+ ret = data->video_->streamOn();
+ if (ret < 0) {
+ data->video_->releaseBuffers();
+ return ret;
+ }
+
+ return 0;
+}
+
+void PipelineHandlerCustom::stopDevice(Camera *camera)
+{
+ CustomCameraData *data = cameraData(camera);
+ data->video_->streamOff();
+ data->video_->releaseBuffers();
+}
+
+int PipelineHandlerCustom::queueRequestDevice(Camera *camera, Request *request)
+{
+ CustomCameraData *data = cameraData(camera);
+ int ret;
+
+ FrameBuffer *buffer = request->findBuffer(&data->stream_);
+ if (!buffer) {
+ LOG(Custom, Error)
+ << "Attempt to queue request with invalid stream";
+
+ return -ENOENT;
+ }
+
+ ret = data->video_->queueBuffer(buffer);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+bool PipelineHandlerCustom::match(DeviceEnumerator *enumerator)
+{
+ MediaDevice *media;
+ bool found = false;
+
+ const char *drivers = utils::secure_getenv(CUSTOM_DRIVERS_ENV);
+ if (!drivers)
+ return false;
+
+ istringstream in(drivers);
+ string driver;
+
+ while (in >> driver) {
+ DeviceMatch dm(driver);
+ media = acquireMediaDevice(enumerator, dm);
+ if (!media)
+ continue;
+
+ std::unique_ptr<CustomCameraData> data =
+ std::make_unique<CustomCameraData>(this);
+
+ if (data->init(media))
+ continue;
+
+ /* Create and register the camera. */
+ std::string id = media->model();
+ if (id.empty()) {
+ LOG(Custom, Error) << "Failed to get camera ID";
+ continue;
+ }
+
+ std::set<Stream *> streams{ &data->stream_ };
+ std::shared_ptr<Camera> camera =
+ Camera::create(std::move(data), id, streams);
+ registerCamera(std::move(camera));
+
+ found = true;
+ }
+
+ return found;
+}
+
+MediaEntity *CustomCameraData::getEntity(MediaDevice *media)
+{
+ const std::vector<MediaEntity *> &entities = media->entities();
+
+ if (utils::secure_getenv(CUSTOM_DEFAULT_ENV)) {
+ auto iter = std::find_if(entities.begin(), entities.end(),
+ [](MediaEntity *e) {
+ string name = utils::secure_getenv(CUSTOM_DEFAULT_ENV);
+ if (e->name() == name) return true;
+ if (name.find("has:") != 0) return false;
+ return e->name().find(name.substr(4)) != string::npos;
+ });
+ return iter == entities.end() ? NULL : *iter;
+ } else {
+ auto iter = std::find_if(entities.begin(), entities.end(),
+ [](MediaEntity *e) {
+ return e->function() == MEDIA_ENT_F_IO_V4L;
+ });
+ return iter == entities.end() ? NULL : *iter;
+ }
+}
+
+int CustomCameraData::init(MediaDevice *media)
+{
+ /* Locate and initialise the camera data with the default video node. */
+ MediaEntity *entity = getEntity(media);
+ if (!entity) {
+ LOG(Custom, Error) << "Could not find default video device";
+ return -ENODEV;
+ }
+
+ /* Create and open the video device. */
+ video_ = std::make_unique<V4L2VideoDevice>(entity);
+ int ret = video_->open();
+ if (ret)
+ return ret;
+
+ video_->bufferReady.connect(this, &CustomCameraData::bufferReady);
+
+ /*
+ * Populate the map of supported formats, and infer the camera sensor
+ * resolution from the largest size it advertises.
+ */
+ Size resolution;
+ for (const auto &format : video_->formats()) {
+ PixelFormat pixelFormat = format.first.toPixelFormat();
+ if (!pixelFormat.isValid())
+ continue;
+
+ formats_[pixelFormat] = format.second;
+
+ const std::vector<SizeRange> &sizeRanges = format.second;
+ for (const SizeRange &sizeRange : sizeRanges) {
+ if (sizeRange.max > resolution)
+ resolution = sizeRange.max;
+ }
+ }
+
+ if (formats_.empty()) {
+ LOG(Custom, Error)
+ << "Camera (" << media->model()
+ << ") doesn't expose any supported format";
+ return -EINVAL;
+ }
+
+ /* Populate the camera properties. */
+ properties_.set(properties::Model, utils::toAscii(media->model()));
+
+ properties_.set(properties::PixelArraySize, resolution);
+ properties_.set(properties::PixelArrayActiveAreas, { Rectangle(resolution) });
+ return 0;
+}
+
+void CustomCameraData::bufferReady(FrameBuffer *buffer)
+{
+ Request *request = buffer->request();
+
+ /* \todo Use the Custom metadata to calculate a more precise timestamp */
+ request->metadata().set(controls::SensorTimestamp,
+ buffer->metadata().timestamp);
+
+ pipe()->completeBuffer(request, buffer);
+ pipe()->completeRequest(request);
+}
+
+REGISTER_PIPELINE_HANDLER(PipelineHandlerCustom)
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/custom/meson.build b/src/libcamera/pipeline/custom/meson.build
new file mode 100644
index 0000000..9d2ee94
--- /dev/null
+++ b/src/libcamera/pipeline/custom/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: CC0-1.0
+
+libcamera_sources += files([
+ 'custom.cpp',
+])
--
2.20.1