libcamera: pipeline: simple: Enable use of Soft ISP and Soft IPA

To enable the Simple Soft ISP and Soft IPA for simple pipeline handler
configure the build with:
  -Dpipelines=simple -Dipas=simple

Also using the Soft ISP for the particular hardware platform must
be enabled in the supportedDevices[] table.  It is currently enabled
for and only for qcom-camss.

If the pipeline uses Converter, Soft ISP and Soft IPA aren't
available.

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>
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:
Andrey Konovalov 2024-04-16 11:13:49 +02:00 committed by Kieran Bingham
parent 4095a8184b
commit c50065c9eb
2 changed files with 127 additions and 26 deletions

View file

@ -34,6 +34,7 @@
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/pipeline_handler.h"
#include "libcamera/internal/software_isp/software_isp.h"
#include "libcamera/internal/v4l2_subdevice.h"
#include "libcamera/internal/v4l2_videodevice.h"
@ -185,18 +186,24 @@ struct SimplePipelineInfo {
* and the number of streams it supports.
*/
std::vector<std::pair<const char *, unsigned int>> converters;
/*
* Using Software ISP is to be enabled per driver.
*
* The Software ISP can't be used together with the converters.
*/
bool swIspEnabled;
};
namespace {
static const SimplePipelineInfo supportedDevices[] = {
{ "dcmipp", {} },
{ "imx7-csi", { { "pxp", 1 } } },
{ "j721e-csi2rx", {} },
{ "mtk-seninf", { { "mtk-mdp", 3 } } },
{ "mxc-isi", {} },
{ "qcom-camss", {} },
{ "sun6i-csi", {} },
{ "dcmipp", {}, false },
{ "imx7-csi", { { "pxp", 1 } }, false },
{ "j721e-csi2rx", {}, false },
{ "mtk-seninf", { { "mtk-mdp", 3 } }, false },
{ "mxc-isi", {}, false },
{ "qcom-camss", {}, true },
{ "sun6i-csi", {}, false },
};
} /* namespace */
@ -275,6 +282,7 @@ public:
bool useConversion_;
std::unique_ptr<Converter> converter_;
std::unique_ptr<SoftwareIsp> swIsp_;
private:
void tryPipeline(unsigned int code, const Size &size);
@ -282,6 +290,9 @@ private:
void conversionInputDone(FrameBuffer *buffer);
void conversionOutputDone(FrameBuffer *buffer);
void ispStatsReady();
void setSensorControls(const ControlList &sensorControls);
};
class SimpleCameraConfiguration : public CameraConfiguration
@ -333,6 +344,7 @@ public:
V4L2VideoDevice *video(const MediaEntity *entity);
V4L2Subdevice *subdev(const MediaEntity *entity);
MediaDevice *converter() { return converter_; }
bool swIspEnabled() const { return swIspEnabled_; }
protected:
int queueRequestDevice(Camera *camera, Request *request) override;
@ -361,6 +373,7 @@ private:
std::map<const MediaEntity *, EntityData> entities_;
MediaDevice *converter_;
bool swIspEnabled_;
};
/* -----------------------------------------------------------------------------
@ -510,6 +523,37 @@ int SimpleCameraData::init()
}
}
/*
* Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
*/
if (!converter_ && pipe->swIspEnabled()) {
swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get());
if (!swIsp_->isValid()) {
LOG(SimplePipeline, Warning)
<< "Failed to create software ISP, disabling software debayering";
swIsp_.reset();
} else {
/*
* The inputBufferReady signal is emitted from the soft ISP thread,
* and needs to be handled in the pipeline handler thread. Signals
* implement queued delivery, but this works transparently only if
* the receiver is bound to the target thread. As the
* SimpleCameraData class doesn't inherit from the Object class, it
* is not bound to any thread, and the signal would be delivered
* synchronously. Instead, connect the signal to a lambda function
* bound explicitly to the pipe, which is bound to the pipeline
* handler thread. The function then simply forwards the call to
* conversionInputDone().
*/
swIsp_->inputBufferReady.connect(pipe, [this](FrameBuffer *buffer) {
this->conversionInputDone(buffer);
});
swIsp_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
swIsp_->ispStatsReady.connect(this, &SimpleCameraData::ispStatsReady);
swIsp_->setSensorControls.connect(this, &SimpleCameraData::setSensorControls);
}
}
video_ = pipe->video(entities_.back().entity);
ASSERT(video_);
@ -600,12 +644,20 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)
config.captureFormat = pixelFormat;
config.captureSize = format.size;
if (!converter_) {
config.outputFormats = { pixelFormat };
config.outputSizes = config.captureSize;
} else {
if (converter_) {
config.outputFormats = converter_->formats(pixelFormat);
config.outputSizes = converter_->sizes(format.size);
} else if (swIsp_) {
config.outputFormats = swIsp_->formats(pixelFormat);
config.outputSizes = swIsp_->sizes(pixelFormat, format.size);
if (config.outputFormats.empty()) {
/* Do not use swIsp for unsupported pixelFormat's. */
config.outputFormats = { pixelFormat };
config.outputSizes = config.captureSize;
}
} else {
config.outputFormats = { pixelFormat };
config.outputSizes = config.captureSize;
}
configs_.push_back(config);
@ -751,9 +803,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
}
/*
* The converter is in use. Requeue the internal buffer for
* capture (unless the stream is being stopped), and complete
* the request with all the user-facing buffers.
* The converter or Software ISP is in use. Requeue the internal
* buffer for capture (unless the stream is being stopped), and
* complete the request with all the user-facing buffers.
*/
if (buffer->metadata().status != FrameMetadata::FrameCancelled)
video_->queueBuffer(buffer);
@ -799,9 +851,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
buffer->metadata().timestamp);
/*
* Queue the captured and the request buffer to the converter if format
* conversion is needed. If there's no queued request, just requeue the
* captured buffer for capture.
* Queue the captured and the request buffer to the converter or Software
* ISP if format conversion is needed. If there's no queued request, just
* requeue the captured buffer for capture.
*/
if (useConversion_) {
if (conversionQueue_.empty()) {
@ -809,7 +861,11 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
return;
}
if (converter_)
converter_->queueBuffers(buffer, conversionQueue_.front());
else
swIsp_->queueBuffers(buffer, conversionQueue_.front());
conversionQueue_.pop();
return;
}
@ -835,6 +891,19 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
pipe->completeRequest(request);
}
void SimpleCameraData::ispStatsReady()
{
/* \todo Use the DelayedControls class */
swIsp_->processStats(sensor_->getControls({ V4L2_CID_ANALOGUE_GAIN,
V4L2_CID_EXPOSURE }));
}
void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
{
ControlList ctrls(sensorControls);
sensor_->setControls(&ctrls);
}
/* Retrieve all source pads connected to a sink pad through active routes. */
std::vector<const MediaPad *> SimpleCameraData::routedSourcePads(MediaPad *sink)
{
@ -1050,7 +1119,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
/* Set the stride, frameSize and bufferCount. */
if (needConversion_) {
std::tie(cfg.stride, cfg.frameSize) =
data_->converter_->strideAndFrameSize(cfg.pixelFormat,
data_->converter_
? data_->converter_->strideAndFrameSize(cfg.pixelFormat,
cfg.size)
: data_->swIsp_->strideAndFrameSize(cfg.pixelFormat,
cfg.size);
if (cfg.stride == 0)
return Invalid;
@ -1214,7 +1286,10 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
inputCfg.stride = captureFormat.planes[0].bpl;
inputCfg.bufferCount = kNumInternalBuffers;
return data->converter_->configure(inputCfg, outputCfgs);
return data->converter_
? data->converter_->configure(inputCfg, outputCfgs)
: data->swIsp_->configure(inputCfg, outputCfgs,
data->sensor_->controls());
}
int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
@ -1228,7 +1303,10 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
* whether the converter is used or not.
*/
if (data->useConversion_)
return data->converter_->exportBuffers(data->streamIndex(stream),
return data->converter_
? data->converter_->exportBuffers(data->streamIndex(stream),
count, buffers)
: data->swIsp_->exportBuffers(data->streamIndex(stream),
count, buffers);
else
return data->video_->exportBuffers(count, buffers);
@ -1274,7 +1352,13 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
}
if (data->useConversion_) {
if (data->converter_)
ret = data->converter_->start();
else if (data->swIsp_)
ret = data->swIsp_->start();
else
ret = 0;
if (ret < 0) {
stop(camera);
return ret;
@ -1293,8 +1377,12 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
SimpleCameraData *data = cameraData(camera);
V4L2VideoDevice *video = data->video_;
if (data->useConversion_)
if (data->useConversion_) {
if (data->converter_)
data->converter_->stop();
else if (data->swIsp_)
data->swIsp_->stop();
}
video->streamOff();
video->releaseBuffers();
@ -1456,6 +1544,8 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
}
}
swIspEnabled_ = info->swIspEnabled;
/* Locate the sensors. */
std::vector<MediaEntity *> sensors = locateSensors();
if (sensors.empty()) {

View file

@ -256,3 +256,14 @@ At some point, yes.
> }
This could be handled better with DelayedControls.
---
12. Use DelayedControls class in ispStatsReady()
> void SimpleCameraData::ispStatsReady()
> {
> swIsp_->processStats(sensor_->getControls({ V4L2_CID_ANALOGUE_GAIN,
> V4L2_CID_EXPOSURE }));
You should use the DelayedControls class.