[libcamera-devel] [PATCH 14/14] libcamera: pipeline: rkisp1: Add converter support
Xavier Roumegue
xavier.roumegue at oss.nxp.com
Thu Sep 8 20:48:50 CEST 2022
This adds a converter, if any present in the system, on each streams
(self and main paths). In case a configuration file is successfully
loaded, the converter use is getting compulsory, such as a dewarping map
is unconditionally applied. Otherwise, the converter is only used if the
stream configuration requires it.
Signed-off-by: Xavier Roumegue <xavier.roumegue at oss.nxp.com>
---
src/libcamera/pipeline/rkisp1/rkisp1.cpp | 126 +++---
src/libcamera/pipeline/rkisp1/rkisp1_path.cpp | 374 +++++++++++++++++-
src/libcamera/pipeline/rkisp1/rkisp1_path.h | 54 ++-
3 files changed, 485 insertions(+), 69 deletions(-)
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index c1522ca6..6bdf5a3a 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019, Google Inc.
+ * Copyright 2022 NXP
*
* rkisp1.cpp - Pipeline handler for Rockchip ISP1
*/
@@ -194,6 +195,7 @@ private:
Camera *activeCamera_;
const MediaPad *ispSink_;
+ MediaDevice *converter_;
};
RkISP1Frames::RkISP1Frames(PipelineHandler *pipe)
@@ -449,57 +451,44 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()
bool mainPathAvailable = true;
bool selfPathAvailable = data_->selfPath_;
+ const std::array<Status, 2> cameraStatus = { Valid, Adjusted };
+
for (unsigned int index : order) {
StreamConfiguration &cfg = config_[index];
- /* Try to match stream without adjusting configuration. */
- if (mainPathAvailable) {
- StreamConfiguration tryCfg = cfg;
- if (data_->mainPath_->validate(&tryCfg) == Valid) {
- mainPathAvailable = false;
- cfg = tryCfg;
- cfg.setStream(const_cast<Stream *>(&data_->mainPathStream_));
- continue;
- }
- }
+ for (auto &_status : cameraStatus) {
+ Status pipeStatus;
- if (selfPathAvailable) {
- StreamConfiguration tryCfg = cfg;
- if (data_->selfPath_->validate(&tryCfg) == Valid) {
- selfPathAvailable = false;
- cfg = tryCfg;
- cfg.setStream(const_cast<Stream *>(&data_->selfPathStream_));
- continue;
- }
- }
+ if (mainPathAvailable) {
+ StreamConfiguration tryCfg = cfg;
+ pipeStatus = data_->mainPath_->validate(&tryCfg);
- /* Try to match stream allowing adjusting configuration. */
- if (mainPathAvailable) {
- StreamConfiguration tryCfg = cfg;
- if (data_->mainPath_->validate(&tryCfg) == Adjusted) {
- mainPathAvailable = false;
- cfg = tryCfg;
- cfg.setStream(const_cast<Stream *>(&data_->mainPathStream_));
- status = Adjusted;
- continue;
+ if (pipeStatus == _status) {
+ mainPathAvailable = false;
+ cfg = tryCfg;
+ cfg.setStream(const_cast<Stream *>(&data_->mainPathStream_));
+ status = _status;
+ break;
+ }
}
- }
- if (selfPathAvailable) {
- StreamConfiguration tryCfg = cfg;
- if (data_->selfPath_->validate(&tryCfg) == Adjusted) {
- selfPathAvailable = false;
- cfg = tryCfg;
- cfg.setStream(const_cast<Stream *>(&data_->selfPathStream_));
- status = Adjusted;
- continue;
+ if (selfPathAvailable) {
+ StreamConfiguration tryCfg = cfg;
+ pipeStatus = data_->selfPath_->validate(&tryCfg);
+
+ if (pipeStatus == _status) {
+ selfPathAvailable = false;
+ cfg = tryCfg;
+ cfg.setStream(const_cast<Stream *>(&data_->selfPathStream_));
+ status = _status;
+ break;
+ }
}
+ /* All paths rejected configuration. */
+ LOG(RkISP1, Debug) << "Camera configuration not supported "
+ << cfg.toString();
+ return Invalid;
}
-
- /* All paths rejected configuraiton. */
- LOG(RkISP1, Debug) << "Camera configuration not supported "
- << cfg.toString();
- return Invalid;
}
/* Select the sensor format. */
@@ -680,15 +669,21 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
std::map<unsigned int, IPAStream> streamConfig;
- for (const StreamConfiguration &cfg : *config) {
+ for (StreamConfiguration &cfg : *config) {
+ size_t idx = 0;
+ StreamConfiguration internalCfg;
if (cfg.stream() == &data->mainPathStream_) {
+ idx = 0;
ret = mainPath_.configure(cfg, format);
- streamConfig[0] = IPAStream(cfg.pixelFormat,
- cfg.size);
+ internalCfg = mainPath_.internalStream();
+ streamConfig[idx] = IPAStream(internalCfg.pixelFormat,
+ internalCfg.size);
} else if (hasSelfPath_) {
+ idx = 1;
ret = selfPath_.configure(cfg, format);
- streamConfig[1] = IPAStream(cfg.pixelFormat,
- cfg.size);
+ internalCfg = selfPath_.internalStream();
+ streamConfig[idx] = IPAStream(internalCfg.pixelFormat,
+ internalCfg.size);
} else {
return -ENODEV;
}
@@ -754,6 +749,20 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera)
data->selfPathStream_.configuration().bufferCount,
});
+ if (data->mainPath_->isEnabled()) {
+ ret = data->mainPath_->allocateBuffers(
+ data->mainPathStream_.configuration().bufferCount);
+ if (ret < 0)
+ goto error;
+ }
+
+ if (hasSelfPath_ && data->selfPath_->isEnabled()) {
+ ret = data->selfPath_->allocateBuffers(
+ data->selfPathStream_.configuration().bufferCount);
+ if (ret < 0)
+ goto error;
+ }
+
ret = param_->allocateBuffers(maxCount, ¶mBuffers_);
if (ret < 0)
goto error;
@@ -813,6 +822,12 @@ int PipelineHandlerRkISP1::freeBuffers(Camera *camera)
if (stat_->releaseBuffers())
LOG(RkISP1, Error) << "Failed to release stat buffers";
+ if (hasSelfPath_ && data->selfPath_->isEnabled())
+ data->selfPath_->releaseBuffers();
+
+ if (data->mainPath_->isEnabled())
+ data->mainPath_->releaseBuffers();
+
return 0;
}
@@ -1055,6 +1070,19 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
hasSelfPath_ = !!media_->getEntityByName("rkisp1_selfpath");
+ /* Seek for a converter */
+ for (auto converterName : ConverterFactory::names()) {
+ LOG(RkISP1, Debug)
+ << "Trying " << converterName << " converter";
+ DeviceMatch converterMatch(converterName);
+ converter_ = acquireMediaDevice(enumerator, converterMatch);
+ if (converter_) {
+ LOG(RkISP1, Debug)
+ << "Get support for " << converterName << " converter";
+ break;
+ }
+ }
+
/* Create the V4L2 subdevices we will need. */
isp_ = V4L2Subdevice::fromEntityName(media_, "rkisp1_isp");
if (isp_->open() < 0)
@@ -1086,10 +1114,10 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
return false;
/* Locate and open the ISP main and self paths. */
- if (!mainPath_.init(media_))
+ if (!mainPath_.init(media_, converter_))
return false;
- if (hasSelfPath_ && !selfPath_.init(media_))
+ if (hasSelfPath_ && !selfPath_.init(media_, converter_))
return false;
mainPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp b/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp
index 2d38f0fb..c19a1e69 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2020, Google Inc.
+ * Copyright 2022 NXP
*
* rkisp1path.cpp - Rockchip ISP1 path helper
*/
@@ -28,7 +29,46 @@ RkISP1Path::RkISP1Path(const char *name, const Span<const PixelFormat> &formats,
{
}
-bool RkISP1Path::init(MediaDevice *media)
+void RkISP1Path::initConverter(MediaDevice *mediaConverter)
+{
+ hasConverter_ = false;
+ useConverter_ = false;
+
+ if (!mediaConverter)
+ return;
+
+ converter_ = ConverterFactory::create(mediaConverter);
+
+ if (!converter_->isValid()) {
+ LOG(RkISP1, Warning)
+ << "Failed to create converter, disabling format conversion";
+ converter_.reset();
+ } else {
+ char const *configFromEnv = utils::secure_getenv("LIBCAMERA_RKISP1_CONVERTER_FILE");
+
+ if (configFromEnv && *configFromEnv != '\0') {
+ int nrMappings;
+ LOG(RkISP1, Debug)
+ << "Getting pipeline converter filename as " << std::string(configFromEnv);
+ nrMappings = converter_->loadConfiguration(std::string(configFromEnv));
+ if (nrMappings < 0) {
+ LOG(RkISP1, Error)
+ << "Error while reading converter configuration file";
+ } else {
+ LOG(RkISP1, Debug)
+ << nrMappings << " mapping(s) loaded";
+ if (nrMappings > 0)
+ /* We want to force the converter use to apply the mapping */
+ useConverter_ = true;
+ }
+ }
+ converter_->inputBufferReady.connect(this, &RkISP1Path::pathConverterInputDone);
+ converter_->outputBufferReady.connect(this, &RkISP1Path::pathConverterOutputDone);
+ hasConverter_ = true;
+ }
+}
+
+bool RkISP1Path::init(MediaDevice *media, MediaDevice *mediaConverter)
{
std::string resizer = std::string("rkisp1_resizer_") + name_ + "path";
std::string video = std::string("rkisp1_") + name_ + "path";
@@ -45,10 +85,13 @@ bool RkISP1Path::init(MediaDevice *media)
if (!link_)
return false;
+ initConverter(mediaConverter);
+
+ video_->bufferReady.connect(this, &RkISP1Path::pathBufferReady);
+
return true;
}
-
-StreamConfiguration RkISP1Path::generateConfiguration(const Size &resolution)
+StreamConfiguration RkISP1Path::generateNativeConfiguration(const Size &resolution)
{
Size maxResolution = maxResolution_.boundedToAspectRatio(resolution)
.boundedTo(resolution);
@@ -67,7 +110,165 @@ StreamConfiguration RkISP1Path::generateConfiguration(const Size &resolution)
return cfg;
}
-CameraConfiguration::Status RkISP1Path::validate(StreamConfiguration *cfg)
+StreamConfiguration RkISP1Path::generateConfiguration(const Size &resolution)
+{
+ StreamConfiguration cfg = generateNativeConfiguration(resolution);
+ StreamFormats formats = cfg.formats();
+ std::map<PixelFormat, std::vector<SizeRange>> fullFormats;
+
+ for (const PixelFormat &fmt : formats.pixelformats()) {
+ Configuration config;
+ SizeRange szRange;
+
+ config.inputFormat = fmt;
+ config.inputSizes = formats.range(fmt);
+
+ if (!useConverter_) {
+ config.outputFormats.push_back(fmt);
+ config.outputSizes = config.inputSizes;
+ config.withConverter = false;
+ configs_.push_back(config);
+ LOG(RkISP1, Debug)
+ << "Pushing native format " << fmt
+ << " resolution range " << config.inputSizes;
+ fullFormats[fmt].push_back(config.inputSizes);
+ }
+
+ if (hasConverter_) {
+ config.outputFormats = converter_->formats(config.inputFormat);
+ if (config.outputFormats.empty())
+ continue;
+ szRange.max = converter_->sizes(config.inputSizes.max).max;
+ szRange.min = converter_->sizes(config.inputSizes.min).min;
+ config.outputSizes = szRange;
+ config.withConverter = true;
+ configs_.push_back(config);
+ for (auto &_fmt : config.outputFormats) {
+ fullFormats[_fmt].push_back(config.outputSizes);
+ LOG(RkISP1, Debug)
+ << "Pushing converted format " << _fmt
+ << " resolution range " << config.outputSizes;
+ }
+ }
+ }
+ /*
+ * Sort the sizes and merge any consecutive overlapping ranges.
+ * Imported from src/libcamera/pipeline/simple.cpp
+ *
+ * TODO: Apply policy of converter use or not.. We likely want to force
+ * the converter use in case there is a mapping defined in the
+ * configuration file if any... if so, shall we filter out resolution
+ * not defined in the configuration file.. or should this policy be let
+ * to the application
+ * */
+
+ for (auto &[format, sizes] : fullFormats) {
+ std::sort(sizes.begin(), sizes.end(),
+ [](SizeRange &a, SizeRange &b) {
+ return a.min < b.min;
+ });
+
+ auto cur = sizes.begin();
+ auto next = cur;
+
+ while (++next != sizes.end()) {
+ if (cur->max.width >= next->min.width &&
+ cur->max.height >= next->min.height)
+ cur->max = next->max;
+ else if (++cur != next)
+ *cur = *next;
+ }
+
+ sizes.erase(++cur, sizes.end());
+ }
+
+ StreamConfiguration fullCfg{ StreamFormats{ fullFormats } };
+ fullCfg.pixelFormat = fullFormats.begin()->first;
+ fullCfg.size = fullFormats.begin()->second[0].max;
+ fullCfg.bufferCount = cfg.bufferCount;
+
+ return fullCfg;
+}
+
+CameraConfiguration::Status RkISP1Path::getPipeConfiguration(
+ StreamConfiguration &streamCfg,
+ const RkISP1Path::Configuration **cfg) const
+{
+ CameraConfiguration::Status _status = CameraConfiguration::Adjusted;
+
+ LOG(RkISP1, Debug)
+ << "Looking for "
+ << streamCfg.pixelFormat << "/" << streamCfg.size
+ << " output configuration";
+ /*
+ * Select the fallback configuration
+ */
+ for (auto &_cfg : configs_) {
+ if (_cfg.withConverter == useConverter_) {
+ *cfg = &_cfg;
+ break;
+ }
+ }
+
+ PixelFormat pixelFormat = (*cfg)->outputFormats.front();
+ Size size = streamCfg.size;
+ size.boundTo((*cfg)->outputSizes.max);
+ size.expandTo((*cfg)->outputSizes.min);
+
+ /* Unless the converter use is enforced through a loaded configuration,
+ * prefer a configuration without the converter if possible
+ */
+ std::vector<bool> converterUse;
+ if (!useConverter_)
+ converterUse.push_back(false);
+ if (hasConverter_)
+ converterUse.push_back(true);
+
+ for (auto withConverter : converterUse) {
+ for (auto &_cfg : configs_) {
+ auto &outFmts = _cfg.outputFormats;
+ if (withConverter != _cfg.withConverter)
+ continue;
+
+ if ((std::find(outFmts.begin(),
+ outFmts.end(),
+ streamCfg.pixelFormat)) == outFmts.end())
+ continue;
+
+ *cfg = &_cfg;
+ pixelFormat = streamCfg.pixelFormat;
+
+ if (_cfg.outputSizes.contains(streamCfg.size)) {
+ _status = CameraConfiguration::Valid;
+ goto done;
+ }
+
+ size.boundTo((*cfg)->outputSizes.max);
+ size.expandTo((*cfg)->outputSizes.min);
+ }
+ }
+
+ streamCfg.pixelFormat = pixelFormat;
+ streamCfg.size = size;
+
+done:
+ if ((*cfg)->withConverter) {
+ std::tie(streamCfg.stride, streamCfg.frameSize) =
+ converter_->strideAndFrameSize(streamCfg.pixelFormat,
+ streamCfg.size);
+ if (streamCfg.stride == 0)
+ return CameraConfiguration::Invalid;
+ }
+
+ LOG(RkISP1, Debug)
+ << "Chosen configuration: "
+ << (*cfg)->inputFormat << "/" << (*cfg)->inputSizes
+ << " --> " << streamCfg.pixelFormat << "/" << streamCfg.size
+ << ((*cfg)->withConverter ? " with" : " without") << " converter";
+ return _status;
+}
+
+CameraConfiguration::Status RkISP1Path::nativeValidate(StreamConfiguration *cfg)
{
const StreamConfiguration reqCfg = *cfg;
CameraConfiguration::Status status = CameraConfiguration::Valid;
@@ -101,7 +302,39 @@ CameraConfiguration::Status RkISP1Path::validate(StreamConfiguration *cfg)
return status;
}
-int RkISP1Path::configure(const StreamConfiguration &config,
+CameraConfiguration::Status RkISP1Path::validate(StreamConfiguration *cfg)
+{
+ StreamConfiguration tryCfg = *cfg;
+ StreamConfiguration internalCfg;
+ const Configuration *pipeCfg;
+ CameraConfiguration::Status pipeStatus;
+
+ pipeStatus = getPipeConfiguration(tryCfg, &pipeCfg);
+ if (pipeStatus == CameraConfiguration::Invalid)
+ return CameraConfiguration::Invalid;
+
+ if (!pipeCfg->withConverter) {
+ tryCfg = *cfg;
+ pipeStatus = nativeValidate(&tryCfg);
+ if (pipeStatus == CameraConfiguration::Invalid)
+ return CameraConfiguration::Invalid;
+ internalCfg = tryCfg;
+ } else {
+ internalCfg = tryCfg;
+ internalCfg.pixelFormat = pipeCfg->inputFormat;
+ auto _status = nativeValidate(&internalCfg);
+ if (_status == CameraConfiguration::Invalid)
+ return CameraConfiguration::Invalid;
+ }
+
+ *cfg = tryCfg;
+ internalStream_ = internalCfg;
+ pipeConfig_ = pipeCfg;
+
+ return pipeStatus;
+}
+
+int RkISP1Path::nativeConfigure(const StreamConfiguration &config,
const V4L2SubdeviceFormat &inputFormat)
{
int ret;
@@ -165,6 +398,117 @@ int RkISP1Path::configure(const StreamConfiguration &config,
return 0;
}
+int RkISP1Path::configure(const StreamConfiguration &config,
+ const V4L2SubdeviceFormat &inputFormat)
+{
+ int ret;
+ LOG(RkISP1, Debug)
+ << "Configuring " << name_ << " path "
+ << internalStream_.pixelFormat << "/" << internalStream_.size
+ << " --> " << config.pixelFormat << "/" << config.size
+ << (pipeConfig_->withConverter ? " with" : " without") << " converter";
+
+ ret = nativeConfigure(internalStream_, inputFormat);
+ if (ret)
+ return ret;
+
+ if (pipeConfig_->withConverter) {
+ StreamConfiguration cfg = config;
+ std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;
+ outputCfgs.push_back(cfg);
+ ret = converter_->configure(internalStream_, outputCfgs);
+ }
+
+ return ret;
+}
+
+int RkISP1Path::exportBuffers(unsigned int bufferCount,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+{
+ if (pipeConfig_->withConverter)
+ return converter_->exportBuffers(0, bufferCount, buffers);
+ else
+ return video_->exportBuffers(bufferCount, buffers);
+}
+
+int RkISP1Path::allocateBuffers(unsigned int bufferCount)
+{
+ if (pipeConfig_->withConverter) {
+ int ret = video_->allocateBuffers(bufferCount, &converterBuffers_);
+ if (ret < 0)
+ return ret;
+ if ((unsigned int)ret != bufferCount)
+ LOG(RkISP1, Warning)
+ << "Requested " << bufferCount << " but got " << ret << " buffers";
+
+ for (std::unique_ptr<FrameBuffer> &buffer : converterBuffers_)
+ availableConverterBuffers_.push(buffer.get());
+ } else {
+ return video_->importBuffers(bufferCount);
+ }
+
+ return 0;
+}
+
+void RkISP1Path::releaseBuffers()
+{
+ while (!availableConverterBuffers_.empty())
+ availableConverterBuffers_.pop();
+
+ converterBuffers_.clear();
+
+ video_->releaseBuffers();
+}
+
+void RkISP1Path::pathBufferReady(FrameBuffer *buffer)
+{
+ Request *request = buffer->request();
+
+ if (request) {
+ internalBufferReady_.emit(buffer);
+ } else {
+ auto iter = converterBuffersMapping_.find(buffer);
+ if (iter != converterBuffersMapping_.end()) {
+ converter_->queueBuffer(buffer, iter->second);
+ converterBuffersMapping_.erase(iter);
+ } else {
+ LOG(RkISP1, Error)
+ << "Final buffer associated to converted buffer not found on "
+ << name_ << " path";
+ }
+ }
+}
+
+void RkISP1Path::pathConverterInputDone(FrameBuffer *buffer)
+{
+ availableConverterBuffers_.push(buffer);
+}
+
+void RkISP1Path::pathConverterOutputDone(FrameBuffer *buffer)
+{
+ internalBufferReady_.emit(buffer);
+}
+
+int RkISP1Path::queueBuffer(FrameBuffer *buffer)
+{
+ FrameBuffer *_buffer;
+
+ if (pipeConfig_->withConverter) {
+ if (availableConverterBuffers_.empty()) {
+ LOG(RkISP1, Error)
+ << "converter buffer underrun on " << name_ << " path";
+ return -ENOMEM;
+ }
+ _buffer = availableConverterBuffers_.front();
+ availableConverterBuffers_.pop();
+ converterBuffersMapping_[_buffer] = buffer;
+ } else {
+ _buffer = buffer;
+ }
+
+ return video_->queueBuffer(_buffer);
+}
+
int RkISP1Path::start()
{
int ret;
@@ -172,20 +516,23 @@ int RkISP1Path::start()
if (running_)
return -EBUSY;
- /* \todo Make buffer count user configurable. */
- ret = video_->importBuffers(RKISP1_BUFFER_COUNT);
- if (ret)
- return ret;
-
ret = video_->streamOn();
if (ret) {
LOG(RkISP1, Error)
<< "Failed to start " << name_ << " path";
-
- video_->releaseBuffers();
return ret;
}
+ if (pipeConfig_->withConverter) {
+ ret = converter_->start();
+ if (ret) {
+ LOG(RkISP1, Error)
+ << "Failed to start converter on " << name_ << " path";
+ stop();
+ return ret;
+ }
+ }
+
running_ = true;
return 0;
@@ -199,7 +546,8 @@ void RkISP1Path::stop()
if (video_->streamOff())
LOG(RkISP1, Warning) << "Failed to stop " << name_ << " path";
- video_->releaseBuffers();
+ if (pipeConfig_->withConverter)
+ converter_->stop();
running_ = false;
}
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1_path.h b/src/libcamera/pipeline/rkisp1/rkisp1_path.h
index f3f1ae39..0da3594f 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1_path.h
+++ b/src/libcamera/pipeline/rkisp1/rkisp1_path.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2020, Google Inc.
+ * Copyright 2022 NXP
*
* rkisp1path.h - Rockchip ISP1 path helper
*/
@@ -8,6 +9,7 @@
#pragma once
#include <memory>
+#include <queue>
#include <vector>
#include <libcamera/base/signal.h>
@@ -17,9 +19,11 @@
#include <libcamera/geometry.h>
#include <libcamera/pixel_format.h>
+#include "libcamera/internal/converter.h"
#include "libcamera/internal/media_object.h"
#include "libcamera/internal/v4l2_videodevice.h"
+
namespace libcamera {
class MediaDevice;
@@ -33,7 +37,7 @@ public:
RkISP1Path(const char *name, const Span<const PixelFormat> &formats,
const Size &minResolution, const Size &maxResolution);
- bool init(MediaDevice *media);
+ bool init(MediaDevice *media, MediaDevice *mediaConverter = nullptr);
int setEnabled(bool enable) { return link_->setEnabled(enable); }
bool isEnabled() const { return link_->flags() & MEDIA_LNK_FL_ENABLED; }
@@ -45,18 +49,31 @@ public:
const V4L2SubdeviceFormat &inputFormat);
int exportBuffers(unsigned int bufferCount,
- std::vector<std::unique_ptr<FrameBuffer>> *buffers)
- {
- return video_->exportBuffers(bufferCount, buffers);
- }
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers);
+
+ int allocateBuffers(unsigned int bufferCount);
+ void releaseBuffers();
int start();
void stop();
- int queueBuffer(FrameBuffer *buffer) { return video_->queueBuffer(buffer); }
- Signal<FrameBuffer *> &bufferReady() { return video_->bufferReady; }
+ int queueBuffer(FrameBuffer *buffer);
+ Signal<FrameBuffer *> &bufferReady() { return internalBufferReady_; }
+
+ StreamConfiguration internalStream() const
+ {
+ return internalStream_;
+ }
private:
+ struct Configuration {
+ SizeRange inputSizes;
+ PixelFormat inputFormat;
+ std::vector<PixelFormat> outputFormats;
+ SizeRange outputSizes;
+ bool withConverter;
+ };
+
static constexpr unsigned int RKISP1_BUFFER_COUNT = 4;
const char *name_;
@@ -65,10 +82,33 @@ private:
const Span<const PixelFormat> formats_;
const Size minResolution_;
const Size maxResolution_;
+ CameraConfiguration::Status getPipeConfiguration(
+ StreamConfiguration &streamCfg,
+ const RkISP1Path::Configuration **cfg) const;
+
+ StreamConfiguration generateNativeConfiguration(const Size &resolution);
+ CameraConfiguration::Status nativeValidate(StreamConfiguration *cfg);
+ int nativeConfigure(const StreamConfiguration &config,
+ const V4L2SubdeviceFormat &inputFormat);
std::unique_ptr<V4L2Subdevice> resizer_;
std::unique_ptr<V4L2VideoDevice> video_;
MediaLink *link_;
+
+ void initConverter(MediaDevice *mediaConverter);
+ std::unique_ptr<Converter> converter_;
+ std::vector<Configuration> configs_;
+ StreamConfiguration internalStream_;
+ const Configuration *pipeConfig_;
+ bool hasConverter_;
+ bool useConverter_;
+ Signal<FrameBuffer *> internalBufferReady_;
+ std::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;
+ std::queue<FrameBuffer *> availableConverterBuffers_;
+ std::map<FrameBuffer *, FrameBuffer *> converterBuffersMapping_;
+ void pathBufferReady(FrameBuffer *buffer);
+ void pathConverterInputDone(FrameBuffer *buffer);
+ void pathConverterOutputDone(FrameBuffer *buffer);
};
class RkISP1MainPath : public RkISP1Path
--
2.37.3
More information about the libcamera-devel
mailing list