[libcamera-devel] [PATCH 1/1] Add fake pipeline handler
Cheng-Hao Yang
chenghaoyang at chromium.org
Fri Oct 14 12:36:48 CEST 2022
Thanks Jacopo!
The patch v2 is uploaded based on your comments.
On Wed, Oct 12, 2022 at 11:06 PM Jacopo Mondi <jacopo at jmondi.org> wrote:
> Hi Harvey,
> I had the same thought as Kieran had, why not vimc ? I understan
> your reasons behind it and the requirement to backport patches for it
> to older kernel (btw, does it need anything more than CAP_IO_MC ?)
>
>
Is it a great effort to backport patches to older kernel versions?
Regarding CAP_IO_MC, I don't know :p. Anyone can help with this?
> I have some general questions
>
> 1) What does this pipeline handler matches on ? It seems to me match()
> always returns true, hence if the pipeline handler is compiled in, it
> will always report one camera available ?
>
>
I think it'll eventually depend on the configuration file provided by the
tester.
We might come up with some fake media devices, without matching the real
ones. But I haven't made up my mind yet.
> 2) What are the requirements for this pipeline handler ?
>
> Is it enough to test the Android layer to implement stub methods,
> or should frames be generated somehow (I'm thinking at CTS frame
> rate checks in example).
>
> Should it support multiple streams ? I guess it depends on the HW
> level one wants to test in CTS, or should it mimik the capabilities
> of a known device (ie IPU3 that can produce 2 YUV streams and one
> RAW)
>
> I guess however this can be started as a skeleton and be populated
> as required to complete CTS.
>
> Additional features like custom configurations (of cameras) and frames
from
a video file will be added.
In CrOS, it helps applications to test usages in different (fake) hardware
setup,
while it also helps libcamera test the Android adapter.
> 3) Should all mentions of IPU3 related components be removed ?
>
> Yes
> 4) Possible bikeshedding, but I wonder if "dummy" isn't better than
> "fake"
>
> As we need some more features, like a custom configuration file/cameras
and frames from a video file, I believe "fake" is still more appropriate.
> On Wed, Oct 12, 2022 at 07:59:25AM +0000, Harvey Yang via libcamera-devel
> wrote:
> > ---
> > meson_options.txt | 2 +-
> > src/android/camera_capabilities.cpp | 1 +
> > src/libcamera/pipeline/fake/fake.cpp | 518 ++++++++++++++++++++++++
> > src/libcamera/pipeline/fake/meson.build | 3 +
> > src/libcamera/pipeline_handler.cpp | 2 +-
> > test/camera/camera_reconfigure.cpp | 2 +-
> > 6 files changed, 525 insertions(+), 3 deletions(-)
> > create mode 100644 src/libcamera/pipeline/fake/fake.cpp
> > create mode 100644 src/libcamera/pipeline/fake/meson.build
> >
> > diff --git a/meson_options.txt b/meson_options.txt
> > index f1d67808..f08dfc5f 100644
> > --- a/meson_options.txt
> > +++ b/meson_options.txt
> > @@ -37,7 +37,7 @@ option('lc-compliance',
> >
> > option('pipelines',
> > type : 'array',
> > - choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple',
> 'uvcvideo', 'vimc'],
> > + choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple',
> 'uvcvideo', 'vimc', 'fake'],
> > description : 'Select which pipeline handlers to include')
> >
> > option('qcam',
> > diff --git a/src/android/camera_capabilities.cpp
> b/src/android/camera_capabilities.cpp
> > index 64bd8dde..730ceafc 100644
> > --- a/src/android/camera_capabilities.cpp
> > +++ b/src/android/camera_capabilities.cpp
> > @@ -1077,6 +1077,7 @@ int CameraCapabilities::initializeStaticMetadata()
> > {
> > const Span<const Rectangle> &rects =
> >
> properties.get(properties::PixelArrayActiveAreas).value_or(Span<const
> Rectangle>{});
> > + // TODO: initialize at least one Rectangle as default.
>
> Unrelated, please drop
>
>
Done.
> > std::vector<int32_t> data{
> > static_cast<int32_t>(rects[0].x),
> > static_cast<int32_t>(rects[0].y),
> > diff --git a/src/libcamera/pipeline/fake/fake.cpp
> b/src/libcamera/pipeline/fake/fake.cpp
> > new file mode 100644
> > index 00000000..518de6aa
> > --- /dev/null
> > +++ b/src/libcamera/pipeline/fake/fake.cpp
> > @@ -0,0 +1,518 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2019, Google Inc.
> > + *
> > + * ipu3.cpp - Pipeline handler for Intel Fake
> > + */
>
> Please update year and remove mentions of Intel ?
>
>
Done.
> > +
> > +#include <algorithm>
> > +#include <iomanip>
> > +#include <memory>
> > +#include <queue>
> > +#include <vector>
>
> Seems like you're also using std::set
>
>
Done.
> > +
> > +#include <libcamera/base/log.h>
> > +#include <libcamera/base/utils.h>
> > +
> > +#include <libcamera/camera.h>
>
> all internal headers "libcamera/internal/something.h" should include
> <libcamera/something.h>, so if you include internal/camera.h you
> probably can remove this
>
>
Done.
> > +#include <libcamera/control_ids.h>
> > +#include <libcamera/formats.h>
> > +#include <libcamera/property_ids.h>
> > +#include <libcamera/request.h>
> > +#include <libcamera/stream.h>
> > +
> > +#include "libcamera/internal/camera.h"
> > +#include "libcamera/internal/camera_lens.h"
> > +#include "libcamera/internal/camera_sensor.h"
> > +#include "libcamera/internal/delayed_controls.h"
>
> The three above are not used it seems
>
>
Done.
> > +#include "libcamera/internal/device_enumerator.h"
> > +#include "libcamera/internal/framebuffer.h"
> > +#include "libcamera/internal/media_device.h"
> > +#include "libcamera/internal/pipeline_handler.h"
> > +
> > +namespace libcamera {
> > +
> > +LOG_DEFINE_CATEGORY(Fake)
> > +
> > +uint64_t CurrentTimestamp()
> > +{
> > + struct timespec ts;
> > + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
> > + LOG(Fake, Error) << "Get clock time fails";
> > + return 0;
> > + }
> > +
> > + return ts.tv_sec * 1'000'000'000LL + ts.tv_nsec;
> > +}
> > +
> > +static const ControlInfoMap::Map FakeControls = {
>
> Missing #include <libcamera/controls.h> ?
>
>
Done.
> + { &controls::draft::PipelineDepth, ControlInfo(2, 3) },
> > +};
> > +
> > +class FakeCameraData : public Camera::Private
> > +{
> > +public:
> > + struct Resolution {
> > + Size size;
> > + std::vector<int> frame_rates;
> > + std::vector<std::string> formats;
>
> I would rather use libcamera::formats to verify the Android HAL does
> the translation right.
>
> I believe you mean libcamera::PixelFormat. Done.
> > + };
> > +
> > + FakeCameraData(PipelineHandler *pipe)
> > + : Camera::Private(pipe), supportsFlips_(false)
> > + {
> > + }
> > +
> > + std::vector<Resolution> supported_resolutions_;
>
> supportedResolutions_
>
> Done.
> > +
> > + Stream outStream_;
> > + Stream vfStream_;
> > + Stream rawStream_;
> > +
> > + // TODO: Check if we should support.
> > + bool supportsFlips_;
> > + Transform rotationTransform_;
>
> For sake of simplicity you can remoev flip/rotation support from the
> first skeleton
>
>
Done.
> > +
> > + // TODO: remove
>
> Please :)
>
> Done.
> > + /* Requests for which no buffer has been queued to the CIO2 device
> yet. */
> > + std::queue<Request *> pendingRequests_;
> > + /* Requests queued to the CIO2 device but not yet processed by the
> ImgU. */
> > + std::queue<Request *> processingRequests_;
> > +
> > + // ControlInfoMap ipaControls_;
>
> This as well
>
> Done.
> > + bool started_ = false;
> > +};
> > +
> > +class FakeCameraConfiguration : public CameraConfiguration
> > +{
> > +public:
> > + static constexpr unsigned int kBufferCount = 4; // 4~6
> > + static constexpr unsigned int kMaxStreams = 3;
> > +
> > + FakeCameraConfiguration(FakeCameraData *data);
> > +
> > + Status validate() override;
> > +
> > +private:
> > + /*
> > + * The FakeCameraData 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.
> > + */
>
> This comes from the IPU3 pipeline handler, and I wonder if it still
> applies there or it should be removed.
>
>
I believe it's still needed in |validate()| to set streams, right?
> > + const FakeCameraData *data_;
> > +};
> > +
> > +class PipelineHandlerFake : public PipelineHandler
> > +{
> > +public:
> > + static constexpr unsigned int V4L2_CID_Fake_PIPE_MODE = 0x009819c1;
>
> Unused, please drop
>
> Done.
> > + static constexpr Size kViewfinderSize{ 1280, 720 };
> > +
> > + enum FakePipeModes {
> > + FakePipeModeVideo = 0,
> > + FakePipeModeStillCapture = 1,
> > + };
>
> ditto
>
>
Done.
> > +
> > + PipelineHandlerFake(CameraManager *manager);
> > +
> > + CameraConfiguration *generateConfiguration(Camera *camera,
> > + const StreamRoles &roles) override;
>
> Align to open (
>
> Done.
> > + 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:
> > + FakeCameraData *cameraData(Camera *camera)
> > + {
> > + return static_cast<FakeCameraData *>(camera->_d());
> > + }
> > +
> > + int registerCameras();
> > +
> > + static bool registered_;
> > +};
> > +
> > +bool PipelineHandlerFake::registered_ = false;
>
> What purpose does this serve ? Do you get multiple calls to match()
> so that you need to keep a flag ?
>
> Yes, in `src/libcamera/camera_manager.cpp`, it intentionally calls match()
multiple times
until it returns false (which means no devices matched). Therefore, this is
the hack as
the fake media devices haven't been created yet.
> > +
> > +FakeCameraConfiguration::FakeCameraConfiguration(FakeCameraData *data)
> > + : CameraConfiguration()
> > +{
> > + data_ = data;
> > +}
> > +
> > +CameraConfiguration::Status FakeCameraConfiguration::validate()
> > +{
> > + Status status = Valid;
> > +
> > + if (config_.empty())
> > + return Invalid;
> > +
> > + // Transform combined = transform * data_->rotationTransform_;
> > +
> > + // /*
> > + // * We combine the platform and user transform, but must "adjust
> away"
> > + // * any combined result that includes a transposition, as we
> can't do
> > + // * those. In this case, flipping only the transpose bit is
> helpful to
> > + // * applications - they either get the transform they requested,
> or have
> > + // * to do a simple transpose themselves (they don't have to
> worry about
> > + // * the other possible cases).
> > + // */
> > + // if (!!(combined & Transform::Transpose)) {
> > + // /*
> > + // * Flipping the transpose bit in "transform" flips it in
> the
> > + // * combined result too (as it's the last thing that
> happens),
> > + // * which is of course clearing it.
> > + // */
> > + // transform ^= Transform::Transpose;
> > + // combined &= ~Transform::Transpose;
> > + // status = Adjusted;
> > + // }
> > +
> > + // /*
> > + // * We also check if the sensor doesn't do h/vflips at all, in
> which
> > + // * case we clear them, and the application will have to do
> everything.
> > + // */
> > + // if (!data_->supportsFlips_ && !!combined) {
> > + // /*
> > + // * If the sensor can do no transforms, then combined must
> be
> > + // * changed to the identity. The only user transform that
> gives
> > + // * rise to this is the inverse of the rotation. (Recall
> that
> > + // * combined = transform * rotationTransform.)
> > + // */
> > + // transform = -data_->rotationTransform_;
> > + // combined = Transform::Identity;
> > + // status = Adjusted;
> > + // }
> > +
> > + /*
> > + * Store the final combined transform that configure() will need to
> > + * apply to the sensor to save us working it out again.
> > + */
> > + // combinedTransform_ = combined;
>
> Please drop commented out code :0
>
> > +
> > + /* Cap the number of entries to the available streams. */
> > + if (config_.size() > kMaxStreams) {
> > + config_.resize(kMaxStreams);
> > + status = Adjusted;
> > + }
> > +
> > + /*
> > + * Validate the requested stream configuration and select the
> sensor
> > + * format by collecting the maximum RAW stream width and height and
> > + * picking the closest larger match.
> > + *
> > + * If no RAW stream is requested use the one of the largest YUV
> stream,
> > + * plus margin pixels for the IF and BDS rectangle to downscale.
> > + *
> > + * \todo Clarify the IF and BDS margins requirements.
>
> Please drop IPU3 related stuff
>
> Will do. I'll update the number of supported streams as well.
> > + */
> > + unsigned int rawCount = 0;
> > + unsigned int yuvCount = 0;
> > + Size rawRequirement;
> > + Size maxYuvSize;
> > + Size rawSize;
> > +
> > + for (const StreamConfiguration &cfg : config_) {
> > + const PixelFormatInfo &info =
> PixelFormatInfo::info(cfg.pixelFormat);
> > +
> > + if (info.colourEncoding ==
> PixelFormatInfo::ColourEncodingRAW) {
> > + rawCount++;
> > + rawSize = std::max(rawSize, cfg.size);
> > + } else {
> > + yuvCount++;
> > + maxYuvSize = std::max(maxYuvSize, cfg.size);
> > + rawRequirement.expandTo(cfg.size);
> > + }
> > + }
> > +
> > + // TODO: Base on number of cameras?
>
> Well, should this pipeline register more than one camera ?
> And anyway, the number of streams is per-camera so the number of
> cameras should be not relevant here
>
>
Yes, and yes. The configuration file should specify the number of supported
streams.
> > + if (rawCount > 1 || yuvCount > 2) {
> > + LOG(Fake, Debug) << "Camera configuration not supported";
> > + return Invalid;
> > + }
> > +
> > + /*
> > + * Generate raw configuration from CIO2.
> > + *
> > + * The output YUV streams will be limited in size to the maximum
> frame
> > + * size requested for the RAW stream, if present.
> > + *
> > + * If no raw stream is requested, generate a size from the largest
> YUV
> > + * stream, aligned to the ImgU constraints and bound
> > + * by the sensor's maximum resolution. See
> > + * https://bugs.libcamera.org/show_bug.cgi?id=32
> > + */
> > + // TODO
> > + if (rawSize.isNull())
> > + rawSize = rawRequirement;
>
> All of these serves on IPU3 to generate a size suitable for the CIO2
> and the sensor. You can remove it.
>
>
Right, thanks!
> +
> > + /*
> > + * Adjust the configurations if needed and assign streams while
> > + * iterating them.
> > + */
> > + bool mainOutputAvailable = true;
> > + for (unsigned int i = 0; i < config_.size(); ++i) {
> > + const PixelFormatInfo &info =
> PixelFormatInfo::info(config_[i].pixelFormat);
> > + const StreamConfiguration originalCfg = config_[i];
> > + StreamConfiguration *cfg = &config_[i];
> > +
> > + LOG(Fake, Debug) << "Validating stream: " <<
> config_[i].toString();
> > +
> > + if (info.colourEncoding ==
> PixelFormatInfo::ColourEncodingRAW) {
> > + /* Initialize the RAW stream with the CIO2
> configuration. */
> > + cfg->size = rawSize;
> > + // TODO: check
> > + cfg->pixelFormat = formats::SBGGR10_IPU3;
> > + cfg->bufferCount =
> FakeCameraConfiguration::kBufferCount;
> > + cfg->stride = info.stride(cfg->size.width, 0, 64);
> > + cfg->frameSize = info.frameSize(cfg->size, 64);
> > + cfg->setStream(const_cast<Stream
> *>(&data_->rawStream_));
> > +
> > + LOG(Fake, Debug) << "Assigned " << cfg->toString()
> > + << " to the raw stream";
> > + } else {
> > + /* Assign and configure the main and viewfinder
> outputs. */
> > +
> > + cfg->pixelFormat = formats::NV12;
> > + cfg->bufferCount = kBufferCount;
> > + cfg->stride = info.stride(cfg->size.width, 0, 1);
> > + cfg->frameSize = info.frameSize(cfg->size, 1);
> > +
> > + /*
> > + * Use the main output stream in case only one
> stream is
> > + * requested or if the current configuration is
> the one
> > + * with the maximum YUV output size.
> > + */
> > + if (mainOutputAvailable &&
> > + (originalCfg.size == maxYuvSize || yuvCount ==
> 1)) {
> > + cfg->setStream(const_cast<Stream
> *>(&data_->outStream_));
> > + mainOutputAvailable = false;
> > +
> > + LOG(Fake, Debug) << "Assigned " <<
> cfg->toString()
> > + << " to the main output";
> > + } else {
> > + cfg->setStream(const_cast<Stream
> *>(&data_->vfStream_));
> > +
> > + LOG(Fake, Debug) << "Assigned " <<
> cfg->toString()
> > + << " to the viewfinder
> output";
> > + }
> > + }
>
> Again I'm not sure how much of the IPU3 should this pipeline mimick,
> or it should establish requirements and constraints (ie max 2 YUV
> streams of max size width x height, and 1 RAW stream in format
> V4L2_PIX_FMT_..)
>
>
Exactly. Will do.
> > +
> > + if (cfg->pixelFormat != originalCfg.pixelFormat ||
> > + cfg->size != originalCfg.size) {
> > + LOG(Fake, Debug)
> > + << "Stream " << i << " configuration
> adjusted to "
> > + << cfg->toString();
> > + status = Adjusted;
> > + }
> > + }
> > +
> > + return status;
> > +}
> > +
> > +PipelineHandlerFake::PipelineHandlerFake(CameraManager *manager)
> > + : PipelineHandler(manager)
> > +{
> > + // TODO: read the fake hal spec file.
>
> If there's a design document with requirements, can you share it ?
>
>
Will do.
> > +}
> > +
> > +CameraConfiguration *PipelineHandlerFake::generateConfiguration(Camera
> *camera,
> > + const
> StreamRoles &roles)
> > +{
> > + FakeCameraData *data = cameraData(camera);
> > + FakeCameraConfiguration *config = new
> FakeCameraConfiguration(data);
> > +
> > + if (roles.empty())
> > + return config;
> > +
> > + Size minSize, sensorResolution;
> > + for (const auto& resolution : data->supported_resolutions_) {
> > + if (minSize.isNull() || minSize > resolution.size)
> > + minSize = resolution.size;
> > +
> > + sensorResolution = std::max(sensorResolution,
> resolution.size);
> > + }
> > +
>
> Those are hard-coded values, I think you can reuse them here.
> Unless the idea is to make this information come from a configuration
> file or something similar.
>
> Yes, supportedResolutions_ should eventually come from a configuration
file, provided by the tester.
> > + for (const StreamRole role : roles) {
> > + std::map<PixelFormat, std::vector<SizeRange>>
> streamFormats;
> > + unsigned int bufferCount;
> > + PixelFormat pixelFormat;
> > + Size size;
> > +
> > + switch (role) {
> > + case StreamRole::StillCapture:
> > + size = sensorResolution;
> > + pixelFormat = formats::NV12;
> > + bufferCount =
> FakeCameraConfiguration::kBufferCount;
> > + streamFormats[pixelFormat] = { { minSize, size } };
> > +
> > + break;
> > +
> > + case StreamRole::Raw: {
> > + // TODO: check
> > + pixelFormat = formats::SBGGR10_IPU3;
> > + size = sensorResolution;
> > + bufferCount =
> FakeCameraConfiguration::kBufferCount;
> > + streamFormats[pixelFormat] = { { minSize, size } };
> > +
> > + break;
> > + }
> > +
> > + case StreamRole::Viewfinder:
> > + case StreamRole::VideoRecording: {
> > + /*
> > + * Default viewfinder and videorecording to
> 1280x720,
> > + * capped to the maximum sensor resolution and
> aligned
> > + * to the ImgU output constraints.
> > + */
> > + size = sensorResolution;
> > + pixelFormat = formats::NV12;
> > + bufferCount =
> FakeCameraConfiguration::kBufferCount;
> > + streamFormats[pixelFormat] = { { minSize, size } };
> > +
> > + break;
> > + }
> > +
> > + default:
> > + LOG(Fake, Error)
> > + << "Requested stream role not supported: "
> << role;
> > + delete config;
> > + return nullptr;
> > + }
> > +
> > + StreamFormats formats(streamFormats);
> > + StreamConfiguration cfg(formats);
> > + cfg.size = size;
> > + cfg.pixelFormat = pixelFormat;
> > + cfg.bufferCount = bufferCount;
> > + config->addConfiguration(cfg);
> > + }
> > +
> > + if (config->validate() == CameraConfiguration::Invalid)
> > + return {};
> > +
> > + return config;
> > +}
> > +
> > +int PipelineHandlerFake::configure(Camera *camera, CameraConfiguration
> *c)
> > +{
> > + if (camera || c)
> > + return 0;
> > + return 0;
> > +}
> > +
> > +int PipelineHandlerFake::exportFrameBuffers(Camera *camera, Stream
> *stream,
> > +
> std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> > +{
> > + // Assume it's never called.
> > + LOG(Fake, Fatal) << "exportFrameBuffers should never be called";
> > + if (camera || stream || buffers)
> > + return -EINVAL;
> > + return -EINVAL;
> > +}
> > +
> > +int PipelineHandlerFake::start(Camera *camera, [[maybe_unused]] const
> ControlList *controls)
> > +{
> > + FakeCameraData *data = cameraData(camera);
> > + data->started_ = true;
> > +
> > + return 0;
> > +}
> > +
> > +void PipelineHandlerFake::stopDevice(Camera *camera)
> > +{
> > + FakeCameraData *data = cameraData(camera);
> > +
> > + data->started_ = false;
> > +}
> > +
> > +int PipelineHandlerFake::queueRequestDevice(Camera *camera, Request
> *request)
> > +{
> > + if (!camera)
> > + return -EINVAL;
>
> Can this happen ?
>
> Actually it's a hack that CrOS compiler will fail if there's any unused
argument...
I'll try to clean it up later.
> > +
> > + for (auto it : request->buffers())
> > + completeBuffer(request, it.second);
> > +
> > + // TODO: request.metadata()
> > + request->metadata().set(controls::SensorTimestamp,
> CurrentTimestamp());
> > + completeRequest(request);
> > +
> > + return 0;
> > +}
> > +
> > +bool PipelineHandlerFake::match(DeviceEnumerator *enumerator)
> > +{
> > + // TODO: exhaust all devices in |enumerator|.
> > + if (!enumerator)
> > + LOG(Fake, Info) << "Invalid enumerator";
>
> Can this happen ?
>
> ditto
> > +
> > + if (registered_)
> > + return false;
> > +
> > + registered_ = true;
> > + return registerCameras() == 0;
> > +}
> > +
> > +/**
> > + * \brief Initialise ImgU and CIO2 devices associated with cameras
> > + *
> > + * Initialise the two ImgU instances and create cameras with an
> associated
> > + * CIO2 device instance.
> > + *
> > + * \return 0 on success or a negative error code for error or if no
> camera
> > + * has been created
> > + * \retval -ENODEV no camera has been created
> > + */
> > +int PipelineHandlerFake::registerCameras()
> > +{
> > + std::unique_ptr<FakeCameraData> data =
> > + std::make_unique<FakeCameraData>(this);
> > + std::set<Stream *> streams = {
> > + &data->outStream_,
> > + &data->vfStream_,
> > + &data->rawStream_,
> > + };
> > +
> > + // TODO: Read from config or from IPC.
> > + // TODO: Check with Han-lin: Can this function be called more than
> once?
> > + data->supported_resolutions_.resize(2);
> > + data->supported_resolutions_[0].size = Size(1920, 1080);
> > + data->supported_resolutions_[0].frame_rates.push_back(30);
> > + data->supported_resolutions_[0].formats.push_back("YCbCr_420_888");
> > + data->supported_resolutions_[0].formats.push_back("BLOB");
> > + data->supported_resolutions_[1].size = Size(1280, 720);
> > + data->supported_resolutions_[1].frame_rates.push_back(30);
> > + data->supported_resolutions_[1].frame_rates.push_back(60);
> > + data->supported_resolutions_[1].formats.push_back("YCbCr_420_888");
> > + data->supported_resolutions_[1].formats.push_back("BLOB");
> > +
> > + // TODO: Assign different locations for different cameras based on
> config.
> > + data->properties_.set(properties::Location,
> properties::CameraLocationFront);
> > + data->properties_.set(properties::PixelArrayActiveAreas, {
> Rectangle(Size(1920, 1080)) });
> > +
> > + // TODO: Set FrameDurationLimits based on config.
> > + ControlInfoMap::Map controls{};
> > + int64_t min_frame_duration = 30, max_frame_duration = 60;
> > + controls[&controls::FrameDurationLimits] =
> ControlInfo(min_frame_duration, max_frame_duration);
> > + data->controlInfo_ = ControlInfoMap(std::move(controls),
> controls::controls);
>
> FakeControls is ignored
>
> Right. Use it to initialize |controls| here.
> > +
> > + std::shared_ptr<Camera> camera =
> > + Camera::create(std::move(data), "\\_SB_.PCI0.I2C4.CAM1" /*
> cameraId */, streams);
> > +
> > + registerCamera(std::move(camera));
> > +
> > + return 0;
> > +}
> > +
> > +REGISTER_PIPELINE_HANDLER(PipelineHandlerFake)
> > +
> > +} /* namespace libcamera */
> > diff --git a/src/libcamera/pipeline/fake/meson.build
> b/src/libcamera/pipeline/fake/meson.build
> > new file mode 100644
> > index 00000000..13980b4a
> > --- /dev/null
> > +++ b/src/libcamera/pipeline/fake/meson.build
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: CC0-1.0
> > +
> > +libcamera_sources += files([ 'fake.cpp' ])
> > diff --git a/src/libcamera/pipeline_handler.cpp
> b/src/libcamera/pipeline_handler.cpp
> > index e5cb751c..4261154d 100644
> > --- a/src/libcamera/pipeline_handler.cpp
> > +++ b/src/libcamera/pipeline_handler.cpp
> > @@ -536,7 +536,7 @@ void
> PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
> > cameras_.push_back(camera);
> >
> > if (mediaDevices_.empty())
> > - LOG(Pipeline, Fatal)
> > + LOG(Pipeline, Error)
>
> I don't think we want that, you should probably open code the
>
> manager_->addCamera(std::move(camera), devnums);
>
> call, with an empty list of devnums, which should be fine for this use
> case.
>
> The PipelineHandler::cameras_ vector will remain unpopulated, but
> since it is used only in case of a media device disconnection (afaict)
> it should be fine in your case
>
>
Ah you're right. Thanks!
>
> > << "Registering camera with no media devices!";
> >
> > /*
> > diff --git a/test/camera/camera_reconfigure.cpp
> b/test/camera/camera_reconfigure.cpp
> > index d12e2413..06c87730 100644
> > --- a/test/camera/camera_reconfigure.cpp
> > +++ b/test/camera/camera_reconfigure.cpp
> > @@ -98,7 +98,7 @@ private:
> > return TestFail;
> > }
> >
> > - requests_.push_back(move(request));
> > + requests_.push_back(std::move(request));
>
> Unrelated and possibly not necessary, as the test has
> using namespace std;
>
>
Yeah... it's also a hack that it's required in the CrOS compiler... It
doesn't
accept |move|...
> Looking forward to seeing this skeleton getting populated, it will help
> testing the Android layer!
>
> Thanks
> j
>
> > }
> >
> > camera_->requestCompleted.connect(this,
> &CameraReconfigure::requestComplete);
> > --
> > 2.38.0.rc1.362.ged0d419d3c-goog
> >
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.libcamera.org/pipermail/libcamera-devel/attachments/20221014/c131628e/attachment.htm>
More information about the libcamera-devel
mailing list