[libcamera-devel] [PATCH 14/14] libcamera: pipeline: rkisp1: Add converter support
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Tue Oct 4 04:36:08 CEST 2022
Hi Xavier,
Thank you for the patch.
On Thu, Sep 08, 2022 at 08:48:50PM +0200, Xavier Roumegue via libcamera-devel wrote:
> 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.
Before doing a full review of this, I'm wondering what use case(s) you
envision for scaling with the dewarper. The ISP has a scaler at the
output, are there cases where you think scaling in the dewarper would
have an advantage ?
> 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
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list