summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libcamera/camera.h17
-rw-r--r--src/cam/main.cpp2
-rw-r--r--src/libcamera/camera.cpp80
-rw-r--r--src/libcamera/pipeline/ipu3/ipu3.cpp253
-rw-r--r--src/libcamera/pipeline/rkisp1/rkisp1.cpp150
-rw-r--r--src/libcamera/pipeline/uvcvideo.cpp51
-rw-r--r--src/libcamera/pipeline/vimc.cpp66
-rw-r--r--src/libcamera/pipeline_handler.cpp19
-rw-r--r--test/camera/capture.cpp7
-rw-r--r--test/camera/configuration_default.cpp4
-rw-r--r--test/camera/configuration_set.cpp7
11 files changed, 515 insertions, 141 deletions
diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
index a3a7289a..fb2f7ba3 100644
--- a/include/libcamera/camera.h
+++ b/include/libcamera/camera.h
@@ -25,14 +25,19 @@ class Request;
class CameraConfiguration
{
public:
+ enum Status {
+ Valid,
+ Adjusted,
+ Invalid,
+ };
+
using iterator = std::vector<StreamConfiguration>::iterator;
using const_iterator = std::vector<StreamConfiguration>::const_iterator;
- CameraConfiguration();
+ virtual ~CameraConfiguration();
void addConfiguration(const StreamConfiguration &cfg);
-
- bool isValid() const;
+ virtual Status validate() = 0;
StreamConfiguration &at(unsigned int index);
const StreamConfiguration &at(unsigned int index) const;
@@ -53,11 +58,13 @@ public:
bool empty() const;
std::size_t size() const;
-private:
+protected:
+ CameraConfiguration();
+
std::vector<StreamConfiguration> config_;
};
-class Camera final
+class Camera final : public std::enable_shared_from_this<Camera>
{
public:
static std::shared_ptr<Camera> create(PipelineHandler *pipe,
diff --git a/src/cam/main.cpp b/src/cam/main.cpp
index 535c2420..5ecd7e0e 100644
--- a/src/cam/main.cpp
+++ b/src/cam/main.cpp
@@ -116,7 +116,7 @@ static std::unique_ptr<CameraConfiguration> prepareCameraConfig()
}
std::unique_ptr<CameraConfiguration> config = camera->generateConfiguration(roles);
- if (!config || !config->isValid()) {
+ if (!config || config->size() != roles.size()) {
std::cerr << "Failed to get default stream configuration"
<< std::endl;
return nullptr;
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index e7931ce0..0f45ab7e 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -52,6 +52,28 @@ LOG_DECLARE_CATEGORY(Camera)
* operator[](int) returns a reference to the StreamConfiguration based on its
* insertion index. Accessing a stream configuration with an invalid index
* results in undefined behaviour.
+ *
+ * CameraConfiguration instances are retrieved from the camera with
+ * Camera::generateConfiguration(). Applications may then inspect the
+ * configuration, modify it, and possibly add new stream configuration entries
+ * with addConfiguration(). Once the camera configuration satisfies the
+ * application, it shall be validated by a call to validate(). The validation
+ * implements "try" semantics: it adjusts invalid configurations to the closest
+ * achievable parameters instead of rejecting them completely. Applications
+ * then decide whether to accept the modified configuration, or try again with
+ * a different set of parameters. Once the configuration is valid, it is passed
+ * to Camera::configure().
+ */
+
+/**
+ * \enum CameraConfiguration::Status
+ * \brief Validity of a camera configuration
+ * \var CameraConfiguration::Valid
+ * The configuration is fully valid
+ * \var CameraConfiguration::Adjusted
+ * The configuration has been adjusted to a valid configuration
+ * \var CameraConfiguration::Invalid
+ * The configuration is invalid and can't be adjusted automatically
*/
/**
@@ -73,6 +95,10 @@ CameraConfiguration::CameraConfiguration()
{
}
+CameraConfiguration::~CameraConfiguration()
+{
+}
+
/**
* \brief Add a stream configuration to the camera configuration
* \param[in] cfg The stream configuration
@@ -83,27 +109,31 @@ void CameraConfiguration::addConfiguration(const StreamConfiguration &cfg)
}
/**
- * \brief Check if the camera configuration is valid
+ * \fn CameraConfiguration::validate()
+ * \brief Validate and possibly adjust the camera configuration
+ *
+ * This method adjusts the camera configuration to the closest valid
+ * configuration and returns the validation status.
+ *
+ * \todo: Define exactly when to return each status code. Should stream
+ * parameters set to 0 by the caller be adjusted without returning Adjusted ?
+ * This would potentially be useful for applications but would get in the way
+ * in Camera::configure(). Do we need an extra status code to signal this ?
*
- * A camera configuration is deemed to be valid if it contains at least one
- * stream configuration and all stream configurations contain valid information.
- * Stream configurations are deemed to be valid if all fields are none zero.
+ * \todo: Handle validation of buffers count when refactoring the buffers API.
*
- * \return True if the configuration is valid
+ * \return A CameraConfiguration::Status value that describes the validation
+ * status.
+ * \retval CameraConfiguration::Invalid The configuration is invalid and can't
+ * be adjusted. This may only occur in extreme cases such as when the
+ * configuration is empty.
+ * \retval CameraConfigutation::Adjusted The configuration has been adjusted
+ * and is now valid. Parameters may have changed for any stream, and stream
+ * configurations may have been removed. The caller shall check the
+ * configuration carefully.
+ * \retval CameraConfiguration::Valid The configuration was already valid and
+ * hasn't been adjusted.
*/
-bool CameraConfiguration::isValid() const
-{
- if (empty())
- return false;
-
- for (const StreamConfiguration &cfg : config_) {
- if (cfg.size.width == 0 || cfg.size.height == 0 ||
- cfg.pixelFormat == 0 || cfg.bufferCount == 0)
- return false;
- }
-
- return true;
-}
/**
* \brief Retrieve a reference to a stream configuration
@@ -219,6 +249,11 @@ std::size_t CameraConfiguration::size() const
}
/**
+ * \var CameraConfiguration::config_
+ * \brief The vector of stream configurations
+ */
+
+/**
* \class Camera
* \brief Camera device
*
@@ -582,10 +617,9 @@ std::unique_ptr<CameraConfiguration> Camera::generateConfiguration(const StreamR
* The caller specifies which streams are to be involved and their configuration
* by populating \a config.
*
- * The easiest way to populate the array of config is to fetch an initial
- * configuration from the camera with generateConfiguration() and then change
- * the parameters to fit the caller's need and once all the streams parameters
- * are configured hand that over to configure() to actually setup the camera.
+ * The configuration is created by generateConfiguration(), and adjusted by the
+ * caller with CameraConfiguration::validate(). This method only accepts fully
+ * valid configurations and returns an error if \a config is not valid.
*
* Exclusive access to the camera shall be ensured by a call to acquire() prior
* to calling this function, otherwise an -EACCES error will be returned.
@@ -610,7 +644,7 @@ int Camera::configure(CameraConfiguration *config)
if (!stateBetween(CameraAcquired, CameraConfigured))
return -EACCES;
- if (!config->isValid()) {
+ if (config->validate() != CameraConfiguration::Valid) {
LOG(Camera, Error)
<< "Can't configure camera with invalid configuration";
return -EINVAL;
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 5b46fb68..05005c42 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -164,6 +164,33 @@ public:
IPU3Stream vfStream_;
};
+class IPU3CameraConfiguration : public CameraConfiguration
+{
+public:
+ IPU3CameraConfiguration(Camera *camera, IPU3CameraData *data);
+
+ Status validate() override;
+
+ const V4L2SubdeviceFormat &sensorFormat() { return sensorFormat_; }
+ const std::vector<const IPU3Stream *> &streams() { return streams_; }
+
+private:
+ static constexpr unsigned int IPU3_BUFFER_COUNT = 4;
+
+ void adjustStream(StreamConfiguration &cfg, bool scale);
+
+ /*
+ * The IPU3CameraData instance is guaranteed to be valid as long as the
+ * corresponding Camera instance is valid. In order to borrow a
+ * reference to the camera data, store a new reference to the camera.
+ */
+ std::shared_ptr<Camera> camera_;
+ const IPU3CameraData *data_;
+
+ V4L2SubdeviceFormat sensorFormat_;
+ std::vector<const IPU3Stream *> streams_;
+};
+
class PipelineHandlerIPU3 : public PipelineHandler
{
public:
@@ -186,8 +213,6 @@ public:
bool match(DeviceEnumerator *enumerator) override;
private:
- static constexpr unsigned int IPU3_BUFFER_COUNT = 4;
-
IPU3CameraData *cameraData(const Camera *camera)
{
return static_cast<IPU3CameraData *>(
@@ -202,6 +227,151 @@ private:
MediaDevice *imguMediaDev_;
};
+IPU3CameraConfiguration::IPU3CameraConfiguration(Camera *camera,
+ IPU3CameraData *data)
+ : CameraConfiguration()
+{
+ camera_ = camera->shared_from_this();
+ data_ = data;
+}
+
+void IPU3CameraConfiguration::adjustStream(StreamConfiguration &cfg, bool scale)
+{
+ /* The only pixel format the driver supports is NV12. */
+ cfg.pixelFormat = V4L2_PIX_FMT_NV12;
+
+ if (scale) {
+ /*
+ * Provide a suitable default that matches the sensor aspect
+ * ratio.
+ */
+ if (!cfg.size.width || !cfg.size.height) {
+ cfg.size.width = 1280;
+ cfg.size.height = 1280 * sensorFormat_.size.height
+ / sensorFormat_.size.width;
+ }
+
+ /*
+ * \todo: Clamp the size to the hardware bounds when we will
+ * figure them out.
+ *
+ * \todo: Handle the scaler (BDS) restrictions. The BDS can
+ * only scale with the same factor in both directions, and the
+ * scaling factor is limited to a multiple of 1/32. At the
+ * moment the ImgU driver hides these constraints by applying
+ * additional cropping, this should be fixed on the driver
+ * side, and cropping should be exposed to us.
+ */
+ } else {
+ /*
+ * \todo: Properly support cropping when the ImgU driver
+ * interface will be cleaned up.
+ */
+ cfg.size = sensorFormat_.size;
+ }
+
+ /*
+ * Clamp the size to match the ImgU alignment constraints. The width
+ * shall be a multiple of 8 pixels and the height a multiple of 4
+ * pixels.
+ */
+ if (cfg.size.width % 8 || cfg.size.height % 4) {
+ cfg.size.width &= ~7;
+ cfg.size.height &= ~3;
+ }
+
+ cfg.bufferCount = IPU3_BUFFER_COUNT;
+}
+
+CameraConfiguration::Status IPU3CameraConfiguration::validate()
+{
+ const CameraSensor *sensor = data_->cio2_.sensor_;
+ Status status = Valid;
+
+ if (config_.empty())
+ return Invalid;
+
+ /* Cap the number of entries to the available streams. */
+ if (config_.size() > 2) {
+ config_.resize(2);
+ status = Adjusted;
+ }
+
+ /*
+ * Select the sensor format by collecting the maximum width and height
+ * and picking the closest larger match, as the IPU3 can downscale
+ * only. If no resolution is requested for any stream, or if no sensor
+ * resolution is large enough, pick the largest one.
+ */
+ Size size = {};
+
+ for (const StreamConfiguration &cfg : config_) {
+ if (cfg.size.width > size.width)
+ size.width = cfg.size.width;
+ if (cfg.size.height > size.height)
+ size.height = cfg.size.height;
+ }
+
+ if (!size.width || !size.height)
+ size = sensor->resolution();
+
+ sensorFormat_ = sensor->getFormat({ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10 },
+ size);
+ if (!sensorFormat_.size.width || !sensorFormat_.size.height)
+ sensorFormat_.size = sensor->resolution();
+
+ /*
+ * Verify and update all configuration entries, and assign a stream to
+ * each of them. The viewfinder stream can scale, while the output
+ * stream can crop only, so select the output stream when the requested
+ * resolution is equal to the sensor resolution, and the viewfinder
+ * stream otherwise.
+ */
+ std::set<const IPU3Stream *> availableStreams = {
+ &data_->outStream_,
+ &data_->vfStream_,
+ };
+
+ streams_.clear();
+ streams_.reserve(config_.size());
+
+ for (unsigned int i = 0; i < config_.size(); ++i) {
+ StreamConfiguration &cfg = config_[i];
+ const unsigned int pixelFormat = cfg.pixelFormat;
+ const Size size = cfg.size;
+ const IPU3Stream *stream;
+
+ if (cfg.size == sensorFormat_.size)
+ stream = &data_->outStream_;
+ else
+ stream = &data_->vfStream_;
+
+ if (availableStreams.find(stream) == availableStreams.end())
+ stream = *availableStreams.begin();
+
+ LOG(IPU3, Debug)
+ << "Assigned '" << stream->name_ << "' to stream " << i;
+
+ bool scale = stream == &data_->vfStream_;
+ adjustStream(config_[i], scale);
+
+ if (cfg.pixelFormat != pixelFormat || cfg.size != size) {
+ LOG(IPU3, Debug)
+ << "Stream " << i << " configuration adjusted to "
+ << cfg.toString();
+ status = Adjusted;
+ }
+
+ streams_.push_back(stream);
+ availableStreams.erase(stream);
+ }
+
+ return status;
+}
+
PipelineHandlerIPU3::PipelineHandlerIPU3(CameraManager *manager)
: PipelineHandler(manager), cio2MediaDev_(nullptr), imguMediaDev_(nullptr)
{
@@ -211,12 +381,14 @@ CameraConfiguration *PipelineHandlerIPU3::generateConfiguration(Camera *camera,
const StreamRoles &roles)
{
IPU3CameraData *data = cameraData(camera);
- CameraConfiguration *config = new CameraConfiguration();
+ IPU3CameraConfiguration *config;
std::set<IPU3Stream *> streams = {
&data->outStream_,
&data->vfStream_,
};
+ config = new IPU3CameraConfiguration(camera, data);
+
for (const StreamRole role : roles) {
StreamConfiguration cfg = {};
IPU3Stream *stream = nullptr;
@@ -296,71 +468,25 @@ CameraConfiguration *PipelineHandlerIPU3::generateConfiguration(Camera *camera,
streams.erase(stream);
- cfg.pixelFormat = V4L2_PIX_FMT_NV12;
- cfg.bufferCount = IPU3_BUFFER_COUNT;
-
config->addConfiguration(cfg);
}
+ config->validate();
+
return config;
}
-int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *config)
+int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
{
+ IPU3CameraConfiguration *config =
+ static_cast<IPU3CameraConfiguration *>(c);
IPU3CameraData *data = cameraData(camera);
IPU3Stream *outStream = &data->outStream_;
IPU3Stream *vfStream = &data->vfStream_;
CIO2Device *cio2 = &data->cio2_;
ImgUDevice *imgu = data->imgu_;
- Size sensorSize = {};
int ret;
- outStream->active_ = false;
- vfStream->active_ = false;
- for (StreamConfiguration &cfg : *config) {
- /*
- * Pick a stream for the configuration entry.
- * \todo: This is a naive temporary implementation that will be
- * reworked when implementing camera configuration validation.
- */
- IPU3Stream *stream = vfStream->active_ ? outStream : vfStream;
-
- /*
- * Verify that the requested size respects the IPU3 alignment
- * requirements (the image width shall be a multiple of 8
- * pixels and its height a multiple of 4 pixels) and the camera
- * maximum sizes.
- *
- * \todo: Consider the BDS scaling factor requirements: "the
- * downscaling factor must be an integer value multiple of 1/32"
- */
- if (cfg.size.width % 8 || cfg.size.height % 4) {
- LOG(IPU3, Error)
- << "Invalid stream size: bad alignment";
- return -EINVAL;
- }
-
- const Size &resolution = cio2->sensor_->resolution();
- if (cfg.size.width > resolution.width ||
- cfg.size.height > resolution.height) {
- LOG(IPU3, Error)
- << "Invalid stream size: larger than sensor resolution";
- return -EINVAL;
- }
-
- /*
- * Collect the maximum width and height: IPU3 can downscale
- * only.
- */
- if (cfg.size.width > sensorSize.width)
- sensorSize.width = cfg.size.width;
- if (cfg.size.height > sensorSize.height)
- sensorSize.height = cfg.size.height;
-
- stream->active_ = true;
- cfg.setStream(stream);
- }
-
/*
* \todo: Enable links selectively based on the requested streams.
* As of now, enable all links unconditionally.
@@ -373,6 +499,7 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *config)
* Pass the requested stream size to the CIO2 unit and get back the
* adjusted format to be propagated to the ImgU output devices.
*/
+ const Size &sensorSize = config->sensorFormat().size;
V4L2DeviceFormat cio2Format = {};
ret = cio2->configure(sensorSize, &cio2Format);
if (ret)
@@ -383,8 +510,22 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *config)
return ret;
/* Apply the format to the configured streams output devices. */
- for (StreamConfiguration &cfg : *config) {
- IPU3Stream *stream = static_cast<IPU3Stream *>(cfg.stream());
+ outStream->active_ = false;
+ vfStream->active_ = false;
+
+ for (unsigned int i = 0; i < config->size(); ++i) {
+ /*
+ * Use a const_cast<> here instead of storing a mutable stream
+ * pointer in the configuration to let the compiler catch
+ * unwanted modifications of camera data in the configuration
+ * validate() implementation.
+ */
+ IPU3Stream *stream = const_cast<IPU3Stream *>(config->streams()[i]);
+ StreamConfiguration &cfg = (*config)[i];
+
+ stream->active_ = true;
+ cfg.setStream(stream);
+
ret = imgu->configureOutput(stream->device_, cfg);
if (ret)
return ret;
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 8b279e76..9b3eea2f 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -5,6 +5,8 @@
* rkisp1.cpp - Pipeline handler for Rockchip ISP1
*/
+#include <algorithm>
+#include <array>
#include <iomanip>
#include <memory>
#include <vector>
@@ -45,6 +47,29 @@ public:
CameraSensor *sensor_;
};
+class RkISP1CameraConfiguration : public CameraConfiguration
+{
+public:
+ RkISP1CameraConfiguration(Camera *camera, RkISP1CameraData *data);
+
+ Status validate() override;
+
+ const V4L2SubdeviceFormat &sensorFormat() { return sensorFormat_; }
+
+private:
+ static constexpr unsigned int RKISP1_BUFFER_COUNT = 4;
+
+ /*
+ * The RkISP1CameraData instance is guaranteed to be valid as long as the
+ * corresponding Camera instance is valid. In order to borrow a
+ * reference to the camera data, store a new reference to the camera.
+ */
+ std::shared_ptr<Camera> camera_;
+ const RkISP1CameraData *data_;
+
+ V4L2SubdeviceFormat sensorFormat_;
+};
+
class PipelineHandlerRkISP1 : public PipelineHandler
{
public:
@@ -68,8 +93,6 @@ public:
bool match(DeviceEnumerator *enumerator) override;
private:
- static constexpr unsigned int RKISP1_BUFFER_COUNT = 4;
-
RkISP1CameraData *cameraData(const Camera *camera)
{
return static_cast<RkISP1CameraData *>(
@@ -88,6 +111,95 @@ private:
Camera *activeCamera_;
};
+RkISP1CameraConfiguration::RkISP1CameraConfiguration(Camera *camera,
+ RkISP1CameraData *data)
+ : CameraConfiguration()
+{
+ camera_ = camera->shared_from_this();
+ data_ = data;
+}
+
+CameraConfiguration::Status RkISP1CameraConfiguration::validate()
+{
+ static const std::array<unsigned int, 8> formats{
+ V4L2_PIX_FMT_YUYV,
+ V4L2_PIX_FMT_YVYU,
+ V4L2_PIX_FMT_VYUY,
+ V4L2_PIX_FMT_NV16,
+ V4L2_PIX_FMT_NV61,
+ V4L2_PIX_FMT_NV21,
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_GREY,
+ };
+
+ const CameraSensor *sensor = data_->sensor_;
+ 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. */
+ if (std::find(formats.begin(), formats.end(), cfg.pixelFormat) ==
+ formats.end()) {
+ LOG(RkISP1, Debug) << "Adjusting format to NV12";
+ cfg.pixelFormat = V4L2_PIX_FMT_NV12;
+ status = Adjusted;
+ }
+
+ /* Select the sensor format. */
+ sensorFormat_ = sensor->getFormat({ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8 },
+ cfg.size);
+ if (!sensorFormat_.size.width || !sensorFormat_.size.height)
+ sensorFormat_.size = sensor->resolution();
+
+ /*
+ * Provide a suitable default that matches the sensor aspect
+ * ratio and clamp the size to the hardware bounds.
+ *
+ * \todo: Check the hardware alignment constraints.
+ */
+ const Size size = cfg.size;
+
+ if (!cfg.size.width || !cfg.size.height) {
+ cfg.size.width = 1280;
+ cfg.size.height = 1280 * sensorFormat_.size.height
+ / sensorFormat_.size.width;
+ }
+
+ cfg.size.width = std::max(32U, std::min(4416U, cfg.size.width));
+ cfg.size.height = std::max(16U, std::min(3312U, cfg.size.height));
+
+ if (cfg.size != size) {
+ LOG(RkISP1, Debug)
+ << "Adjusting size from " << size.toString()
+ << " to " << cfg.size.toString();
+ status = Adjusted;
+ }
+
+ cfg.bufferCount = RKISP1_BUFFER_COUNT;
+
+ return status;
+}
+
PipelineHandlerRkISP1::PipelineHandlerRkISP1(CameraManager *manager)
: PipelineHandler(manager), dphy_(nullptr), isp_(nullptr),
video_(nullptr)
@@ -109,7 +221,7 @@ CameraConfiguration *PipelineHandlerRkISP1::generateConfiguration(Camera *camera
const StreamRoles &roles)
{
RkISP1CameraData *data = cameraData(camera);
- CameraConfiguration *config = new CameraConfiguration();
+ CameraConfiguration *config = new RkISP1CameraConfiguration(camera, data);
if (roles.empty())
return config;
@@ -117,29 +229,23 @@ CameraConfiguration *PipelineHandlerRkISP1::generateConfiguration(Camera *camera
StreamConfiguration cfg{};
cfg.pixelFormat = V4L2_PIX_FMT_NV12;
cfg.size = data->sensor_->resolution();
- cfg.bufferCount = RKISP1_BUFFER_COUNT;
config->addConfiguration(cfg);
+ config->validate();
+
return config;
}
-int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *config)
+int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
{
+ RkISP1CameraConfiguration *config =
+ static_cast<RkISP1CameraConfiguration *>(c);
RkISP1CameraData *data = cameraData(camera);
StreamConfiguration &cfg = config->at(0);
CameraSensor *sensor = data->sensor_;
int ret;
- /* Verify the configuration. */
- const Size &resolution = sensor->resolution();
- if (cfg.size.width > resolution.width ||
- cfg.size.height > resolution.height) {
- LOG(RkISP1, Error)
- << "Invalid stream size: larger than sensor resolution";
- return -EINVAL;
- }
-
/*
* Configure the sensor links: enable the link corresponding to this
* camera and disable all the other sensor links.
@@ -167,21 +273,7 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *config
* Configure the format on the sensor output and propagate it through
* the pipeline.
*/
- V4L2SubdeviceFormat format;
- format = sensor->getFormat({ MEDIA_BUS_FMT_SBGGR12_1X12,
- MEDIA_BUS_FMT_SGBRG12_1X12,
- MEDIA_BUS_FMT_SGRBG12_1X12,
- MEDIA_BUS_FMT_SRGGB12_1X12,
- MEDIA_BUS_FMT_SBGGR10_1X10,
- MEDIA_BUS_FMT_SGBRG10_1X10,
- MEDIA_BUS_FMT_SGRBG10_1X10,
- MEDIA_BUS_FMT_SRGGB10_1X10,
- MEDIA_BUS_FMT_SBGGR8_1X8,
- MEDIA_BUS_FMT_SGBRG8_1X8,
- MEDIA_BUS_FMT_SGRBG8_1X8,
- MEDIA_BUS_FMT_SRGGB8_1X8 },
- cfg.size);
-
+ V4L2SubdeviceFormat format = config->sensorFormat();
LOG(RkISP1, Debug) << "Configuring sensor with " << format.toString();
ret = sensor->setFormat(&format);
diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp
index 4ffe52aa..45260f34 100644
--- a/src/libcamera/pipeline/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo.cpp
@@ -39,6 +39,14 @@ public:
Stream stream_;
};
+class UVCCameraConfiguration : public CameraConfiguration
+{
+public:
+ UVCCameraConfiguration();
+
+ Status validate() override;
+};
+
class PipelineHandlerUVC : public PipelineHandler
{
public:
@@ -68,6 +76,45 @@ private:
}
};
+UVCCameraConfiguration::UVCCameraConfiguration()
+ : CameraConfiguration()
+{
+}
+
+CameraConfiguration::Status UVCCameraConfiguration::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];
+
+ /* \todo: Validate the configuration against the device capabilities. */
+ const unsigned int pixelFormat = cfg.pixelFormat;
+ const Size size = cfg.size;
+
+ cfg.pixelFormat = V4L2_PIX_FMT_YUYV;
+ cfg.size = { 640, 480 };
+
+ if (cfg.pixelFormat != pixelFormat || cfg.size != size) {
+ LOG(UVC, Debug)
+ << "Adjusting configuration from " << cfg.toString()
+ << " to " << cfg.size.toString() << "-YUYV";
+ status = Adjusted;
+ }
+
+ cfg.bufferCount = 4;
+
+ return status;
+}
+
PipelineHandlerUVC::PipelineHandlerUVC(CameraManager *manager)
: PipelineHandler(manager)
{
@@ -76,7 +123,7 @@ PipelineHandlerUVC::PipelineHandlerUVC(CameraManager *manager)
CameraConfiguration *PipelineHandlerUVC::generateConfiguration(Camera *camera,
const StreamRoles &roles)
{
- CameraConfiguration *config = new CameraConfiguration();
+ CameraConfiguration *config = new UVCCameraConfiguration();
if (roles.empty())
return config;
@@ -88,6 +135,8 @@ CameraConfiguration *PipelineHandlerUVC::generateConfiguration(Camera *camera,
config->addConfiguration(cfg);
+ config->validate();
+
return config;
}
diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp
index ed5b1ad4..0e4eede3 100644
--- a/src/libcamera/pipeline/vimc.cpp
+++ b/src/libcamera/pipeline/vimc.cpp
@@ -5,6 +5,9 @@
* vimc.cpp - Pipeline handler for the vimc device
*/
+#include <algorithm>
+#include <array>
+
#include <libcamera/camera.h>
#include <libcamera/request.h>
#include <libcamera/stream.h>
@@ -39,6 +42,14 @@ public:
Stream stream_;
};
+class VimcCameraConfiguration : public CameraConfiguration
+{
+public:
+ VimcCameraConfiguration();
+
+ Status validate() override;
+};
+
class PipelineHandlerVimc : public PipelineHandler
{
public:
@@ -68,6 +79,57 @@ private:
}
};
+VimcCameraConfiguration::VimcCameraConfiguration()
+ : CameraConfiguration()
+{
+}
+
+CameraConfiguration::Status VimcCameraConfiguration::validate()
+{
+ static const std::array<unsigned int, 3> formats{
+ V4L2_PIX_FMT_BGR24,
+ V4L2_PIX_FMT_RGB24,
+ V4L2_PIX_FMT_ARGB32,
+ };
+
+ 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. */
+ if (std::find(formats.begin(), formats.end(), cfg.pixelFormat) ==
+ formats.end()) {
+ LOG(VIMC, Debug) << "Adjusting format to RGB24";
+ cfg.pixelFormat = V4L2_PIX_FMT_RGB24;
+ status = Adjusted;
+ }
+
+ /* Clamp the size based on the device limits. */
+ const Size size = cfg.size;
+
+ cfg.size.width = std::max(16U, std::min(4096U, cfg.size.width));
+ cfg.size.height = std::max(16U, std::min(2160U, cfg.size.height));
+
+ if (cfg.size != size) {
+ LOG(VIMC, Debug)
+ << "Adjusting size to " << cfg.size.toString();
+ status = Adjusted;
+ }
+
+ cfg.bufferCount = 4;
+
+ return status;
+}
+
PipelineHandlerVimc::PipelineHandlerVimc(CameraManager *manager)
: PipelineHandler(manager)
{
@@ -76,7 +138,7 @@ PipelineHandlerVimc::PipelineHandlerVimc(CameraManager *manager)
CameraConfiguration *PipelineHandlerVimc::generateConfiguration(Camera *camera,
const StreamRoles &roles)
{
- CameraConfiguration *config = new CameraConfiguration();
+ CameraConfiguration *config = new VimcCameraConfiguration();
if (roles.empty())
return config;
@@ -88,6 +150,8 @@ CameraConfiguration *PipelineHandlerVimc::generateConfiguration(Camera *camera,
config->addConfiguration(cfg);
+ config->validate();
+
return config;
}
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index de46e988..dd56907d 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -248,17 +248,14 @@ void PipelineHandler::unlock()
* is the Camera class which will receive configuration to apply from the
* application.
*
- * Each pipeline handler implementation is responsible for validating
- * that the configuration requested in \a config can be achieved
- * exactly. Any difference in pixel format, frame size or any other
- * parameter shall result in the -EINVAL error being returned, and no
- * change in configuration being applied to the pipeline. If
- * configuration of a subset of the streams can't be satisfied, the
- * whole configuration is considered invalid.
- *
- * Once the configuration is validated and the camera configured, the pipeline
- * handler shall associate a Stream instance to each StreamConfiguration entry
- * in the CameraConfiguration with the StreamConfiguration::setStream() method.
+ * The configuration is guaranteed to have been validated with
+ * CameraConfiguration::valid(). The pipeline handler implementation shall not
+ * perform further validation and may rely on any custom field stored in its
+ * custom CameraConfiguration derived class.
+ *
+ * When configuring the camera the pipeline handler shall associate a Stream
+ * instance to each StreamConfiguration entry in the CameraConfiguration using
+ * the StreamConfiguration::setStream() method.
*
* \return 0 on success or a negative error code otherwise
*/
diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp
index bb7d380c..c0835c25 100644
--- a/test/camera/capture.cpp
+++ b/test/camera/capture.cpp
@@ -45,7 +45,7 @@ protected:
CameraTest::init();
config_ = camera_->generateConfiguration({ StreamRole::VideoRecording });
- if (!config_) {
+ if (!config_ || config_->size() != 1) {
cout << "Failed to generate default configuration" << endl;
CameraTest::cleanup();
return TestFail;
@@ -58,11 +58,6 @@ protected:
{
StreamConfiguration &cfg = config_->at(0);
- if (!config_->isValid()) {
- cout << "Failed to read default configuration" << endl;
- return TestFail;
- }
-
if (camera_->acquire()) {
cout << "Failed to acquire the camera" << endl;
return TestFail;
diff --git a/test/camera/configuration_default.cpp b/test/camera/configuration_default.cpp
index 8a767d23..ce2ec5d0 100644
--- a/test/camera/configuration_default.cpp
+++ b/test/camera/configuration_default.cpp
@@ -22,7 +22,7 @@ protected:
/* Test asking for configuration for a video stream. */
config = camera_->generateConfiguration({ StreamRole::VideoRecording });
- if (!config || !config->isValid()) {
+ if (!config || config->size() != 1) {
cout << "Default configuration invalid" << endl;
return TestFail;
}
@@ -32,7 +32,7 @@ protected:
* stream roles returns an empty camera configuration.
*/
config = camera_->generateConfiguration({});
- if (!config || config->isValid()) {
+ if (!config || config->size() != 0) {
cout << "Failed to retrieve configuration for empty roles list"
<< endl;
return TestFail;
diff --git a/test/camera/configuration_set.cpp b/test/camera/configuration_set.cpp
index ece987c7..9f10f795 100644
--- a/test/camera/configuration_set.cpp
+++ b/test/camera/configuration_set.cpp
@@ -21,7 +21,7 @@ protected:
CameraTest::init();
config_ = camera_->generateConfiguration({ StreamRole::VideoRecording });
- if (!config_) {
+ if (!config_ || config_->size() != 1) {
cout << "Failed to generate default configuration" << endl;
CameraTest::cleanup();
return TestFail;
@@ -34,11 +34,6 @@ protected:
{
StreamConfiguration &cfg = config_->at(0);
- if (!config_->isValid()) {
- cout << "Failed to read default configuration" << endl;
- return TestFail;
- }
-
if (camera_->acquire()) {
cout << "Failed to acquire the camera" << endl;
return TestFail;