Files
external_libcamera/src/libcamera/pipeline/virtual/virtual.cpp
T
Barnabás Pőcze 9c29274382 libcamera: pipeline: virtual: Simplify error return
Just return an `std::unique_ptr` constructed from an empty
initializer instead of doing a `reset()` on the existing
`config` variable and returning that. This is simpler.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-04 18:32:23 +01:00

404 lines
10 KiB
C++

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2024, Google Inc.
*
* Pipeline handler for virtual cameras
*/
#include "virtual.h"
#include <algorithm>
#include <array>
#include <chrono>
#include <errno.h>
#include <map>
#include <memory>
#include <ostream>
#include <set>
#include <stdint.h>
#include <string>
#include <time.h>
#include <utility>
#include <vector>
#include <libcamera/base/flags.h>
#include <libcamera/base/log.h>
#include <libcamera/control_ids.h>
#include <libcamera/controls.h>
#include <libcamera/formats.h>
#include <libcamera/pixel_format.h>
#include <libcamera/property_ids.h>
#include <libcamera/request.h>
#include "libcamera/internal/camera.h"
#include "libcamera/internal/dma_buf_allocator.h"
#include "libcamera/internal/formats.h"
#include "libcamera/internal/framebuffer.h"
#include "libcamera/internal/pipeline_handler.h"
#include "libcamera/internal/yaml_parser.h"
#include "pipeline/virtual/config_parser.h"
namespace libcamera {
LOG_DEFINE_CATEGORY(Virtual)
namespace {
uint64_t currentTimestamp()
{
const auto now = std::chrono::steady_clock::now();
auto nsecs = std::chrono::duration_cast<std::chrono::nanoseconds>(
now.time_since_epoch());
return nsecs.count();
}
} /* namespace */
template<class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
class VirtualCameraConfiguration : public CameraConfiguration
{
public:
static constexpr unsigned int kBufferCount = 4;
VirtualCameraConfiguration(VirtualCameraData *data);
Status validate() override;
private:
const VirtualCameraData *data_;
};
class PipelineHandlerVirtual : public PipelineHandler
{
public:
PipelineHandlerVirtual(CameraManager *manager);
~PipelineHandlerVirtual();
std::unique_ptr<CameraConfiguration> generateConfiguration(Camera *camera,
Span<const StreamRole> 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, const ControlList *controls) override;
void stopDevice(Camera *camera) override;
int queueRequestDevice(Camera *camera, Request *request) override;
bool match(DeviceEnumerator *enumerator) override;
private:
static bool created_;
VirtualCameraData *cameraData(Camera *camera)
{
return static_cast<VirtualCameraData *>(camera->_d());
}
bool initFrameGenerator(Camera *camera);
DmaBufAllocator dmaBufAllocator_;
bool resetCreated_ = false;
};
VirtualCameraData::VirtualCameraData(PipelineHandler *pipe,
const std::vector<Resolution> &supportedResolutions)
: Camera::Private(pipe)
{
config_.resolutions = supportedResolutions;
for (const auto &resolution : config_.resolutions) {
if (config_.minResolutionSize.isNull() || config_.minResolutionSize > resolution.size)
config_.minResolutionSize = resolution.size;
config_.maxResolutionSize = std::max(config_.maxResolutionSize, resolution.size);
}
properties_.set(properties::PixelArrayActiveAreas,
{ Rectangle(config_.maxResolutionSize) });
/* \todo Support multiple streams and pass multi_stream_test */
streamConfigs_.resize(kMaxStream);
}
VirtualCameraConfiguration::VirtualCameraConfiguration(VirtualCameraData *data)
: CameraConfiguration(), data_(data)
{
}
CameraConfiguration::Status VirtualCameraConfiguration::validate()
{
Status status = Valid;
if (config_.empty()) {
LOG(Virtual, Error) << "Empty config";
return Invalid;
}
/* Only one stream is supported */
if (config_.size() > VirtualCameraData::kMaxStream) {
config_.resize(VirtualCameraData::kMaxStream);
status = Adjusted;
}
for (StreamConfiguration &cfg : config_) {
bool adjusted = false;
bool found = false;
for (const auto &resolution : data_->config_.resolutions) {
if (resolution.size.width == cfg.size.width &&
resolution.size.height == cfg.size.height) {
found = true;
break;
}
}
if (!found) {
/*
* \todo It's a pipeline's decision to choose a
* resolution when the exact one is not supported.
* Defining the default logic in PipelineHandler to
* find the closest resolution would be nice.
*/
cfg.size = data_->config_.maxResolutionSize;
status = Adjusted;
adjusted = true;
}
if (cfg.pixelFormat != formats::NV12) {
cfg.pixelFormat = formats::NV12;
status = Adjusted;
adjusted = true;
}
if (adjusted)
LOG(Virtual, Info)
<< "Stream configuration adjusted to " << cfg.toString();
const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat);
cfg.stride = info.stride(cfg.size.width, 0, 1);
cfg.frameSize = info.frameSize(cfg.size, 1);
cfg.bufferCount = VirtualCameraConfiguration::kBufferCount;
}
return status;
}
/* static */
bool PipelineHandlerVirtual::created_ = false;
PipelineHandlerVirtual::PipelineHandlerVirtual(CameraManager *manager)
: PipelineHandler(manager),
dmaBufAllocator_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
{
}
PipelineHandlerVirtual::~PipelineHandlerVirtual()
{
if (resetCreated_)
created_ = false;
}
std::unique_ptr<CameraConfiguration>
PipelineHandlerVirtual::generateConfiguration(Camera *camera,
Span<const StreamRole> roles)
{
VirtualCameraData *data = cameraData(camera);
auto config = std::make_unique<VirtualCameraConfiguration>(data);
if (roles.empty())
return config;
for (const StreamRole role : roles) {
switch (role) {
case StreamRole::StillCapture:
case StreamRole::VideoRecording:
case StreamRole::Viewfinder:
break;
case StreamRole::Raw:
default:
LOG(Virtual, Error)
<< "Requested stream role not supported: " << role;
return {};
}
std::map<PixelFormat, std::vector<SizeRange>> streamFormats;
PixelFormat pixelFormat = formats::NV12;
streamFormats[pixelFormat] = { { data->config_.minResolutionSize,
data->config_.maxResolutionSize } };
StreamFormats formats(streamFormats);
StreamConfiguration cfg(formats);
cfg.pixelFormat = pixelFormat;
cfg.size = data->config_.maxResolutionSize;
cfg.bufferCount = VirtualCameraConfiguration::kBufferCount;
config->addConfiguration(cfg);
}
ASSERT(config->validate() != CameraConfiguration::Invalid);
return config;
}
int PipelineHandlerVirtual::configure(Camera *camera,
CameraConfiguration *config)
{
VirtualCameraData *data = cameraData(camera);
for (auto [i, c] : utils::enumerate(*config)) {
c.setStream(&data->streamConfigs_[i].stream);
/* Start reading the images/generating test patterns */
data->streamConfigs_[i].frameGenerator->configure(c.size);
}
return 0;
}
int PipelineHandlerVirtual::exportFrameBuffers([[maybe_unused]] Camera *camera,
Stream *stream,
std::vector<std::unique_ptr<FrameBuffer>> *buffers)
{
if (!dmaBufAllocator_.isValid())
return -ENOBUFS;
const StreamConfiguration &config = stream->configuration();
const PixelFormatInfo &info = PixelFormatInfo::info(config.pixelFormat);
std::vector<unsigned int> planeSizes;
for (size_t i = 0; i < info.numPlanes(); ++i)
planeSizes.push_back(info.planeSize(config.size, i));
return dmaBufAllocator_.exportBuffers(config.bufferCount, planeSizes, buffers);
}
int PipelineHandlerVirtual::start([[maybe_unused]] Camera *camera,
[[maybe_unused]] const ControlList *controls)
{
return 0;
}
void PipelineHandlerVirtual::stopDevice([[maybe_unused]] Camera *camera)
{
}
int PipelineHandlerVirtual::queueRequestDevice([[maybe_unused]] Camera *camera,
Request *request)
{
VirtualCameraData *data = cameraData(camera);
for (auto const &[stream, buffer] : request->buffers()) {
bool found = false;
/* map buffer and fill test patterns */
for (auto &streamConfig : data->streamConfigs_) {
if (stream == &streamConfig.stream) {
found = true;
if (streamConfig.frameGenerator->generateFrame(
stream->configuration().size, buffer))
buffer->_d()->cancel();
completeBuffer(request, buffer);
break;
}
}
ASSERT(found);
}
request->metadata().set(controls::SensorTimestamp, currentTimestamp());
completeRequest(request);
return 0;
}
bool PipelineHandlerVirtual::match([[maybe_unused]] DeviceEnumerator *enumerator)
{
if (created_)
return false;
created_ = true;
std::string configFile = configurationFile("virtual", "virtual.yaml", true);
if (configFile.empty()) {
LOG(Virtual, Debug)
<< "Configuration file not found, skipping virtual cameras";
return false;
}
File file(configFile);
if (!file.open(File::OpenModeFlag::ReadOnly)) {
LOG(Virtual, Error)
<< "Failed to open config file `" << file.fileName() << "`";
return false;
}
ConfigParser parser;
auto configData = parser.parseConfigFile(file, this);
if (configData.size() == 0) {
LOG(Virtual, Error) << "Failed to parse any cameras from the config file: "
<< file.fileName();
return false;
}
/* Configure and register cameras with configData */
for (auto &data : configData) {
std::set<Stream *> streams;
for (auto &streamConfig : data->streamConfigs_)
streams.insert(&streamConfig.stream);
std::string id = data->config_.id;
std::shared_ptr<Camera> camera = Camera::create(std::move(data), id, streams);
if (!initFrameGenerator(camera.get())) {
LOG(Virtual, Error) << "Failed to initialize frame "
<< "generator for camera: " << id;
continue;
}
registerCamera(std::move(camera));
}
resetCreated_ = true;
return true;
}
bool PipelineHandlerVirtual::initFrameGenerator(Camera *camera)
{
auto data = cameraData(camera);
auto &frame = data->config_.frame;
std::visit(overloaded{
[&](TestPattern &testPattern) {
for (auto &streamConfig : data->streamConfigs_) {
if (testPattern == TestPattern::DiagonalLines)
streamConfig.frameGenerator = std::make_unique<DiagonalLinesGenerator>();
else
streamConfig.frameGenerator = std::make_unique<ColorBarsGenerator>();
}
},
[&](ImageFrames &imageFrames) {
for (auto &streamConfig : data->streamConfigs_)
streamConfig.frameGenerator = ImageFrameGenerator::create(imageFrames);
} },
frame);
for (auto &streamConfig : data->streamConfigs_)
if (!streamConfig.frameGenerator)
return false;
return true;
}
REGISTER_PIPELINE_HANDLER(PipelineHandlerVirtual, "virtual")
} /* namespace libcamera */