[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