libcamera: Introduce SoftwareIsp
Doxygen documentation by Dennis Bonke. Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s Tested-by: Pavel Machek <pavel@ucw.cz> Reviewed-by: Pavel Machek <pavel@ucw.cz> Co-developed-by: Dennis Bonke <admin@dennisbonke.com> Signed-off-by: Dennis Bonke <admin@dennisbonke.com> Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
committed by
Kieran Bingham
parent
c987946e43
commit
6362bd46ca
@@ -2,5 +2,6 @@
|
||||
|
||||
libcamera_internal_headers += files([
|
||||
'debayer_params.h',
|
||||
'software_isp.h',
|
||||
'swisp_stats.h',
|
||||
])
|
||||
|
||||
99
include/libcamera/internal/software_isp/software_isp.h
Normal file
99
include/libcamera/internal/software_isp/software_isp.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* Copyright (C) 2023, Linaro Ltd
|
||||
*
|
||||
* software_isp.h - Simple software ISP implementation
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include <libcamera/base/class.h>
|
||||
#include <libcamera/base/log.h>
|
||||
#include <libcamera/base/signal.h>
|
||||
#include <libcamera/base/thread.h>
|
||||
|
||||
#include <libcamera/geometry.h>
|
||||
#include <libcamera/pixel_format.h>
|
||||
|
||||
#include <libcamera/ipa/soft_ipa_interface.h>
|
||||
#include <libcamera/ipa/soft_ipa_proxy.h>
|
||||
|
||||
#include "libcamera/internal/camera_sensor.h"
|
||||
#include "libcamera/internal/dma_heaps.h"
|
||||
#include "libcamera/internal/pipeline_handler.h"
|
||||
#include "libcamera/internal/shared_mem_object.h"
|
||||
#include "libcamera/internal/software_isp/debayer_params.h"
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
class DebayerCpu;
|
||||
class FrameBuffer;
|
||||
class PixelFormat;
|
||||
struct StreamConfiguration;
|
||||
|
||||
LOG_DECLARE_CATEGORY(SoftwareIsp)
|
||||
|
||||
class SoftwareIsp
|
||||
{
|
||||
public:
|
||||
SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor);
|
||||
~SoftwareIsp();
|
||||
|
||||
int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
std::vector<PixelFormat> formats(PixelFormat input);
|
||||
|
||||
SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
|
||||
|
||||
std::tuple<unsigned int, unsigned int>
|
||||
strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
|
||||
|
||||
int configure(const StreamConfiguration &inputCfg,
|
||||
const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
|
||||
const ControlInfoMap &sensorControls);
|
||||
|
||||
int exportBuffers(unsigned int output, unsigned int count,
|
||||
std::vector<std::unique_ptr<FrameBuffer>> *buffers);
|
||||
|
||||
void processStats(const ControlList &sensorControls);
|
||||
|
||||
int start();
|
||||
void stop();
|
||||
|
||||
int queueBuffers(FrameBuffer *input,
|
||||
const std::map<unsigned int, FrameBuffer *> &outputs);
|
||||
|
||||
void process(FrameBuffer *input, FrameBuffer *output);
|
||||
|
||||
Signal<FrameBuffer *> inputBufferReady;
|
||||
Signal<FrameBuffer *> outputBufferReady;
|
||||
Signal<> ispStatsReady;
|
||||
Signal<const ControlList &> setSensorControls;
|
||||
|
||||
private:
|
||||
void saveIspParams();
|
||||
void setSensorCtrls(const ControlList &sensorControls);
|
||||
void statsReady();
|
||||
void inputReady(FrameBuffer *input);
|
||||
void outputReady(FrameBuffer *output);
|
||||
|
||||
std::unique_ptr<DebayerCpu> debayer_;
|
||||
Thread ispWorkerThread_;
|
||||
SharedMemObject<DebayerParams> sharedParams_;
|
||||
DebayerParams debayerParams_;
|
||||
DmaHeap dmaHeap_;
|
||||
|
||||
std::unique_ptr<ipa::soft::IPAProxySoft> ipa_;
|
||||
};
|
||||
|
||||
} /* namespace libcamera */
|
||||
@@ -10,5 +10,6 @@ endif
|
||||
libcamera_sources += files([
|
||||
'debayer.cpp',
|
||||
'debayer_cpu.cpp',
|
||||
'software_isp.cpp',
|
||||
'swstats_cpu.cpp',
|
||||
])
|
||||
|
||||
357
src/libcamera/software_isp/software_isp.cpp
Normal file
357
src/libcamera/software_isp/software_isp.cpp
Normal file
@@ -0,0 +1,357 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* Copyright (C) 2023, Linaro Ltd
|
||||
*
|
||||
* software_isp.cpp - Simple software ISP implementation
|
||||
*/
|
||||
|
||||
#include "libcamera/internal/software_isp/software_isp.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libcamera/formats.h>
|
||||
#include <libcamera/stream.h>
|
||||
|
||||
#include "libcamera/internal/bayer_format.h"
|
||||
#include "libcamera/internal/framebuffer.h"
|
||||
#include "libcamera/internal/ipa_manager.h"
|
||||
#include "libcamera/internal/mapped_framebuffer.h"
|
||||
|
||||
#include "debayer_cpu.h"
|
||||
|
||||
/**
|
||||
* \file software_isp.cpp
|
||||
* \brief Simple software ISP implementation
|
||||
*/
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
LOG_DEFINE_CATEGORY(SoftwareIsp)
|
||||
|
||||
/**
|
||||
* \class SoftwareIsp
|
||||
* \brief Class for the Software ISP
|
||||
*/
|
||||
|
||||
/**
|
||||
* \var SoftwareIsp::inputBufferReady
|
||||
* \brief A signal emitted when the input frame buffer completes
|
||||
*/
|
||||
|
||||
/**
|
||||
* \var SoftwareIsp::outputBufferReady
|
||||
* \brief A signal emitted when the output frame buffer completes
|
||||
*/
|
||||
|
||||
/**
|
||||
* \var SoftwareIsp::ispStatsReady
|
||||
* \brief A signal emitted when the statistics for IPA are ready
|
||||
*/
|
||||
|
||||
/**
|
||||
* \var SoftwareIsp::setSensorControls
|
||||
* \brief A signal emitted when the values to write to the sensor controls are
|
||||
* ready
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Constructs SoftwareIsp object
|
||||
* \param[in] pipe The pipeline handler in use
|
||||
* \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline
|
||||
* handler
|
||||
*/
|
||||
SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor)
|
||||
: debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10,
|
||||
DebayerParams::kGain10, 0.5f },
|
||||
dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
|
||||
{
|
||||
if (!dmaHeap_.isValid()) {
|
||||
LOG(SoftwareIsp, Error) << "Failed to create DmaHeap object";
|
||||
return;
|
||||
}
|
||||
|
||||
sharedParams_ = SharedMemObject<DebayerParams>("softIsp_params");
|
||||
if (!sharedParams_) {
|
||||
LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters";
|
||||
return;
|
||||
}
|
||||
|
||||
auto stats = std::make_unique<SwStatsCpu>();
|
||||
if (!stats->isValid()) {
|
||||
LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
|
||||
return;
|
||||
}
|
||||
stats->statsReady.connect(this, &SoftwareIsp::statsReady);
|
||||
|
||||
debayer_ = std::make_unique<DebayerCpu>(std::move(stats));
|
||||
debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady);
|
||||
debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady);
|
||||
|
||||
ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0);
|
||||
if (!ipa_) {
|
||||
LOG(SoftwareIsp, Error)
|
||||
<< "Creating IPA for software ISP failed";
|
||||
debayer_.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The API tuning file is made from the sensor name. If the tuning file
|
||||
* isn't found, fall back to the 'uncalibrated' file.
|
||||
*/
|
||||
std::string ipaTuningFile = ipa_->configurationFile(sensor->model() + ".yaml");
|
||||
if (ipaTuningFile.empty())
|
||||
ipaTuningFile = ipa_->configurationFile("uncalibrated.yaml");
|
||||
|
||||
int ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() },
|
||||
debayer_->getStatsFD(),
|
||||
sharedParams_.fd(),
|
||||
sensor->controls());
|
||||
if (ret) {
|
||||
LOG(SoftwareIsp, Error) << "IPA init failed";
|
||||
debayer_.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
ipa_->setIspParams.connect(this, &SoftwareIsp::saveIspParams);
|
||||
ipa_->setSensorControls.connect(this, &SoftwareIsp::setSensorCtrls);
|
||||
|
||||
debayer_->moveToThread(&ispWorkerThread_);
|
||||
}
|
||||
|
||||
SoftwareIsp::~SoftwareIsp()
|
||||
{
|
||||
/* make sure to destroy the DebayerCpu before the ispWorkerThread_ is gone */
|
||||
debayer_.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* \fn int SoftwareIsp::loadConfiguration([[maybe_unused]] const std::string &filename)
|
||||
* \brief Load a configuration from a file
|
||||
* \param[in] filename The file to load the configuration data from
|
||||
*
|
||||
* Currently is a stub doing nothing and always returning "success".
|
||||
*
|
||||
* \return 0 on success
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Process the statistics gathered
|
||||
* \param[in] sensorControls The sensor controls
|
||||
*
|
||||
* Requests the IPA to calculate new parameters for ISP and new control
|
||||
* values for the sensor.
|
||||
*/
|
||||
void SoftwareIsp::processStats(const ControlList &sensorControls)
|
||||
{
|
||||
ASSERT(ipa_);
|
||||
ipa_->processStats(sensorControls);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check the validity of Software Isp object
|
||||
* \return True if Software Isp is valid, false otherwise
|
||||
*/
|
||||
bool SoftwareIsp::isValid() const
|
||||
{
|
||||
return !!debayer_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the output formats supported for the given input format
|
||||
* \param[in] inputFormat The input format
|
||||
* \return All the supported output formats or an empty vector if there are none
|
||||
*/
|
||||
std::vector<PixelFormat> SoftwareIsp::formats(PixelFormat inputFormat)
|
||||
{
|
||||
ASSERT(debayer_);
|
||||
|
||||
return debayer_->formats(inputFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the supported output sizes for the given input format and size
|
||||
* \param[in] inputFormat The input format
|
||||
* \param[in] inputSize The input frame size
|
||||
* \return The valid size range or an empty range if there are none
|
||||
*/
|
||||
SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize)
|
||||
{
|
||||
ASSERT(debayer_);
|
||||
|
||||
return debayer_->sizes(inputFormat, inputSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output stride and the frame size in bytes for the given output format and size
|
||||
* \param[in] outputFormat The output format
|
||||
* \param[in] size The output size (width and height in pixels)
|
||||
* \return A tuple of the stride and the frame size in bytes, or a tuple of 0,0
|
||||
* if there is no valid output config
|
||||
*/
|
||||
std::tuple<unsigned int, unsigned int>
|
||||
SoftwareIsp::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
|
||||
{
|
||||
ASSERT(debayer_);
|
||||
|
||||
return debayer_->strideAndFrameSize(outputFormat, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure the SoftwareIsp object according to the passed in parameters
|
||||
* \param[in] inputCfg The input configuration
|
||||
* \param[in] outputCfgs The output configurations
|
||||
* \param[in] sensorControls ControlInfoMap of the controls supported by the sensor
|
||||
* \return 0 on success, a negative errno on failure
|
||||
*/
|
||||
int SoftwareIsp::configure(const StreamConfiguration &inputCfg,
|
||||
const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
|
||||
const ControlInfoMap &sensorControls)
|
||||
{
|
||||
ASSERT(ipa_ && debayer_);
|
||||
|
||||
int ret = ipa_->configure(sensorControls);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return debayer_->configure(inputCfg, outputCfgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Export the buffers from the Software ISP
|
||||
* \param[in] output Output stream index exporting the buffers
|
||||
* \param[in] count Number of buffers to allocate
|
||||
* \param[out] buffers Vector to store the allocated buffers
|
||||
* \return The number of allocated buffers on success or a negative error code
|
||||
* otherwise
|
||||
*/
|
||||
int SoftwareIsp::exportBuffers(unsigned int output, unsigned int count,
|
||||
std::vector<std::unique_ptr<FrameBuffer>> *buffers)
|
||||
{
|
||||
ASSERT(debayer_ != nullptr);
|
||||
|
||||
/* single output for now */
|
||||
if (output >= 1)
|
||||
return -EINVAL;
|
||||
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
const std::string name = "frame-" + std::to_string(i);
|
||||
const size_t frameSize = debayer_->frameSize();
|
||||
|
||||
FrameBuffer::Plane outPlane;
|
||||
outPlane.fd = SharedFD(dmaHeap_.alloc(name.c_str(), frameSize));
|
||||
if (!outPlane.fd.isValid()) {
|
||||
LOG(SoftwareIsp, Error)
|
||||
<< "failed to allocate a dma_buf";
|
||||
return -ENOMEM;
|
||||
}
|
||||
outPlane.offset = 0;
|
||||
outPlane.length = frameSize;
|
||||
|
||||
std::vector<FrameBuffer::Plane> planes{ outPlane };
|
||||
buffers->emplace_back(std::make_unique<FrameBuffer>(std::move(planes)));
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Queue buffers to Software ISP
|
||||
* \param[in] input The input framebuffer
|
||||
* \param[in] outputs The container holding the output stream indexes and
|
||||
* their respective frame buffer outputs
|
||||
* \return 0 on success, a negative errno on failure
|
||||
*/
|
||||
int SoftwareIsp::queueBuffers(FrameBuffer *input,
|
||||
const std::map<unsigned int, FrameBuffer *> &outputs)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
|
||||
/*
|
||||
* Validate the outputs as a sanity check: at least one output is
|
||||
* required, all outputs must reference a valid stream and no two
|
||||
* outputs can reference the same stream.
|
||||
*/
|
||||
if (outputs.empty())
|
||||
return -EINVAL;
|
||||
|
||||
for (auto [index, buffer] : outputs) {
|
||||
if (!buffer)
|
||||
return -EINVAL;
|
||||
if (index >= 1) /* only single stream atm */
|
||||
return -EINVAL;
|
||||
if (mask & (1 << index))
|
||||
return -EINVAL;
|
||||
|
||||
mask |= 1 << index;
|
||||
}
|
||||
|
||||
process(input, outputs.at(0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Starts the Software ISP streaming operation
|
||||
* \return 0 on success, any other value indicates an error
|
||||
*/
|
||||
int SoftwareIsp::start()
|
||||
{
|
||||
int ret = ipa_->start();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ispWorkerThread_.start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Stops the Software ISP streaming operation
|
||||
*/
|
||||
void SoftwareIsp::stop()
|
||||
{
|
||||
ispWorkerThread_.exit();
|
||||
ispWorkerThread_.wait();
|
||||
|
||||
ipa_->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Passes the input framebuffer to the ISP worker to process
|
||||
* \param[in] input The input framebuffer
|
||||
* \param[out] output The framebuffer to write the processed frame to
|
||||
*/
|
||||
void SoftwareIsp::process(FrameBuffer *input, FrameBuffer *output)
|
||||
{
|
||||
debayer_->invokeMethod(&DebayerCpu::process,
|
||||
ConnectionTypeQueued, input, output, debayerParams_);
|
||||
}
|
||||
|
||||
void SoftwareIsp::saveIspParams()
|
||||
{
|
||||
debayerParams_ = *sharedParams_;
|
||||
}
|
||||
|
||||
void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
|
||||
{
|
||||
setSensorControls.emit(sensorControls);
|
||||
}
|
||||
|
||||
void SoftwareIsp::statsReady()
|
||||
{
|
||||
ispStatsReady.emit();
|
||||
}
|
||||
|
||||
void SoftwareIsp::inputReady(FrameBuffer *input)
|
||||
{
|
||||
inputBufferReady.emit(input);
|
||||
}
|
||||
|
||||
void SoftwareIsp::outputReady(FrameBuffer *output)
|
||||
{
|
||||
outputBufferReady.emit(output);
|
||||
}
|
||||
|
||||
} /* namespace libcamera */
|
||||
Reference in New Issue
Block a user