mirror of
https://git.libcamera.org/libcamera/libcamera.git
synced 2025-07-13 15:29:45 +03:00
libcamera: Add V4L2Subdevice
Add V4L2Subdevice class that provides an interface to interact with V4L2 defined sub-devices. Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
parent
8dbc203bb2
commit
468176fa07
3 changed files with 317 additions and 0 deletions
51
src/libcamera/include/v4l2_subdevice.h
Normal file
51
src/libcamera/include/v4l2_subdevice.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* Copyright (C) 2019, Google Inc.
|
||||
*
|
||||
* v4l2_subdevice.h - V4L2 Subdevice
|
||||
*/
|
||||
#ifndef __LIBCAMERA_V4L2_SUBDEVICE_H__
|
||||
#define __LIBCAMERA_V4L2_SUBDEVICE_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
struct Rectangle;
|
||||
|
||||
struct V4L2SubdeviceFormat {
|
||||
uint32_t mbus_code;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
};
|
||||
|
||||
class V4L2Subdevice
|
||||
{
|
||||
public:
|
||||
explicit V4L2Subdevice(const MediaEntity *entity);
|
||||
V4L2Subdevice(const V4L2Subdevice &) = delete;
|
||||
V4L2Subdevice &operator=(const V4L2Subdevice &) = delete;
|
||||
|
||||
int open();
|
||||
bool isOpen() const;
|
||||
void close();
|
||||
|
||||
std::string deviceNode() const { return deviceNode_; }
|
||||
|
||||
int setCrop(unsigned int pad, Rectangle *rect);
|
||||
int setCompose(unsigned int pad, Rectangle *rect);
|
||||
|
||||
int getFormat(unsigned int pad, V4L2SubdeviceFormat *format);
|
||||
int setFormat(unsigned int pad, V4L2SubdeviceFormat *format);
|
||||
|
||||
private:
|
||||
int setSelection(unsigned int pad, unsigned int target,
|
||||
Rectangle *rect);
|
||||
|
||||
std::string deviceNode_;
|
||||
int fd_;
|
||||
};
|
||||
|
||||
} /* namespace libcamera */
|
||||
|
||||
#endif /* __LIBCAMERA_V4L2_SUBDEVICE_H__ */
|
|
@ -15,6 +15,7 @@ libcamera_sources = files([
|
|||
'stream.cpp',
|
||||
'timer.cpp',
|
||||
'v4l2_device.cpp',
|
||||
'v4l2_subdevice.cpp',
|
||||
])
|
||||
|
||||
libcamera_headers = files([
|
||||
|
|
265
src/libcamera/v4l2_subdevice.cpp
Normal file
265
src/libcamera/v4l2_subdevice.cpp
Normal file
|
@ -0,0 +1,265 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* Copyright (C) 2019, Google Inc.
|
||||
*
|
||||
* v4l2_subdevice.cpp - V4L2 Subdevice
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/v4l2-subdev.h>
|
||||
|
||||
#include "geometry.h"
|
||||
#include "log.h"
|
||||
#include "media_object.h"
|
||||
#include "v4l2_subdevice.h"
|
||||
|
||||
/**
|
||||
* \file v4l2_subdevice.h
|
||||
* \brief V4L2 Subdevice API
|
||||
*/
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
LOG_DEFINE_CATEGORY(V4L2Subdev)
|
||||
|
||||
/**
|
||||
* \struct V4L2SubdeviceFormat
|
||||
* \brief The V4L2 sub-device image format and sizes
|
||||
*
|
||||
* This structure describes the format of images when transported between
|
||||
* separate components connected through a physical bus, such as image sensor
|
||||
* and image receiver or between components part of the same System-on-Chip that
|
||||
* realize an image transformation pipeline.
|
||||
*
|
||||
* The format of images when transported on physical interconnections is known
|
||||
* as the "media bus format", and it is identified by a resolution and a pixel
|
||||
* format identification code, known as the "media bus code", not to be confused
|
||||
* with the fourcc code that identify the format of images when stored in memory
|
||||
* (see V4L2Device::V4L2DeviceFormat).
|
||||
*
|
||||
* Media Bus formats supported by the V4L2 APIs are described in Section
|
||||
* 4.15.3.4.1 of the "Part I - Video for Linux API" chapter of the "Linux Media
|
||||
* Infrastructure userspace API", part of the Linux kernel documentation.
|
||||
*
|
||||
* Image media bus formats are properties of the subdev pads. When images are
|
||||
* transported between two media pads identified by a 0-indexed number, the
|
||||
* image bus format configured on the two pads should match (according to the
|
||||
* underlying driver format matching criteria) in order to prepare for a
|
||||
* successful streaming operation. For a more detailed description of the image
|
||||
* format negotiation process when performed between V4L2 subdevices, refer to
|
||||
* Section 4.15.3.1 of the above mentioned Linux kernel documentation section.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \var V4L2SubdeviceFormat::width
|
||||
* \brief The image width in pixels
|
||||
*/
|
||||
|
||||
/**
|
||||
* \var V4L2SubdeviceFormat::height
|
||||
* \brief The image height in pixels
|
||||
*/
|
||||
|
||||
/**
|
||||
* \var V4L2SubdeviceFormat::mbus_code
|
||||
* \brief The image format bus code
|
||||
*/
|
||||
|
||||
/**
|
||||
* \class V4L2Subdevice
|
||||
* \brief A V4L2 subdevice as exposed by the Linux kernel
|
||||
*
|
||||
* The V4L2Subdevice class provides an API to the "Sub-device interface" as
|
||||
* described in section 4.15 of the "Linux Media Infrastructure userspace API"
|
||||
* chapter of the Linux Kernel documentation.
|
||||
*
|
||||
* A V4L2Subdevice is constructed from a MediaEntity instance, using the system
|
||||
* path of the entity's device node. No API call other than open(), isOpen()
|
||||
* and close() shall be called on an unopened device instance. Upon destruction
|
||||
* any device left open will be closed, and any resources released.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Create a V4L2 subdevice from a MediaEntity using its device node
|
||||
* path
|
||||
*/
|
||||
V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)
|
||||
: deviceNode_(entity->deviceNode()), fd_(-1)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Open a V4L2 subdevice
|
||||
*
|
||||
* \return 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int V4L2Subdevice::open()
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (isOpen()) {
|
||||
LOG(V4L2Subdev, Error) << "Device already open";
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = ::open(deviceNode_.c_str(), O_RDWR);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
LOG(V4L2Subdev, Error)
|
||||
<< "Failed to open V4L2 subdevice '" << deviceNode_
|
||||
<< "': " << strerror(-ret);
|
||||
return ret;
|
||||
}
|
||||
fd_ = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if the subdevice is open
|
||||
* \return True if the subdevice is open, false otherwise
|
||||
*/
|
||||
bool V4L2Subdevice::isOpen() const
|
||||
{
|
||||
return fd_ != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Close the subdevice, releasing any resources acquired by open()
|
||||
*/
|
||||
void V4L2Subdevice::close()
|
||||
{
|
||||
if (!isOpen())
|
||||
return;
|
||||
|
||||
::close(fd_);
|
||||
fd_ = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \fn V4L2Subdevice::deviceNode()
|
||||
* \brief Retrieve the path of the device node associated with the subdevice
|
||||
*
|
||||
* \return The subdevice's device node system path
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Set a crop rectangle on one of the V4L2 subdevice pads
|
||||
* \param[in] pad The 0-indexed pad number the rectangle is to be applied to
|
||||
* \param[inout] rect The rectangle describing crop target area
|
||||
*
|
||||
* \return 0 on success, or a negative error code otherwise
|
||||
*/
|
||||
int V4L2Subdevice::setCrop(unsigned int pad, Rectangle *rect)
|
||||
{
|
||||
return setSelection(pad, V4L2_SEL_TGT_CROP, rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set a compose rectangle on one of the V4L2 subdevice pads
|
||||
* \param[in] pad The 0-indexed pad number the rectangle is to be applied to
|
||||
* \param[inout] rect The rectangle describing the compose target area
|
||||
*
|
||||
* \return 0 on success, or a negative error code otherwise
|
||||
*/
|
||||
int V4L2Subdevice::setCompose(unsigned int pad, Rectangle *rect)
|
||||
{
|
||||
return setSelection(pad, V4L2_SEL_TGT_COMPOSE, rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Retrieve the image format set on one of the V4L2 subdevice pads
|
||||
* \param[in] pad The 0-indexed pad number the format is to be retrieved from
|
||||
* \param[out] format The image bus format
|
||||
*
|
||||
* \return 0 for success, a negative error code otherwise
|
||||
*/
|
||||
int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)
|
||||
{
|
||||
struct v4l2_subdev_format subdevFmt = {};
|
||||
subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
||||
subdevFmt.pad = pad;
|
||||
|
||||
int ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
LOG(V4L2Subdev, Error)
|
||||
<< "Unable to get format on pad " << pad
|
||||
<< " of " << deviceNode_ << ": " << strerror(-ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
format->width = subdevFmt.format.width;
|
||||
format->height = subdevFmt.format.height;
|
||||
format->mbus_code = subdevFmt.format.code;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set an image format on one of the V4L2 subdevice pads
|
||||
* \param[in] pad The 0-indexed pad number the format is to be applied to
|
||||
* \param[inout] format The image bus format to apply to the subdevice's pad
|
||||
*
|
||||
* Apply the requested image format to the desired media pad and return the
|
||||
* actually applied format parameters, as \ref V4L2Subdevice::getFormat would
|
||||
* do.
|
||||
*
|
||||
* \return 0 for success, a negative error code otherwise
|
||||
*/
|
||||
int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
|
||||
{
|
||||
struct v4l2_subdev_format subdevFmt = {};
|
||||
subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
||||
subdevFmt.pad = pad;
|
||||
subdevFmt.format.width = format->width;
|
||||
subdevFmt.format.height = format->height;
|
||||
subdevFmt.format.code = format->mbus_code;
|
||||
|
||||
int ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
LOG(Error) << "Unable to set format: " << strerror(-ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
format->width = subdevFmt.format.width;
|
||||
format->height = subdevFmt.format.height;
|
||||
format->mbus_code = subdevFmt.format.code;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
|
||||
Rectangle *rect)
|
||||
{
|
||||
struct v4l2_subdev_selection sel = {};
|
||||
|
||||
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
||||
sel.pad = pad;
|
||||
sel.target = target;
|
||||
sel.flags = 0;
|
||||
|
||||
sel.r.left = rect->y;
|
||||
sel.r.top = rect->x;
|
||||
sel.r.width = rect->w;
|
||||
sel.r.height = rect->h;
|
||||
|
||||
int ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, sel);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
LOG(V4L2Subdev, Error)
|
||||
<< "Unable to set rectangle " << target << " on pad "
|
||||
<< pad << " of " << deviceNode_ << ": "
|
||||
<< strerror(-ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* namespace libcamera */
|
Loading…
Add table
Add a link
Reference in a new issue