[libcamera-devel] [PATCH 4/5] libcamera: imx8-isi: Split Bayer/YUV config generation
Paul Elder
paul.elder at ideasonboard.com
Thu Mar 2 13:12:12 CET 2023
On Sun, Jan 29, 2023 at 02:58:29PM +0100, Jacopo Mondi via libcamera-devel wrote:
> At generateConfiguration() a YUV/RGB pixel format is preferred for the
> StillCapture/VideoRecording/Viewfinder roles, but currently there are no
> guarantees in place that the sensor provides a non-Bayer bus format from
> which YUV/RGB can be generated.
>
> This makes the default configuration generated for those roles not to
> work if the sensor is a RAW-only one.
>
> To improve the situation split the configuration generation in two,
> one for YUV modes and one for Raw Bayer mode.
>
> StreamRoles assigned to a YUV mode will try to first generate a YUV
> configuration and then fallback to RAW if that's what the sensor can
> provide.
>
> As an additional requirement, for YUV streams, the generated mode has to
> be validated with the sensor to confirm the desired sizes can be
> generated. In order to test a format on the sensor introduce
> CameraSensor::tryFormat().
imo CameraSensor::tryFormat() should be split into a separate patch, but
it's not that big of a deal.
>
> Reported-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> Signed-off-by: Jacopo Mondi <jacopo.mondi at ideasonboard.com>
> ---
> include/libcamera/internal/camera_sensor.h | 1 +
> src/libcamera/camera_sensor.cpp | 14 ++
> src/libcamera/pipeline/imx8-isi/imx8-isi.cpp | 231 +++++++++++++------
> 3 files changed, 170 insertions(+), 76 deletions(-)
>
> diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
> index b9f4d7867854..ce3a790f00ee 100644
> --- a/include/libcamera/internal/camera_sensor.h
> +++ b/include/libcamera/internal/camera_sensor.h
> @@ -54,6 +54,7 @@ public:
> V4L2SubdeviceFormat getFormat(const std::vector<unsigned int> &mbusCodes,
> const Size &size) const;
> int setFormat(V4L2SubdeviceFormat *format);
> + int tryFormat(V4L2SubdeviceFormat *format) const;
>
> const ControlInfoMap &controls() const;
> ControlList getControls(const std::vector<uint32_t> &ids);
> diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
> index a210aa4fa370..dfe593033def 100644
> --- a/src/libcamera/camera_sensor.cpp
> +++ b/src/libcamera/camera_sensor.cpp
> @@ -757,6 +757,20 @@ int CameraSensor::setFormat(V4L2SubdeviceFormat *format)
> return 0;
> }
>
> +/**
> + * \brief Try the sensor output format
> + * \param[in] format The desired sensor output format
> + *
> + * The ranges of any controls associated with the sensor are not updated.
> + *
> + * \return 0 on success or a negative error code otherwise
> + */
> +int CameraSensor::tryFormat(V4L2SubdeviceFormat *format) const
> +{
> + return subdev_->setFormat(pad_, format,
> + V4L2Subdevice::Whence::TryFormat);
> +}
> +
> /**
> * \brief Retrieve the supported V4L2 controls and their information
> *
> diff --git a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
> index 5976a63d27dd..0e1e87c7a2aa 100644
> --- a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
> +++ b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
> @@ -145,6 +145,10 @@ private:
>
> Pipe *pipeFromStream(Camera *camera, const Stream *stream);
>
> + StreamConfiguration generateYUVConfiguration(Camera *camera,
> + const Size &size);
> + StreamConfiguration generateRawConfiguration(Camera *camera);
> +
> void bufferReady(FrameBuffer *buffer);
>
> MediaDevice *isiDev_;
> @@ -705,6 +709,129 @@ PipelineHandlerISI::PipelineHandlerISI(CameraManager *manager)
> {
> }
>
> +/*
> + * Generate a StreamConfiguration for YUV/RGB use case.
> + *
> + * Verify it the sensor can produce a YUV/RGB media bus format and collect
> + * all the processed pixel formats the ISI can generate as supported stream
> + * configurations.
> + */
> +StreamConfiguration PipelineHandlerISI::generateYUVConfiguration(Camera *camera,
> + const Size &size)
> +{
> + ISICameraData *data = cameraData(camera);
> + PixelFormat pixelFormat = formats::YUYV;
> + unsigned int mbusCode;
> +
> + mbusCode = data->getYuvMediaBusFormat(&pixelFormat);
> + if (!mbusCode)
> + return {};
> +
> + /* Adjust the requested size to the sensor's capabilities. */
> + const CameraSensor *sensor = data->sensor_.get();
> +
> + V4L2SubdeviceFormat sensorFmt;
> + sensorFmt.mbus_code = mbusCode;
> + sensorFmt.size = size;
> +
> + int ret = sensor->tryFormat(&sensorFmt);
> + if (ret) {
> + LOG(ISI, Error) << "Failed to try sensor format.";
> + return {};
> + }
> +
> + Size sensorSize = sensorFmt.size;
> +
> + /*
> + * Populate the StreamConfiguration.
> + *
> + * As the sensor supports at least one YUV/RGB media bus format all the
> + * processed ones in formatsMap_ can be generated from it.
> + */
> + std::map<PixelFormat, std::vector<SizeRange>> streamFormats;
> +
> + for (const auto &[pixFmt, pipeFmt] : ISICameraConfiguration::formatsMap_) {
> + const PixelFormatInfo &info = PixelFormatInfo::info(pixFmt);
> + if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)
> + continue;
> +
> + streamFormats[pixFmt] = { { kMinISISize, sensorSize } };
> + }
> +
> + StreamFormats formats(streamFormats);
> +
> + StreamConfiguration cfg(formats);
> + cfg.pixelFormat = pixelFormat;
> + cfg.size = sensorSize;
> + cfg.bufferCount = 4;
> +
> + return cfg;
> +}
> +
> +/*
> + * Generate a StreamConfiguration for Raw Bayer use case. Verify if the sensor
> + * can produce the requested RAW bayer format and eventually adjust it to
> + * the one with the largest bit-depth the sensor can produce.
> + */
> +StreamConfiguration PipelineHandlerISI::generateRawConfiguration(Camera *camera)
> +{
> + const std::map<unsigned int, PixelFormat> rawFormats = {
static?
Other than that, looks good to me.
Reviewed-by: Paul Elder <paul.elder at ideasonboard.com>
> + { MEDIA_BUS_FMT_SBGGR8_1X8, formats::SBGGR8 },
> + { MEDIA_BUS_FMT_SGBRG8_1X8, formats::SGBRG8 },
> + { MEDIA_BUS_FMT_SGRBG8_1X8, formats::SGRBG8 },
> + { MEDIA_BUS_FMT_SRGGB8_1X8, formats::SRGGB8 },
> + { MEDIA_BUS_FMT_SBGGR10_1X10, formats::SBGGR10 },
> + { MEDIA_BUS_FMT_SGBRG10_1X10, formats::SGBRG10 },
> + { MEDIA_BUS_FMT_SGRBG10_1X10, formats::SGRBG10 },
> + { MEDIA_BUS_FMT_SRGGB10_1X10, formats::SRGGB10 },
> + { MEDIA_BUS_FMT_SBGGR12_1X12, formats::SBGGR12 },
> + { MEDIA_BUS_FMT_SGBRG12_1X12, formats::SGBRG12 },
> + { MEDIA_BUS_FMT_SGRBG12_1X12, formats::SGRBG12 },
> + { MEDIA_BUS_FMT_SRGGB12_1X12, formats::SRGGB12 },
> + };
> +
> + ISICameraData *data = cameraData(camera);
> + PixelFormat pixelFormat = formats::SBGGR10;
> + unsigned int mbusCode;
> +
> + /* pixelFormat will be adjusted, if the sensor can produce RAW. */
> + mbusCode = data->getRawMediaBusFormat(&pixelFormat);
> + if (!mbusCode)
> + return {};
> +
> + /*
> + * Populate the StreamConfiguration with all the supported Bayer
> + * formats the sensor can produce.
> + */
> + std::map<PixelFormat, std::vector<SizeRange>> streamFormats;
> + const CameraSensor *sensor = data->sensor_.get();
> +
> + for (unsigned int code : sensor->mbusCodes()) {
> + /* Find a Bayer media bus code from the sensor. */
> + const BayerFormat &bayerFormat = BayerFormat::fromMbusCode(code);
> + if (!bayerFormat.isValid())
> + continue;
> +
> + auto it = rawFormats.find(code);
> + if (it == rawFormats.end()) {
> + LOG(ISI, Warning) << bayerFormat
> + << " not supported in ISI formats map.";
> + continue;
> + }
> +
> + streamFormats[it->second] = { { kMinISISize, sensor->resolution() } };
> + }
> +
> + StreamFormats formats(streamFormats);
> +
> + StreamConfiguration cfg(formats);
> + cfg.size = sensor->resolution();
> + cfg.pixelFormat = pixelFormat;
> + cfg.bufferCount = 4;
> +
> + return cfg;
> +}
> +
> std::unique_ptr<CameraConfiguration>
> PipelineHandlerISI::generateConfiguration(Camera *camera,
> const StreamRoles &roles)
> @@ -722,79 +849,45 @@ PipelineHandlerISI::generateConfiguration(Camera *camera,
> return nullptr;
> }
>
> - bool isRaw = false;
> for (const auto &role : roles) {
> /*
> - * Prefer the following formats
> + * Prefer the following formats:
> * - Still Capture: Full resolution YUYV
> * - ViewFinder/VideoRecording: 1080p YUYV
> - * - RAW: sensor's native format and resolution
> + * - RAW: Full resolution Bayer
> */
> - std::map<PixelFormat, std::vector<SizeRange>> streamFormats;
> - PixelFormat pixelFormat;
> - Size size;
> + StreamConfiguration cfg;
>
> switch (role) {
> case StreamRole::StillCapture:
> - /*
> - * \todo Make sure the sensor can produce non-RAW formats
> - * compatible with the ones supported by the pipeline.
> - */
> - size = data->sensor_->resolution();
> - pixelFormat = formats::YUYV;
> + cfg = generateYUVConfiguration(camera,
> + data->sensor_->resolution());
> + if (!cfg.pixelFormat.isValid()) {
> + /*
> + * Fallback to use a Bayer format if that's what
> + * the sensor supports.
> + */
> + cfg = generateRawConfiguration(camera);
> + }
> +
> break;
>
> case StreamRole::Viewfinder:
> case StreamRole::VideoRecording:
> - /*
> - * \todo Make sure the sensor can produce non-RAW formats
> - * compatible with the ones supported by the pipeline.
> - */
> - size = PipelineHandlerISI::kPreviewSize;
> - pixelFormat = formats::YUYV;
> - break;
> -
> - case StreamRole::Raw: {
> - /*
> - * Make sure the sensor can generate a RAW format and
> - * prefer the ones with a larger bitdepth.
> - */
> - const ISICameraConfiguration::FormatMap::value_type *rawPipeFormat = nullptr;
> - unsigned int maxDepth = 0;
> -
> - for (unsigned int code : data->sensor_->mbusCodes()) {
> - const BayerFormat &bayerFormat = BayerFormat::fromMbusCode(code);
> - if (!bayerFormat.isValid())
> - continue;
> -
> - /* Make sure the format is supported by the pipeline handler. */
> - auto it = std::find_if(ISICameraConfiguration::formatsMap_.begin(),
> - ISICameraConfiguration::formatsMap_.end(),
> - [code](auto &isiFormat) {
> - auto &pipe = isiFormat.second;
> - return pipe.sensorCode == code;
> - });
> - if (it == ISICameraConfiguration::formatsMap_.end())
> - continue;
> -
> - if (bayerFormat.bitDepth > maxDepth) {
> - maxDepth = bayerFormat.bitDepth;
> - rawPipeFormat = &(*it);
> - }
> - }
> -
> - if (!rawPipeFormat) {
> - LOG(ISI, Error)
> - << "Cannot generate a configuration for RAW stream";
> - return nullptr;
> + cfg = generateYUVConfiguration(camera,
> + PipelineHandlerISI::kPreviewSize);
> + if (!cfg.pixelFormat.isValid()) {
> + /*
> + * Fallback to use a Bayer format if that's what
> + * the sensor supports.
> + */
> + cfg = generateRawConfiguration(camera);
> }
>
> - size = data->sensor_->resolution();
> - pixelFormat = rawPipeFormat->first;
> -
> - streamFormats[pixelFormat] = { { kMinISISize, size } };
> - isRaw = true;
> + break;
>
> + case StreamRole::Raw: {
> + cfg = generateRawConfiguration(camera);
> break;
> }
>
> @@ -803,26 +896,12 @@ PipelineHandlerISI::generateConfiguration(Camera *camera,
> return nullptr;
> }
>
> - /*
> - * For non-RAW configurations the ISI can perform colorspace
> - * conversion. List all the supported output formats here.
> - */
> - if (!isRaw) {
> - for (const auto &[pixFmt, pipeFmt] : ISICameraConfiguration::formatsMap_) {
> - const PixelFormatInfo &info = PixelFormatInfo::info(pixFmt);
> - if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)
> - continue;
> -
> - streamFormats[pixFmt] = { { kMinISISize, size } };
> - }
> + if (!cfg.pixelFormat.isValid()) {
> + LOG(ISI, Error)
> + << "Cannot generate configuration for role: " << role;
> + return nullptr;
> }
>
> - StreamFormats formats(streamFormats);
> -
> - StreamConfiguration cfg(formats);
> - cfg.pixelFormat = pixelFormat;
> - cfg.size = size;
> - cfg.bufferCount = 4;
> config->addConfiguration(cfg);
> }
>
> --
> 2.39.0
>
More information about the libcamera-devel
mailing list