[RFC] libcamera: lc-compliance: Add initial set of per frame control tests
Stefan Klug
stefan.klug at ideasonboard.com
Fri Mar 1 14:34:52 CET 2024
Hi Kieran,
thanks for you review. I will fix the commit/style issues in the first patchset.
For now I was more focused on starting a dicussion on the overall idea.
Am 29.02.24 um 18:36 schrieb Kieran Bingham:
> Hi Stefan,
>
> Some quick review notes.
>
> I'm happy to see a test that actually tries to validate the
> DelayedControls implementations, and considers the real effects on the
> sensor.
>
> We try to break patches down to single topic commits, so here I can see
> at least
> - The introduction of the TimeSheet helper class
> - Fix the SimpleCapture::start() to accept a control list
> - Add per-frame-control tests>
> Quoting Stefan Klug (2024-02-29 17:01:15)
>> These tests check if controls (only exposure time and analogue gain at
>> the moment) get applied on the frame they were requested for.
>>
>> This is tested by looking at the metadata and additionally by
>> calculating a mean brightness on a centered rect of 20x20 pixels.
>>
>> Until today, these tests where only run on a project specific branch
>> with a modified simple pipeline. In theory they should pass on a
>> current master :-)
>
> I love good theories! ;-) I'm curious to see what happens on master and
> other platforms.
>
>
>> Current test setup: imx219 with simple pipeline on an imx8mp.
>> Modifications of either the exposure delay or the gain delay in
>> the camera_sensor class resulted in test failures.
>> Which is exactly what this test shall proove.
>
> 'prove' ;-)
>
>>
>> Signed-off-by: Stefan Klug <stefan.klug at ideasonboard.com>
>> ---
>> src/apps/lc-compliance/capture_test.cpp | 39 ++++
>> src/apps/lc-compliance/meson.build | 2 +
>> src/apps/lc-compliance/per_frame_controls.cpp | 214 ++++++++++++++++++
>> src/apps/lc-compliance/per_frame_controls.h | 41 ++++
>> src/apps/lc-compliance/simple_capture.cpp | 4 +-
>> src/apps/lc-compliance/simple_capture.h | 2 +-
>> src/apps/lc-compliance/time_sheet.cpp | 135 +++++++++++
>> src/apps/lc-compliance/time_sheet.h | 53 +++++
>> 8 files changed, 487 insertions(+), 3 deletions(-)
>> create mode 100644 src/apps/lc-compliance/per_frame_controls.cpp
>> create mode 100644 src/apps/lc-compliance/per_frame_controls.h
>> create mode 100644 src/apps/lc-compliance/time_sheet.cpp
>> create mode 100644 src/apps/lc-compliance/time_sheet.h
>>
>> diff --git a/src/apps/lc-compliance/capture_test.cpp b/src/apps/lc-compliance/capture_test.cpp
>> index 1dcfcf92..43fe59f3 100644
>> --- a/src/apps/lc-compliance/capture_test.cpp
>> +++ b/src/apps/lc-compliance/capture_test.cpp
>> @@ -11,6 +11,7 @@
>> #include <gtest/gtest.h>
>>
>> #include "environment.h"
>> +#include "per_frame_controls.h"
>> #include "simple_capture.h"
>>
>> using namespace libcamera;
>> @@ -133,3 +134,41 @@ INSTANTIATE_TEST_SUITE_P(CaptureTests,
>> testing::Combine(testing::ValuesIn(ROLES),
>> testing::ValuesIn(NUMREQUESTS)),
>> SingleStream::nameParameters);
>> +
>> +/*
>> + * Test Per frame controls
>> + */
>> +TEST_F(SingleStream, testFramePreciseExposureChange)
>> +{
>> + PerFrameControls capture(camera_);
>> + capture.configure(StreamRole::Viewfinder);
>> + capture.testFramePreciseExposureChange();
>> +}
>> +
>> +TEST_F(SingleStream, testFramePreciseGainChange)
>> +{
>> + PerFrameControls capture(camera_);
>> + capture.configure(StreamRole::Viewfinder);
>> + capture.testFramePreciseGainChange();
>> +}
>> +
>> +TEST_F(SingleStream, testExposureGainIsAppliedOnFirstFrame)
>> +{
>> + PerFrameControls capture(camera_);
>> + capture.configure(StreamRole::Viewfinder);
>> + capture.testExposureGainIsAppliedOnFirstFrame();
>> +}
>> +
>> +TEST_F(SingleStream, testExposureGainFromFirstRequestGetsApplied)
>> +{
>> + PerFrameControls capture(camera_);
>> + capture.configure(StreamRole::Viewfinder);
>> + capture.testExposureGainFromFirstRequestGetsApplied();
>> +}
>> +
>> +TEST_F(SingleStream, testExposureGainFromFirstAndSecondRequestGetsApplied)
>> +{
>> + PerFrameControls capture(camera_);
>> + capture.configure(StreamRole::Viewfinder);
>> + capture.testExposureGainFromFirstAndSecondRequestGetsApplied();
>> +}
>> diff --git a/src/apps/lc-compliance/meson.build b/src/apps/lc-compliance/meson.build
>> index c792f072..2a6f52af 100644
>> --- a/src/apps/lc-compliance/meson.build
>> +++ b/src/apps/lc-compliance/meson.build
>> @@ -15,7 +15,9 @@ lc_compliance_sources = files([
>> 'capture_test.cpp',
>> 'environment.cpp',
>> 'main.cpp',
>> + 'per_frame_controls.cpp',
>> 'simple_capture.cpp',
>> + 'time_sheet.cpp',
>> ])
>>
>> lc_compliance = executable('lc-compliance', lc_compliance_sources,
>> diff --git a/src/apps/lc-compliance/per_frame_controls.cpp b/src/apps/lc-compliance/per_frame_controls.cpp
>> new file mode 100644
>> index 00000000..70fc44ac
>> --- /dev/null
>> +++ b/src/apps/lc-compliance/per_frame_controls.cpp
>> @@ -0,0 +1,214 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Copyright (C) 2024, Ideas on Board Oy
>> + *
>> + * per_frame_controls.cpp - Tests for per frame controls
>> + */
>> +#include "per_frame_controls.h"
>> +
>> +#include <gtest/gtest.h>
>> +
>> +#include "time_sheet.h"
>> +
>> +using namespace libcamera;
>> +
>> +PerFrameControls::PerFrameControls(std::shared_ptr<Camera> camera)
>> + : SimpleCapture(camera)
>> +{
>> +}
>> +
>> +std::shared_ptr<TimeSheet> PerFrameControls::startCaptureWithTimeSheet(unsigned int framesToCapture, const ControlList *controls)
>> +{
>> + ControlList ctrls(camera_->controls().idmap());
>> + /* Ensure defined default values */
>> + ctrls.set(controls::AeEnable, false);
>> + ctrls.set(controls::AeExposureMode, controls::ExposureCustom);
>> + ctrls.set(controls::ExposureTime, 10000);
>> + ctrls.set(controls::AnalogueGain, 1.0);
>> +
>> + if (controls) {
>> + ctrls.merge(*controls, true);
>> + }
>
> No need for { } on a single line statement.
> if (controls)
> ctrls.merge(*controls, true);
>
> is sufficient in our code style.
>
>
>> +
>> + start(&ctrls);
>> +
>> + queueCount_ = 0;
>> + captureCount_ = 0;
>> + captureLimit_ = framesToCapture;
>> +
>> + auto timeSheet = std::make_shared<TimeSheet>(captureLimit_, camera_->controls().idmap());
>> + timeSheet_ = timeSheet;
>
> I'm curious. Why shared and not make_unique? Is it kept / shared in
> multiple locations? I guess I'll see later.
>
>> + return timeSheet;
>> +}
>> +
>> +int PerFrameControls::queueRequest(Request *request)
>> +{
>> + queueCount_++;
>> + if (queueCount_ > captureLimit_)
>
> Should this return an error as it's /not/ queueing? Maybe not essential
> ... lets see how it's used...
>
>
> Probably not as we just want to stop queueing and we still want to wait
> for the requests queued to complete...
>
>
>> + return 0;
>> +
>> + auto ts = timeSheet_.lock();
>> + if (ts) {
>> + ts->prepareForQueue(request, queueCount_ - 1);
>> + }
>
> Single line so no braces, same elsewhere.
>
>> +
>> + return camera_->queueRequest(request);
>> +}
>> +
>> +void PerFrameControls::requestComplete(Request *request)
>> +{
>> + auto ts = timeSheet_.lock();
>> + if (ts) {
>> + ts->handleCompleteRequest(request);
>> + }
>> +
>> + captureCount_++;
>> + if (captureCount_ >= captureLimit_) {
>> + loop_->exit(0);
>> + return;
>> + }
>> +
>> + request->reuse(Request::ReuseBuffers);
>> + if (queueRequest(request))
>> + loop_->exit(-EINVAL);
>> +}
>> +
>> +void PerFrameControls::runCaptureSession()
>> +{
>> + Stream *stream = config_->at(0).stream();
>> + const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);
>> +
>> + /* Queue the recommended number of reqeuests. */
>> + for (const std::unique_ptr<FrameBuffer> &buffer : buffers) {
>> + std::unique_ptr<Request> request = camera_->createRequest();
>> + request->addBuffer(stream, buffer.get());
>> + queueRequest(request.get());
>> + requests_.push_back(std::move(request));
>> + }
>> +
>> + /* Run capture session. */
>> + loop_ = new EventLoop();
>> + loop_->exec();
>> + stop();
>> + delete loop_;
>> +}
>> +
>> +void PerFrameControls::testFramePreciseExposureChange()
>> +{
>> + auto timeSheet = startCaptureWithTimeSheet(10);
>> + auto &ts = *timeSheet;
>> +
>> +
>> + ts[3].controls().set(controls::ExposureTime, 5000);
>> + //wait a few frames to settle
>
> /* Single line comment style. */
>
>> + ts[6].controls().set(controls::ExposureTime, 20000);
>> + ts.printAllInfos();
>> +
>> + runCaptureSession();
>> +
>> + EXPECT_NEAR(ts[5].metadata().get(controls::ExposureTime).value(), 5000, 20);
>> + EXPECT_NEAR(ts[6].metadata().get(controls::ExposureTime).value(), 20000, 20);
>> +
>> + /* No increase just before setting exposure */
>> + EXPECT_NEAR(ts[5].getBrightnessChange(), 1.0, 0.05) << "Brightness changed too much before the expected time of change (control delay too high?).";
>
> I'd wrap this a little, but it's better to keep the comment in one.
> Perhaps:
>
> EXPECT_NEAR(ts[5].getBrightnessChange(), 1.0, 0.05)
> << "Brightness changed too much before the expected time of change (control delay too high?).";
>
>> + /*
>> + * Todo: The change is brightness was a bit low (Exposure time increase by 4x resulted in a brightness increase of < 2).
>> + * This should be investigated.
>> + */
>
> indentation is broken here. Also lines could be wrapped to target
> 80chars.
>
>
>
>> + EXPECT_GT(ts[6].getBrightnessChange(), 1.3) << "Brightness in frame " << 6 << " did not increase as expected (reference: "
>> + << ts[3].getSpotBrightness() << " current: " << ts[6].getSpotBrightness() << " )" << std::endl;
>> +
>> + /* No increase just after setting exposure */
>> + EXPECT_NEAR(ts[7].getBrightnessChange(), 1.0, 0.05) << "Brightness changed too much after the expected time of change (control delay too low?).";
>> +
>> + /* No increase just after setting exposure */
>> + EXPECT_NEAR(ts[8].getBrightnessChange(), 1.0, 0.05) << "Brightness changed too much2 frames after the expected time of change (control delay too low?).";
>
> Same here, I'd wrap the message to the line below. But that's all we can
> do here I think.
>
> "changed too much2 frames" probably needs cleaning up.
>
>> +}
>> +
>> +void PerFrameControls::testFramePreciseGainChange()
>> +{
>> + auto timeSheet = startCaptureWithTimeSheet(10);
>> + auto &ts = *timeSheet;
>> +
>> + ts[3].controls().set(controls::AnalogueGain, 1.0);
>> + //wait a few frames to settle
>> + ts[6].controls().set(controls::AnalogueGain, 4.0);
>> +
>> + runCaptureSession();
>> +
>> + EXPECT_NEAR(ts[5].metadata().get(controls::AnalogueGain).value(), 1.0, 0.1);
>> + EXPECT_NEAR(ts[6].metadata().get(controls::AnalogueGain).value(), 4.0, 0.1);
>> +
>> + /* No increase just before setting gain */
>> + EXPECT_NEAR(ts[5].getBrightnessChange(), 1.0, 0.05) << "Brightness changed too much before the expected time of change (control delay too high?).";
>> + /*
>> + * Todo: I see a brightness change of roughly half the expected one. This is not yet understood and needs investigation
>> + */
>
> Same indentation issues here, and wrapping where possible.
>
>> + EXPECT_GT(ts[6].getBrightnessChange(), 1.7) << "Brightness in frame " << 6 << " did not increase as expected (reference: "
>> + << ts[5].getSpotBrightness() << " current: " << ts[6].getSpotBrightness() << " )" << std::endl;
>> +
>> + /* No increase just after setting gain */
>> + EXPECT_NEAR(ts[7].getBrightnessChange(), 1.0, 0.05) << "Brightness changed too much after the expected time of change (control delay too low?).";
>> +}
>> +
>> +void PerFrameControls::testExposureGainFromFirstRequestGetsApplied()
>> +{
>> + auto timeSheet = startCaptureWithTimeSheet(5);
>> + auto &ts = *timeSheet;
>> +
>> + ts[0].controls().set(controls::ExposureTime, 10000);
>> + ts[0].controls().set(controls::AnalogueGain, 4.0);
>> +
>> + runCaptureSession();
>> +
>> + /* We expect it to be applied after 3 frames, the latest*/
>
> "latest */"
>
>> + EXPECT_NEAR(ts[4].metadata().get(controls::ExposureTime).value(), 10000, 20);
>> + EXPECT_NEAR(ts[4].metadata().get(controls::AnalogueGain).value(), 4.0, 0.1);
>> +}
>> +
>> +void PerFrameControls::testExposureGainFromFirstAndSecondRequestGetsApplied()
>> +{
>> + auto timeSheet = startCaptureWithTimeSheet(5);
>> + auto &ts = *timeSheet;
>> +
>> + ts[0].controls().set(controls::ExposureTime, 8000);
>> + ts[0].controls().set(controls::AnalogueGain, 2.0);
>> + ts[1].controls().set(controls::ExposureTime, 10000);
>> + ts[1].controls().set(controls::AnalogueGain, 4.0);
>> +
>> + runCaptureSession();
>> +
>> + /* We expect it to be applied after 3 frames, the latest*/
>
> "latest */"
>
>> + EXPECT_NEAR(ts[4].metadata().get(controls::ExposureTime).value(), 10000, 20);
>> + EXPECT_NEAR(ts[4].metadata().get(controls::AnalogueGain).value(), 4.0, 0.1);
>> +}
>> +
>> +void PerFrameControls::testExposureGainIsAppliedOnFirstFrame()
>> +{
>> + ControlList startValues;
>> + startValues.set(controls::ExposureTime, 5000);
>> + startValues.set(controls::AnalogueGain, 1.0);
>> +
>> + auto ts1 = startCaptureWithTimeSheet(3, &startValues);
>> +
>> + runCaptureSession();
>> +
>> + EXPECT_NEAR((*ts1)[0].metadata().get(controls::ExposureTime).value(), 5000, 20);
>> + EXPECT_NEAR((*ts1)[0].metadata().get(controls::AnalogueGain).value(), 1.0, 0.01);
>> +
>> + /* Second capture with different values to ensure we don't hit default/old values */
>> +
>> + startValues.set(controls::ExposureTime, 15000);
>> + startValues.set(controls::AnalogueGain, 4.0);
>> +
>> + auto ts2 = startCaptureWithTimeSheet(3, &startValues);
>> +
>> + runCaptureSession();
>> +
>> + EXPECT_NEAR((*ts2)[0].metadata().get(controls::ExposureTime).value(), 15000, 20);
>> + EXPECT_NEAR((*ts2)[0].metadata().get(controls::AnalogueGain).value(), 4.0, 0.01);
>> +
>> + /* with 3x exposure and 4x gain we could expect a brightness increase of 2x */
>> + double brightnessChange = ts2->get(1).getSpotBrightness() / ts1->get(1).getSpotBrightness();
>> + EXPECT_GT(brightnessChange, 2.0);
>> +}
>> diff --git a/src/apps/lc-compliance/per_frame_controls.h b/src/apps/lc-compliance/per_frame_controls.h
>> new file mode 100644
>> index 00000000..e783f024
>> --- /dev/null
>> +++ b/src/apps/lc-compliance/per_frame_controls.h
>> @@ -0,0 +1,41 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Copyright (C) 2024, Ideas on Board Oy
>> + *
>> + * per_frame_controls.h - Tests for per frame controls
>> + */
>> +
>> +#pragma once
>> +
>> +#include <memory>
>> +
>> +#include <libcamera/libcamera.h>
>> +
>> +#include "../common/event_loop.h"
>> +
>> +#include "simple_capture.h"
>> +#include "time_sheet.h"
>> +
>> +class PerFrameControls : public SimpleCapture
>> +{
>> +public:
>> + PerFrameControls(std::shared_ptr<libcamera::Camera> camera);
>> +
>> + std::shared_ptr<TimeSheet> startCaptureWithTimeSheet(unsigned int framesToCapture, const libcamera::ControlList *controls = nullptr);
>> + void runCaptureSession();
>> +
>> + void testFramePreciseExposureChange();
>> + void testFramePreciseGainChange();
>> + void testExposureGainIsAppliedOnFirstFrame();
>> + void testExposureGainFromFirstRequestGetsApplied();
>> + void testExposureGainFromFirstAndSecondRequestGetsApplied();
>> +
>> + int queueRequest(libcamera::Request *request);
>> + void requestComplete(libcamera::Request *request) override;
>> +
>> + unsigned int queueCount_;
>> + unsigned int captureCount_;
>> + unsigned int captureLimit_;
>> +
>> + std::weak_ptr<TimeSheet> timeSheet_;
>> +};
>> diff --git a/src/apps/lc-compliance/simple_capture.cpp b/src/apps/lc-compliance/simple_capture.cpp
>> index cf4d7cf3..56680a83 100644
>> --- a/src/apps/lc-compliance/simple_capture.cpp
>> +++ b/src/apps/lc-compliance/simple_capture.cpp
>> @@ -42,7 +42,7 @@ void SimpleCapture::configure(StreamRole role)
>> }
>> }
>>
>> -void SimpleCapture::start()
>> +void SimpleCapture::start(const ControlList *controls)
>> {
>> Stream *stream = config_->at(0).stream();
>> int count = allocator_->allocate(stream);
>> @@ -52,7 +52,7 @@ void SimpleCapture::start()
>>
>> camera_->requestCompleted.connect(this, &SimpleCapture::requestComplete);
>>
>> - ASSERT_EQ(camera_->start(), 0) << "Failed to start camera";
>> + ASSERT_EQ(camera_->start(controls), 0) << "Failed to start camera";
>> }
>>
>> void SimpleCapture::stop()
>> diff --git a/src/apps/lc-compliance/simple_capture.h b/src/apps/lc-compliance/simple_capture.h
>> index 2911d601..54b1d54b 100644
>> --- a/src/apps/lc-compliance/simple_capture.h
>> +++ b/src/apps/lc-compliance/simple_capture.h
>> @@ -22,7 +22,7 @@ protected:
>> SimpleCapture(std::shared_ptr<libcamera::Camera> camera);
>> virtual ~SimpleCapture();
>>
>> - void start();
>> + void start(const libcamera::ControlList *controls = nullptr);
>> void stop();
>
>
> I would suggest breaking out the change here that enables setting
> controls at SimpleCapture::start() to it's own patch.
>
>
>>
>> virtual void requestComplete(libcamera::Request *request) = 0;
>> diff --git a/src/apps/lc-compliance/time_sheet.cpp b/src/apps/lc-compliance/time_sheet.cpp
>> new file mode 100644
>> index 00000000..9a0e6544
>> --- /dev/null
>> +++ b/src/apps/lc-compliance/time_sheet.cpp
>
> I think introducting TimeSheet can be a separate patch to make it
> clearer.
>
>
>> @@ -0,0 +1,135 @@
>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> +/*
>> + * Copyright (C) 2024, Ideas on Board Oy
>> + *
>> + * time_sheet.cpp
>> + */
>> +#include "time_sheet.h"
>> +
>> +#include <sstream>
>> +#include <libcamera/libcamera.h>
>> +
>> +#include "libcamera/internal/formats.h"
>> +#include "libcamera/internal/mapped_framebuffer.h"
>> +
>> +using namespace libcamera;
>> +
>> +double calcPixelMeanNV12(const uint8_t *data)
>> +{
>> + return (double)*data;
>
> Oh, from the name "calcPixelMean" I was expecting a calculation, not
> just a cast.
It was actually coincidence that I happened to start with pixel formats where a
simple cast is sufficient. In case of RGB it would be a calculation :-)
>
>
> Maybe "double readPixelNV12()" or something.
>
> I think Laurent started looking at some 'image' type classes to tackle
> something similar to this problem.
>
That would be nice.
Cheers,
Stefan
>
>> +}
>> +
>> +double calcPixelMeanRAW10(const uint8_t *data)
>> +{
>> + return (double)*((const uint16_t *)data);
>> +}
>> +
>> +double calculateMeanBrightnessFromCenterSpot(libcamera::Request *request)
>> +{
>> + const Request::BufferMap &buffers = request->buffers();
>> + for (const auto &[stream, buffer] : buffers) {
>> + MappedFrameBuffer in(buffer, MappedFrameBuffer::MapFlag::Read);
>> + if (in.isValid()) {
>> + auto data = in.planes()[0].data();
>> + auto streamConfig = stream->configuration();
>> + auto formatInfo = PixelFormatInfo::info(streamConfig.pixelFormat);
>> +
>> + std::function<double(const uint8_t *data)> calcPixelMean;
>> + int pixelStride;
>> +
>> + switch (streamConfig.pixelFormat) {
>> + case formats::NV12:
>> + calcPixelMean = calcPixelMeanNV12;
>> + pixelStride = 1;
>> + break;
>> + case formats::SRGGB10:
>> + calcPixelMean = calcPixelMeanRAW10;
>> + pixelStride = 2;
>> + break;
>> + default:
>> + std::stringstream s;
>> + s << "Unsupported Pixelformat " << formatInfo.name;
>> + throw std::invalid_argument(s.str());
>> + }
>> +
>> + double sum = 0;
>> + int w = 20;
>> + int xs = streamConfig.size.width / 2 - w / 2;
>> + int ys = streamConfig.size.height / 2 - w / 2;
>> +
>> + for (auto y = ys; y < ys + w; y++) {
>> + auto line = data + y * streamConfig.stride;
>> + for (auto x = xs; x < xs + w; x++) {
>> + sum += calcPixelMean(line + x * pixelStride);
>> + }
>> + }
>> + sum = sum / (w * w);
>> + return sum;
>> + }
>> + }
>> + return 0;
>> +}
>> +
>> +TimeSheetEntry::TimeSheetEntry(const ControlIdMap &idmap)
>> + : controls_(idmap)
>> +{
>> +}
>> +
>> +void TimeSheetEntry::handleCompleteRequest(libcamera::Request *request, const TimeSheetEntry *previous)
>> +{
>> + metadata_ = request->metadata();
>> +
>> + spotBrightness_ = calculateMeanBrightnessFromCenterSpot(request);
>> + if (previous) {
>> + brightnessChange_ = spotBrightness_ / previous->getSpotBrightness();
>> + }
>> + sequence_ = request->sequence();
>> +}
>> +
>> +void TimeSheetEntry::printInfo()
>> +{
>> + std::cout << "=== Frame " << sequence_ << std::endl;
>> + std::cout << "Brightness: " << spotBrightness_ << std::endl;
>> +
>> + if (!metadata_.empty()) {
>> + std::cout << "Metadata:" << std::endl;
>> + auto idMap = metadata_.idMap();
>> + assert(idMap);
>> + for (const auto &[id, value] : metadata_) {
>> + std::cout << " " << idMap->at(id)->name() << " : " << value.toString() << std::endl;
>> + }
>> + }
>> +}
>> +
>> +TimeSheetEntry &TimeSheet::get(size_t pos)
>> +{
>> + auto &entry = entries_[pos];
>> + if (!entry)
>> + entry = std::make_shared<TimeSheetEntry>(idmap_);
>> + return *entry;
>> +}
>> +
>> +void TimeSheet::prepareForQueue(libcamera::Request *request, uint32_t sequence)
>> +{
>> + request->controls() = get(sequence).controls();
>> +}
>> +
>> +void TimeSheet::handleCompleteRequest(libcamera::Request *request)
>> +{
>> + uint32_t sequence = request->sequence();
>> + auto &entry = get(sequence);
>> + TimeSheetEntry *previous = nullptr;
>> + if (sequence >= 1) {
>> + previous = entries_[sequence - 1].get();
>> + }
>> +
>> + entry.handleCompleteRequest(request, previous);
>> +}
>> +
>> +void TimeSheet::printAllInfos()
>> +{
>> + for (auto entry : entries_) {
>> + if (entry)
>> + entry->printInfo();
>> + }
>> +}
>> diff --git a/src/apps/lc-compliance/time_sheet.h b/src/apps/lc-compliance/time_sheet.h
>> new file mode 100644
>> index 00000000..c155763c
>> --- /dev/null
>> +++ b/src/apps/lc-compliance/time_sheet.h
>> @@ -0,0 +1,53 @@
>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> +/*
>> + * Copyright (C) 2024, Ideas on Board Oy
>> + *
>> + * time_sheet.h
>> + */
>> +
>> +#pragma once
>> +
>> +#include <future>
>> +#include <vector>
>> +
>> +#include <libcamera/libcamera.h>
>> +
>> +class TimeSheetEntry
>> +{
>> +public:
>> + TimeSheetEntry(const libcamera::ControlIdMap &idmap);
>> + TimeSheetEntry(TimeSheetEntry &&other) noexcept = default;
>> + TimeSheetEntry(const TimeSheetEntry &) = delete;
>> +
>> + libcamera::ControlList &controls() { return controls_; };
>> + libcamera::ControlList &metadata() { return metadata_; };
>> + void handleCompleteRequest(libcamera::Request *request, const TimeSheetEntry *previous);
>> + void printInfo();
>> + double getSpotBrightness() const { return spotBrightness_; };
>> + double getBrightnessChange() const { return brightnessChange_; };
>> +
>> +private:
>> + double spotBrightness_ = 0.0;
>> + double brightnessChange_ = 0.0;
>> + libcamera::ControlList controls_;
>> + libcamera::ControlList metadata_;
>> + uint32_t sequence_ = 0;
>> +};
>> +
>> +class TimeSheet
>> +{
>> +public:
>> + TimeSheet(int count, const libcamera::ControlIdMap &idmap)
>> + : idmap_(idmap), entries_(count){};
>> +
>> + void prepareForQueue(libcamera::Request *request, uint32_t sequence);
>> + void handleCompleteRequest(libcamera::Request *request);
>> + void printAllInfos();
>> +
>> + TimeSheetEntry &operator[](size_t pos) { return get(pos); };
>> + TimeSheetEntry &get(size_t pos);
>> +
>> +private:
>> + const libcamera::ControlIdMap &idmap_;
>> + std::vector<std::shared_ptr<TimeSheetEntry>> entries_;
>> +};
>> --
>> 2.40.1
>>
--
Regards,
Stefan Klug
More information about the libcamera-devel
mailing list