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

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


On 19/06/2020 13:10, Kieran Bingham wrote:
> 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.

Perhaps an alternative route to supporting this however would be to make
the simple pipeline handler support pipelines which *don't* have a
sensor object... though the VIVID controls could be useful to support
directly.

Perhaps - inheriting the simple-pipeline handler should be possible, to
provide some 'device specific' adjustments for things like device
specific controls.


>> 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