[libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID pipeline support

Kieran Bingham kieran.bingham at ideasonboard.com
Fri Jun 19 14:10:24 CEST 2020


Hi Kieran,

On 19/06/2020 13:02, Kieran Bingham wrote:
> The VIVID driver supports more pixel formats and properties than the VIMC
> driver, and can provide extended testing for libcamera.
> 
> The VIMC pipeline handler is duplicated and simplified to support the
> VIVID device.
> 
> Unfortuantely, the VIVID device can not be handled by either of the

"Unfortunately"

> generic UVC or Simple pipeline handlers.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham at ideasonboard.com>

Of course I realise there is resistance to merging more pipeline
handlers at the moment, but this is now the *third* time I've sent this
pipeline out for individuals to use/see - so I'm sending it to the list.


> ---
>  meson_options.txt                        |   2 +-
>  src/libcamera/pipeline/vivid/meson.build |   5 +
>  src/libcamera/pipeline/vivid/vivid.cpp   | 441 +++++++++++++++++++++++
>  3 files changed, 447 insertions(+), 1 deletion(-)
>  create mode 100644 src/libcamera/pipeline/vivid/meson.build
>  create mode 100644 src/libcamera/pipeline/vivid/vivid.cpp
> 
> diff --git a/meson_options.txt b/meson_options.txt
> index badace151bb6..dc4684df49b2 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -16,7 +16,7 @@ option('gstreamer',
>  
>  option('pipelines',
>          type : 'array',
> -        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo', 'vimc'],
> +        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo', 'vimc', 'vivid'],
>          description : 'Select which pipeline handlers to include')
>  
>  option('test',
> diff --git a/src/libcamera/pipeline/vivid/meson.build b/src/libcamera/pipeline/vivid/meson.build
> new file mode 100644
> index 000000000000..086bb825387c
> --- /dev/null
> +++ b/src/libcamera/pipeline/vivid/meson.build
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: CC0-1.0
> +
> +libcamera_sources += files([
> +    'vivid.cpp',
> +])
> diff --git a/src/libcamera/pipeline/vivid/vivid.cpp b/src/libcamera/pipeline/vivid/vivid.cpp
> new file mode 100644
> index 000000000000..b811e33a0299
> --- /dev/null
> +++ b/src/libcamera/pipeline/vivid/vivid.cpp
> @@ -0,0 +1,441 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2018, Google Inc.

This should be 2020 of course,

> + *
> + * vivid.cpp - Pipeline handler for the vivid capture device
> + */
> +
> +#include <algorithm>
> +#include <iomanip>
> +#include <map>
> +#include <math.h>
> +#include <tuple>
> +
> +#include <linux/media-bus-format.h>
> +#include <linux/version.h>
> +
> +#include <libcamera/camera.h>
> +#include <libcamera/control_ids.h>
> +#include <libcamera/controls.h>

> +#include <libcamera/ipa/ipa_interface.h>
> +#include <libcamera/ipa/ipa_module_info.h>

No need for IPA headers of course, as there is no IPA.

> +#include <libcamera/request.h>
> +#include <libcamera/stream.h>
> +
> +#include "libcamera/internal/camera_sensor.h"
> +#include "libcamera/internal/device_enumerator.h"
> +#include "libcamera/internal/ipa_manager.h"
> +#include "libcamera/internal/log.h"
> +#include "libcamera/internal/media_device.h"
> +#include "libcamera/internal/pipeline_handler.h"
> +#include "libcamera/internal/utils.h"
> +#include "libcamera/internal/v4l2_controls.h"
> +#include "libcamera/internal/v4l2_subdevice.h"
> +#include "libcamera/internal/v4l2_videodevice.h"
> +
> +#define VIVID_CID_CUSTOM_BASE           (V4L2_CID_USER_BASE | 0xf000)
> +#define VIVID_CID_BUTTON                (VIVID_CID_CUSTOM_BASE + 0)
> +#define VIVID_CID_BOOLEAN               (VIVID_CID_CUSTOM_BASE + 1)
> +#define VIVID_CID_INTEGER               (VIVID_CID_CUSTOM_BASE + 2)
> +#define VIVID_CID_INTEGER64             (VIVID_CID_CUSTOM_BASE + 3)
> +#define VIVID_CID_MENU                  (VIVID_CID_CUSTOM_BASE + 4)
> +#define VIVID_CID_STRING                (VIVID_CID_CUSTOM_BASE + 5)
> +#define VIVID_CID_BITMASK               (VIVID_CID_CUSTOM_BASE + 6)
> +#define VIVID_CID_INTMENU               (VIVID_CID_CUSTOM_BASE + 7)
> +#define VIVID_CID_U32_ARRAY             (VIVID_CID_CUSTOM_BASE + 8)
> +#define VIVID_CID_U16_MATRIX            (VIVID_CID_CUSTOM_BASE + 9)
> +#define VIVID_CID_U8_4D_ARRAY           (VIVID_CID_CUSTOM_BASE + 10)
> +
> +#define VIVID_CID_VIVID_BASE            (0x00f00000 | 0xf000)
> +#define VIVID_CID_VIVID_CLASS           (0x00f00000 | 1)
> +#define VIVID_CID_TEST_PATTERN          (VIVID_CID_VIVID_BASE + 0)
> +#define VIVID_CID_OSD_TEXT_MODE         (VIVID_CID_VIVID_BASE + 1)
> +#define VIVID_CID_HOR_MOVEMENT          (VIVID_CID_VIVID_BASE + 2)
> +#define VIVID_CID_VERT_MOVEMENT         (VIVID_CID_VIVID_BASE + 3)
> +#define VIVID_CID_SHOW_BORDER           (VIVID_CID_VIVID_BASE + 4)
> +#define VIVID_CID_SHOW_SQUARE           (VIVID_CID_VIVID_BASE + 5)
> +#define VIVID_CID_INSERT_SAV            (VIVID_CID_VIVID_BASE + 6)
> +#define VIVID_CID_INSERT_EAV            (VIVID_CID_VIVID_BASE + 7)
> +#define VIVID_CID_VBI_CAP_INTERLACED    (VIVID_CID_VIVID_BASE + 8)
> +
> +#define VIVID_CID_HFLIP                 (VIVID_CID_VIVID_BASE + 20)
> +#define VIVID_CID_VFLIP                 (VIVID_CID_VIVID_BASE + 21)
> +#define VIVID_CID_STD_ASPECT_RATIO      (VIVID_CID_VIVID_BASE + 22)
> +#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO       (VIVID_CID_VIVID_BASE + 23)
> +#define VIVID_CID_TSTAMP_SRC            (VIVID_CID_VIVID_BASE + 24)
> +#define VIVID_CID_COLORSPACE            (VIVID_CID_VIVID_BASE + 25)
> +#define VIVID_CID_XFER_FUNC             (VIVID_CID_VIVID_BASE + 26)
> +#define VIVID_CID_YCBCR_ENC             (VIVID_CID_VIVID_BASE + 27)
> +#define VIVID_CID_QUANTIZATION          (VIVID_CID_VIVID_BASE + 28)
> +#define VIVID_CID_LIMITED_RGB_RANGE     (VIVID_CID_VIVID_BASE + 29)
> +#define VIVID_CID_ALPHA_MODE            (VIVID_CID_VIVID_BASE + 30)
> +#define VIVID_CID_HAS_CROP_CAP          (VIVID_CID_VIVID_BASE + 31)
> +#define VIVID_CID_HAS_COMPOSE_CAP       (VIVID_CID_VIVID_BASE + 32)
> +#define VIVID_CID_HAS_SCALER_CAP        (VIVID_CID_VIVID_BASE + 33)
> +#define VIVID_CID_HAS_CROP_OUT          (VIVID_CID_VIVID_BASE + 34)
> +#define VIVID_CID_HAS_COMPOSE_OUT       (VIVID_CID_VIVID_BASE + 35)
> +#define VIVID_CID_HAS_SCALER_OUT        (VIVID_CID_VIVID_BASE + 36)
> +#define VIVID_CID_LOOP_VIDEO            (VIVID_CID_VIVID_BASE + 37)
> +#define VIVID_CID_SEQ_WRAP              (VIVID_CID_VIVID_BASE + 38)
> +#define VIVID_CID_TIME_WRAP             (VIVID_CID_VIVID_BASE + 39)
> +#define VIVID_CID_MAX_EDID_BLOCKS       (VIVID_CID_VIVID_BASE + 40)
> +#define VIVID_CID_PERCENTAGE_FILL       (VIVID_CID_VIVID_BASE + 41)
> +#define VIVID_CID_REDUCED_FPS           (VIVID_CID_VIVID_BASE + 42)
> +#define VIVID_CID_HSV_ENC               (VIVID_CID_VIVID_BASE + 43)
> +#define VIVID_CID_DISPLAY_PRESENT       (VIVID_CID_VIVID_BASE + 44)
> +
> +#define VIVID_CID_STD_SIGNAL_MODE       (VIVID_CID_VIVID_BASE + 60)
> +#define VIVID_CID_STANDARD              (VIVID_CID_VIVID_BASE + 61)
> +#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE        (VIVID_CID_VIVID_BASE + 62)
> +#define VIVID_CID_DV_TIMINGS            (VIVID_CID_VIVID_BASE + 63)
> +#define VIVID_CID_PERC_DROPPED          (VIVID_CID_VIVID_BASE + 64)
> +#define VIVID_CID_DISCONNECT            (VIVID_CID_VIVID_BASE + 65)
> +#define VIVID_CID_DQBUF_ERROR           (VIVID_CID_VIVID_BASE + 66)
> +#define VIVID_CID_QUEUE_SETUP_ERROR     (VIVID_CID_VIVID_BASE + 67)
> +#define VIVID_CID_BUF_PREPARE_ERROR     (VIVID_CID_VIVID_BASE + 68)
> +#define VIVID_CID_START_STR_ERROR       (VIVID_CID_VIVID_BASE + 69)
> +#define VIVID_CID_QUEUE_ERROR           (VIVID_CID_VIVID_BASE + 70)
> +#define VIVID_CID_CLEAR_FB              (VIVID_CID_VIVID_BASE + 71)
> +#define VIVID_CID_REQ_VALIDATE_ERROR    (VIVID_CID_VIVID_BASE + 72)
> +
> +#define VIVID_CID_RADIO_SEEK_MODE       (VIVID_CID_VIVID_BASE + 90)
> +#define VIVID_CID_RADIO_SEEK_PROG_LIM   (VIVID_CID_VIVID_BASE + 91)
> +#define VIVID_CID_RADIO_RX_RDS_RBDS     (VIVID_CID_VIVID_BASE + 92)
> +#define VIVID_CID_RADIO_RX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 93)
> +
> +#define VIVID_CID_RADIO_TX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 94)
> +
> +#define VIVID_CID_SDR_CAP_FM_DEVIATION  (VIVID_CID_VIVID_BASE + 110)
> +
> +
> +namespace libcamera {
> +
> +LOG_DEFINE_CATEGORY(VIVID)
> +
> +class VividCameraData : public CameraData
> +{
> +public:
> +	VividCameraData(PipelineHandler *pipe, MediaDevice *media)
> +		: CameraData(pipe), media_(media), video_(nullptr)
> +	{
> +	}
> +
> +	~VividCameraData()
> +	{
> +		delete video_;
> +	}
> +
> +	int init();
> +	void bufferReady(FrameBuffer *buffer);
> +
> +	MediaDevice *media_;
> +	V4L2VideoDevice *video_;
> +	Stream stream_;
> +};
> +
> +class VividCameraConfiguration : public CameraConfiguration
> +{
> +public:
> +	VividCameraConfiguration();
> +
> +	Status validate() override;
> +};
> +
> +class PipelineHandlerVivid : public PipelineHandler
> +{
> +public:
> +	PipelineHandlerVivid(CameraManager *manager);
> +
> +	CameraConfiguration *generateConfiguration(Camera *camera,
> +						   const StreamRoles &roles) override;
> +	int configure(Camera *camera, CameraConfiguration *config) override;
> +
> +	int exportFrameBuffers(Camera *camera, Stream *stream,
> +			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> +
> +	int start(Camera *camera) override;
> +	void stop(Camera *camera) override;
> +
> +	int queueRequestDevice(Camera *camera, Request *request) override;
> +
> +	bool match(DeviceEnumerator *enumerator) override;
> +
> +private:
> +	int processControls(VividCameraData *data, Request *request);
> +
> +	VividCameraData *cameraData(const Camera *camera)
> +	{
> +		return static_cast<VividCameraData *>(
> +			PipelineHandler::cameraData(camera));
> +	}
> +};
> +
> +VividCameraConfiguration::VividCameraConfiguration()
> +	: CameraConfiguration()
> +{
> +}
> +
> +CameraConfiguration::Status VividCameraConfiguration::validate()
> +{
> +	Status status = Valid;
> +
> +	if (config_.empty())
> +		return Invalid;
> +
> +	/* Cap the number of entries to the available streams. */
> +	if (config_.size() > 1) {
> +		config_.resize(1);
> +		status = Adjusted;
> +	}
> +
> +	StreamConfiguration &cfg = config_[0];
> +
> +	/* Adjust the pixel format. */
> +	const std::vector<libcamera::PixelFormat> formats = cfg.formats().pixelformats();
> +	if (std::find(formats.begin(), formats.end(), cfg.pixelFormat) == formats.end()) {
> +		cfg.pixelFormat = cfg.formats().pixelformats()[0];
> +		LOG(VIVID, Debug) << "Adjusting format to " << cfg.pixelFormat.toString();
> +		status = Adjusted;
> +	}
> +
> +	cfg.bufferCount = 4;
> +
> +	return status;
> +}
> +
> +PipelineHandlerVivid::PipelineHandlerVivid(CameraManager *manager)
> +	: PipelineHandler(manager)
> +{
> +}
> +
> +CameraConfiguration *PipelineHandlerVivid::generateConfiguration(Camera *camera,
> +								 const StreamRoles &roles)
> +{
> +	CameraConfiguration *config = new VividCameraConfiguration();
> +	VividCameraData *data = cameraData(camera);
> +
> +	if (roles.empty())
> +		return config;
> +
> +	std::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =
> +		data->video_->formats();
> +	std::map<PixelFormat, std::vector<SizeRange>> deviceFormats;
> +	std::transform(v4l2Formats.begin(), v4l2Formats.end(),
> +		       std::inserter(deviceFormats, deviceFormats.begin()),
> +		       [&](const decltype(v4l2Formats)::value_type &format) {
> +			       return decltype(deviceFormats)::value_type{
> +				       format.first.toPixelFormat(),
> +				       format.second
> +			       };
> +		       });
> +
> +	StreamFormats formats(deviceFormats);
> +	StreamConfiguration cfg(formats);
> +
> +	cfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888);
> +	cfg.size = { 1280, 720 };
> +	cfg.bufferCount = 4;
> +
> +	config->addConfiguration(cfg);
> +
> +	config->validate();
> +
> +	return config;
> +}
> +
> +int PipelineHandlerVivid::configure(Camera *camera, CameraConfiguration *config)
> +{
> +	VividCameraData *data = cameraData(camera);
> +	StreamConfiguration &cfg = config->at(0);
> +	int ret;
> +
> +	V4L2DeviceFormat format = {};
> +	format.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat);
> +	format.size = cfg.size;
> +
> +	ret = data->video_->setFormat(&format);
> +	if (ret)
> +		return ret;
> +
> +	if (format.size != cfg.size ||
> +	    format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat))
> +		return -EINVAL;
> +
> +	cfg.setStream(&data->stream_);
> +	cfg.stride = format.planes[0].bpl;
> +
> +	return 0;
> +}
> +
> +int PipelineHandlerVivid::exportFrameBuffers(Camera *camera, Stream *stream,
> +					     std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> +{
> +	VividCameraData *data = cameraData(camera);
> +	unsigned int count = stream->configuration().bufferCount;
> +
> +	return data->video_->exportBuffers(count, buffers);
> +}
> +
> +int PipelineHandlerVivid::start(Camera *camera)
> +{
> +	VividCameraData *data = cameraData(camera);
> +	unsigned int count = data->stream_.configuration().bufferCount;
> +
> +	int ret = data->video_->importBuffers(count);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = data->video_->streamOn();
> +	if (ret < 0) {
> +		data->ipa_->stop();
> +		data->video_->releaseBuffers();
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +void PipelineHandlerVivid::stop(Camera *camera)
> +{
> +	VividCameraData *data = cameraData(camera);
> +	data->video_->streamOff();
> +	data->video_->releaseBuffers();
> +}
> +
> +int PipelineHandlerVivid::processControls(VividCameraData *data, Request *request)
> +{
> +	ControlList controls(data->video_->controls());
> +
> +	for (auto it : request->controls()) {
> +		unsigned int id = it.first;
> +		unsigned int offset;
> +		uint32_t cid;
> +
> +		if (id == controls::Brightness) {
> +			cid = V4L2_CID_BRIGHTNESS;
> +			offset = 128;
> +		} else if (id == controls::Contrast) {
> +			cid = V4L2_CID_CONTRAST;
> +			offset = 0;
> +		} else if (id == controls::Saturation) {
> +			cid = V4L2_CID_SATURATION;
> +			offset = 0;
> +		} else {
> +			continue;
> +		}
> +
> +		int32_t value = lroundf(it.second.get<float>() * 128 + offset);
> +		controls.set(cid, utils::clamp(value, 0, 255));
> +	}
> +
> +	for (const auto &ctrl : controls)
> +		LOG(VIVID, Debug)
> +			<< "Setting control " << utils::hex(ctrl.first)
> +			<< " to " << ctrl.second.toString();
> +
> +	int ret = data->video_->setControls(&controls);
> +	if (ret) {
> +		LOG(VIVID, Error) << "Failed to set controls: " << ret;
> +		return ret < 0 ? ret : -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +int PipelineHandlerVivid::queueRequestDevice(Camera *camera, Request *request)
> +{
> +	VividCameraData *data = cameraData(camera);
> +	FrameBuffer *buffer = request->findBuffer(&data->stream_);
> +	if (!buffer) {
> +		LOG(VIVID, Error)
> +			<< "Attempt to queue request with invalid stream";
> +
> +		return -ENOENT;
> +	}
> +
> +	int ret = processControls(data, request);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = data->video_->queueBuffer(buffer);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +bool PipelineHandlerVivid::match(DeviceEnumerator *enumerator)
> +{
> +	DeviceMatch dm("vivid");
> +	dm.add("vivid-000-vid-cap");
> +
> +	MediaDevice *media = acquireMediaDevice(enumerator, dm);
> +	if (!media)
> +		return false;
> +
> +	std::unique_ptr<VividCameraData> data = std::make_unique<VividCameraData>(this, media);
> +
> +	/* Locate and open the capture video node. */
> +	if (data->init())
> +		return false;
> +
> +	/* Create and register the camera. */
> +	std::set<Stream *> streams{ &data->stream_ };
> +	std::shared_ptr<Camera> camera = Camera::create(this, data->video_->deviceName(), streams);
> +	registerCamera(std::move(camera), std::move(data));
> +
> +	return true;
> +}
> +
> +int VividCameraData::init()
> +{
> +	video_ = new V4L2VideoDevice(media_->getEntityByName("vivid-000-vid-cap"));
> +	if (video_->open())
> +		return -ENODEV;
> +
> +	video_->bufferReady.connect(this, &VividCameraData::bufferReady);
> +
> +	/* Initialise the supported controls. */
> +	const ControlInfoMap &controls = video_->controls();
> +	ControlInfoMap::Map ctrls;
> +
> +	for (const auto &ctrl : controls) {
> +		const ControlId *id;
> +		ControlInfo info;
> +
> +		switch (ctrl.first->id()) {
> +		case V4L2_CID_BRIGHTNESS:
> +			id = &controls::Brightness;
> +			info = ControlInfo{ { -1.0f }, { 1.0f }, { 0.0f } };
> +			break;
> +		case V4L2_CID_CONTRAST:
> +			id = &controls::Contrast;
> +			info = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };
> +			break;
> +		case V4L2_CID_SATURATION:
> +			id = &controls::Saturation;
> +			info = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };
> +			break;
> +		default:
> +			continue;
> +		}
> +
> +		ctrls.emplace(id, info);
> +	}
> +
> +	controlInfo_ = std::move(ctrls);
> +
> +	return 0;
> +}
> +
> +void VividCameraData::bufferReady(FrameBuffer *buffer)
> +{
> +	Request *request = buffer->request();
> +
> +	pipe_->completeBuffer(camera_, request, buffer);
> +	pipe_->completeRequest(camera_, request);
> +}
> +
> +REGISTER_PIPELINE_HANDLER(PipelineHandlerVivid);
> +
> +} /* namespace libcamera */
> 

-- 
Regards
--
Kieran


More information about the libcamera-devel mailing list