Compare commits

..

349 commits

Author SHA1 Message Date
Laurent Pinchart
afd9890b7b libcamera: delayed_controls: Inherit from Object class
A second use-after-free bug related to signals staying connected after
the receiver DelayedControls instance gets deleted has been found, this
time in the simple pipeline handler. Fix the issue once and for all by
making the DelayedControls class inherit from Object. This will
disconnect signals automatically upon deletion of the receiver.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Stanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
Tested-by: Isaac Scott <isaac.scott@ideasonboard.com>
Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-11 12:25:46 +01:00
Umang Jain
fb72083975 camera: Fix spell error
Correct 'CameraConfigutation' spell error to 'CameraConfiguration'.

Signed-off-by: Umang Jain <uajain@igalia.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-08 14:10:14 +01:00
Naushir Patuck
29a88d85b7 libcamera: controls: Use nanoseconds units for FrameWallClock
Use nanoseconds for the FrameWallClock control to match the units for
other timestamp controls, including SensorTimestamp.

Update the RPi pipeline handlers to match the new nanoseconds units when
converting from SensorTimestamp to FrameWallClock.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-08 11:18:58 +01:00
Naushir Patuck
a437212753 libcamera: controls: Remove hyphenation in control description text
Remove the hyphenation in "micro-seconds" in the description for the
ExposureTime control to match the rest of the document.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-08 11:18:47 +01:00
Nick Hollinghurst
e6fb24ffdb ipa: rpi: Fix bug in AfState reporting
A previous change introduced a bug in which it reported AfStateIdle
when idle in Auto mode, when it should continue to report the most
recent AF cycle's outcome (AfStateFocused or AfStateFailed).

Also fix the Pause method so it won't reset state to AfStateIdle
when paused in Continuous AF mode (to match documented behaviour).

Fixes: ea5f451c56 ("ipa: rpi: controller: AutoFocus bidirectional scanning")
Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Tested-by: David Plowman <david.plowman@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-08 11:18:17 +01:00
Harvey Yang
525325440b V4L2VideoDevice: Call FrameBuffer::Private::cancel() in streamOff()
At the moment `V4L2VideoDevice::streamOff()` sets
`FrameBuffer::Private`'s metadata directly, while that's equivalent to
calling `FrameBuffer::Private::cancel()`. To ease code tracing, this
patch replace the manual modification with the function call.

Signed-off-by: Harvey Yang <chenghaoyang@chromium.org>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Umang Jain <uajain@igalia.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-08 11:18:12 +01:00
Christian Rauch
17eed522e8 subprojects: libpisp: Update to 1.2.1
Update the libpisp wrap to use the latest 1.2.1 release which silences
an 'unused-parameter' warning.

Bug: https://github.com/raspberrypi/libpisp/pull/43
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Christian Rauch <Rauch.Christian@gmx.de>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-07-08 13:05:54 +03:00
Nick Hollinghurst
619da07f73 ipa: rpi: Update IMX708 camera tuning files for AutoFocus changes
Explicitly add new parameters: "retrigger_ratio", "retrigger_delay",
"check_for_ir". Tweak other parameters to suit algorithm changes.
(Though existing tuning files should still work acceptably.)

Add AfSpeedFast parameters for the Raspberry Pi V3 standard lens.

Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-03 10:24:33 +01:00
Nick Hollinghurst
ea5f451c56 ipa: rpi: controller: AutoFocus bidirectional scanning
To reduce unnecessary lens movements, allow the CDAF-based
search procedure to start from either end of the range;
or if not near an end, from the current lens position.

This sometimes requires a second coarse scan, if the first
one started in the middle and did not find peak contrast.

Shorten the fine scan from 5 steps to 3 steps; allow fine scan
to be omitted altogether when "step_fine": 0 in the tuning file.

Move updateLensPosition() out of startProgrammedScan() to avoid
calling it more than once per iteration.

Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-03 10:24:32 +01:00
Nick Hollinghurst
686f88707c ipa: rpi: controller: Autofocus to use AWB statistics; re-trigger
Analyse AWB statistics: used both for scene change detection
and to detect IR lighting (when a flag is set in the tuning file).

Option to suppress PDAF altogether when IR lighting is detected.

Rather than being based solely on PDAF "dropout", allow a scan to
be (re-)triggered whenever the scene changes and then stabilizes,
based on contrast and average RGB statistics within the AF window.

Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-03 10:24:32 +01:00
Nick Hollinghurst
3d44987bc6 ipa: rpi: controller: AutoFocus tweak earlyTerminationByPhase()
Increase threshold for ETBP, from "confEpsilon" to "confThresh".
Correct sign test to take account of pdafGain sign (typically -ve).
Reduce allowed extrapolation range, but relax the check in the
case of Continuous AF, when we go back into the PDAF closed loop.

Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-03 10:24:32 +01:00
Nick Hollinghurst
429a5ab48f ipa: rpi: controller: Autofocus CAF/PDAF stability tweak
When in Continuous AF mode using PDAF, only move the lens when
phase has had the same sign for at least 4 frames. This reduces
lens wobble in e.g. noisy conditions.

Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-03 10:24:32 +01:00
Nick Hollinghurst
0fa2b05a86 ipa: rpi: controller: AutoFocus weighting tweak
In getPhase(), stop using different weights for sumWc and sumWcp.
This should improve linearity e.g. in earlyTerminationByPhase().
Phases are slightly larger but confidence values slightly reduced.

Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-03 10:24:32 +01:00
Nick Hollinghurst
a283287fbf ipa: rpi: controller: Improve findPeak() function in AF algorithm
Improve quadratic peak fitting in findPeak(). The old approximation
was good but only valid when points were equally spaced and the
MAX was not at one end of the series.

Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-03 10:24:32 +01:00
Nick Hollinghurst
30114cadd8 ipa: rpi: Defer initialising AF LensPosition ControlInfo and value
This fixes two small bugs:

We previously populated LensPosition's ControlInfo with hard-coded
values, ignoring the tuning file. Now we query the AfAlgorithm to
get limits (over all AF ranges) and default (for AfRangeNormal).

We previously sent a default position to the lens driver, even when
a user-specified starting position would follow. Defer doing this,
to reduce unnecessary lens movement at startup (for some drivers).

Bug: https://bugs.libcamera.org/show_bug.cgi?id=258
Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-07-03 10:24:26 +01:00
Barnabás Pőcze
6b5cc1c92a libcamera: pipeline: uvcvideo: Handle controls during startup
Process the control list passed to `Camera::start()`, and set
the V4L2 controls accordingly.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Umang Jain <uajain@igalia.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-07-02 10:26:41 +02:00
Naushir Patuck
5f94209b1d pipeline: rpi: Fix for enumerating the media graphs
When there are multiple entities between the sensor and CFE device (e.g.
a serialiser and deserialiser or multiple mux devices), the media graph
enumeration would work incorrectly and report that the frontend entity
was not found. This is because the found flag was stored locally in a
boolean and got lost in the recursion.

Fix this by explicitly tracking and returning the frontend found flag
through the return value of enumerateVideoDevices(). This ensures the
flag does not get lost through nested recursion.

This flag can also be used to fail a camera registration if the frontend
is not found.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Umang Jain <uajain@igalia.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-07-01 02:10:34 +03:00
Barnabás Pőcze
35ee8752b7 libcamera: pipeline: uvcvideo: Silently ignore AeEnable
The `AeEnable` control is handled in `Camera::queueRequest()` but it
still reaches the pipeline handler because a single element cannot be
removed from a `ControlList`. So ignore it silently.

Fixes: ffcecda4d5 ("libcamera: pipeline: uvcvideo: Report new AeEnable control as available")
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-06-30 14:46:19 +02:00
Umang Jain
e9528306f2 camera_sensor: Expand on computeTransform() documentation
The description for computeTransform() when the desired orientation
cannot be achieved, can be expanded a further bit, to clearly report
that orientation will be adjusted to native camera sensor mounting
rotation.

Signed-off-by: Umang Jain <uajain@igalia.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-26 16:20:53 +03:00
Barnabás Pőcze
a29c53f6a6 meson: Use libyaml wrap file from wrapdb
Use the libyaml wrap file from the meson wrapdb instead of
creating the wrap file manually and using the cmake module.
This provides better integration with meson, such as the
`force_fallback_for` built-in option.

This is also needed because the upstream CMakeLists.txt is
out of date, failing with a sufficiently new cmake version:

    CMake Error at CMakeLists.txt:2 (cmake_minimum_required):
    Compatibility with CMake < 3.5 has been removed from CMake.

The above is nonetheless addressed by https://github.com/yaml/libyaml/pull/314,
but the project seems a bit inactive at the moment.

The wrap file was added using `meson wrap install libyaml`,
and it can be updated using `meson wrap update libyaml`.

`default_library=static` is used to match the behaviour of the
previously used cmake build. `werror=false` needs to be set
because libyaml does not compile without warnings, and that
would abort the build process otherwise.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-26 14:01:19 +02:00
Kieran Bingham
5f4d2ac935 libcamera: controls: Revert incorrect SPDX removal
In commit 6a09deaf7d ("controls: Add FrameWallClock control") the
existing SPDX was accidentally removed, likely from a rebase operation
at some point.

Unfortunately as this patch had already collected Reviewed-by tags, the
surruptious removal wasn't noticed until after it was merged.

Re-insert the existing SPDX and copyright banner as the header to the
control definitions file.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-26 12:59:03 +01:00
Stefan Klug
0dfb052fbd libcamera: base: Fix log level parsing when multiple categories are
listed

For a list of log levels like LIBCAMERA_LOG_LEVELS="CatA:0,CatB:1" only
the severity of the last entry is correctly parsed.

Due to the change of level to a string_view in 24c2caa1c1 ("libcamera:
base: log: Use `std::string_view` to avoid some copies") the level is no
longer necessarily null terminated as it is a view on the original data.

Replace the check for a terminating null by a check for the end position
to fix the issue.

Fixes: 24c2caa1c1 ("libcamera: base: log: Use `std::string_view` to avoid some copies")
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-23 16:40:43 +02:00
Stefan Klug
8ea3ef083f libcamera: test: Add a failing test for the log level parser
Log level parsing doesn't always work as expected.  Add a failing test
for that.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-23 16:39:47 +02:00
Laurent Pinchart
c19047dfdf gstreamer: Use std::exchange() instead of g_steal_pointer()
g_steal_pointer) only preserves the type since glib 2.68, requiring
casts. Use std::exchange() instead.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
2025-06-23 02:30:47 +03:00
Laurent Pinchart
02a3b436c4 ipa: rkisp1: Move Sharpness control creation to Filter algorithm
The Sharpness control is used solely by the Filter algorithm. Create it
there, to avoid exposing it to applications when the algorithm is
disabled.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-06-23 02:30:47 +03:00
David Plowman
1537da7442 pipeline: rpi: Add wallclock timestamp support
A ClockRecovery object is added for derived classes to use, and
wallclock timestamps are copied into the request metadata for
applications.

Wallclock timestamps are derived corresponding to the sensor
timestamp, and made available to the base pipeline handler class and
to IPAs, for both vc4 and pisp platforms.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-19 11:12:26 +01:00
David Plowman
1d1ba78b45 controls: Add camera synchronisation controls for Raspberry Pi
New controls are added to control the camera "sync" algorithm, which
allows different cameras to synchronise their frames. For the time
being, the controls are Raspberry Pi specific, though this is expected
to change in future.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-19 11:12:26 +01:00
David Plowman
2a4e347dfe libcamera: Add ClockRecovery class to generate wallclock timestamps
The ClockRecovery class takes pairs of timestamps from two different
clocks, and models the second ("output") clock from the first ("input")
clock.

We can use it, in particular, to get a good wallclock estimate for a
frame's SensorTimestamp.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-19 11:12:26 +01:00
David Plowman
6a09deaf7d controls: Add FrameWallClock control
Add a FrameWallClock control that reports the same moment as the
frame's SensorTimestamp, but in wallclock units.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-19 11:12:26 +01:00
Hou Qi
4a277906a4 gstreamer: Fix libcamerasrc responding latency before setting caps
Whenever a downstream element queries latency, libcamerasrc will always reply,
even though it has not yet determined the latency.

However some downstream elements (e.g. glvideomixer/aggregator) will query the
latency before libcamerasrc sets the caps. When these elements get the latency,
they will start the caps negotiation. Since libcamerasrc has not yet determined
caps, invalid negotiation is performed and workflow is disrupted.

So, set latency to 'GST_CLOCK_TIME_NONE' during initialization, and reply to the
query after libcamerasrc confirms the latency. At this time, libcamerasrc has also
completed caps negotiation and downstream elements work fine.

In addition, every time the src pad task stops, we reset the latency to
GST_CLOCK_TIME_NONE to ensure that when next time task starts, the downstream
elements can generate out buffers after receiving the effective latency.

Signed-off-by: Hou Qi <qi.hou@nxp.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-19 01:50:38 +01:00
Barnabás Pőcze
b4c92a61bf ipa: rpi: Initialize enum controls with a list of values
This is how uvcvideo and rkisp1 do it. See ee918b370a
("ipa: rkisp1: agc: Initialize enum controls with a list of values")
for the motivation. In summary, having a list of values is used as a sign
that the control is an enum in multiple places (e.g. `cam`, `camshark`).

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-17 10:59:12 +02:00
Laurent Pinchart
b3ff75d758 gstreamer: Replace NULL with nullptr
Usage of NULL has slowly crept in the libcamerasrc sources. Replace it
with nullptr.

Reported-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2025-06-17 01:01:31 +03:00
Laurent Pinchart
a8f90517e0 gstreamer: Drop incorrect unref on caps
The caps object passeed to the gst_libcamera_create_video_pool()
function is managed as a g_autoptr() in the caller. The function doesn't
acquire any new reference, so it shouldn't call gst_caps_unref(). Fix
it.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2025-06-17 01:01:29 +03:00
Laurent Pinchart
772b06bd8c gstreamer: Fix leak of GstQuery and GstBufferPool in error path
The gst_libcamera_create_video_pool() function leaks a GstQuery instance
and a GstBufferPool instance in an error path. Fix the leaks with
g_autoptr().

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
2025-06-17 01:01:26 +03:00
Laurent Pinchart
f7c4fcd301 gstreamer: Rename variable in gst_libcamera_create_video_pool()
Now that the code is isolated in a function, the video_pool variable in
gst_libcamera_create_video_pool() can be renamed to pool without
clashing with another local variable. Do so to reduce line length.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2025-06-17 01:01:23 +03:00
Laurent Pinchart
613202b809 gstreamer: Reduce indentation in gst_libcamera_create_video_pool()
Now that video pool creation is handled by a dedicated function, the
logic can be simplified by returning early instead of nesting scopes. Do
so to decrease indentation and improve readability, and document the
implementation of the function with comments.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2025-06-17 01:01:20 +03:00
Laurent Pinchart
3b68207789 gstreamer: Factor out video pool creation
The gst_libcamera_src_negotiate() function uses 5 indentation levels,
causing long lines. Move video pool creation to a separate function to
increase readability.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2025-06-17 01:01:17 +03:00
Laurent Pinchart
04e7823eb2 gstreamer: Document improvements when updating minimum GStreamer version
A const_cast<> was recently added to fix a compilation issue with older
GStreamer versions. Add a comment to indicate it can be removed when
bumping the minimum GStreamer version requirement. While at it, also
document a possible future improvement in the same function, and wrap
long lines.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2025-06-17 01:01:14 +03:00
Antoine Bouyer
d3f3b95b64 pipeline: imx8-isi: Dynamically compute crossbar subdevice's first source.
So far, imx8-isi pipeline supports _symetrical_ crossbar, with same
amount of sink and source pads.

But for some other imx SoCs, such as i.MX8QM or i.MX95, crossbar is not
symetric anymore.

Since each crossbar source is already captured as a pipes_ vector entry,
we use pipes_ vector's size to compute 1st source index.

  "1st source index" = "total number of crossbar pads" - pipes_.count()

Signed-off-by: Antoine Bouyer <antoine.bouyer@nxp.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-17 00:44:05 +03:00
Antoine Bouyer
5621ac27a2 pipeline: imx8-isi: Fix match returned value in error case
The match() function returns a boolean type, while it could return int
in case of error when opening the capture file.

Fixes: 0ec982d210 ("libcamera: pipeline: Add IMX8 ISI pipeline")
Signed-off-by: Antoine Bouyer <antoine.bouyer@nxp.com>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-17 00:19:54 +03:00
Antoine Bouyer
5c8de8a08e pipeline: imx8-isi: Cosmetic changes
Change indentation to pass checkstyle script.

Fixes: 680cde6005 ("libcamera: imx8-isi: Split Bayer/YUV config generation")
Signed-off-by: Antoine Bouyer <antoine.bouyer@nxp.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-17 00:19:53 +03:00
Barnabás Pőcze
b544ce1c19 apps: common: image: Fix assertion
`plane` must be strictly less than the vector's size,
it cannot be equal to it.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-16 10:57:54 +02:00
Naushir Patuck
8d2cd0b5b8 ipa: rpi: Rename dropFrameCount_ to invalidCount_
Rename dropFrameCount_ to invalidCount_ to better reflect its use as
frames are no longer dropped by the pipeline handler.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-12 17:26:55 +01:00
Naushir Patuck
a402f9ebc1 pipeline: rpi: Remove ispOutputCount_ and ispOutputTotal_
With the drop frame logic removed from the pipeline handler, these
member variables and not used, so remove them.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-12 17:26:55 +01:00
Naushir Patuck
98d144fef3 pipeline: rpi: Remove disable_startup_frame_drops config option
With the previous change to not drop frames in the pipeline handler,
the "disable_startup_frame_drops" pipeline config option is not used.
Remove it, and throw a warning if the option is present in the YAML
config file.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-12 17:26:55 +01:00
Naushir Patuck
6cf9c4d34f pipeline: ipa: rpi: Split RPiCameraData::dropFrameCount_
Split the pipeline handler drop frame tracking into startup frames and
invalid frames, as reported by the IPA.

Remove the drop buffer handling logic in the pipeline handler. Now all
image buffers are returned out with the appropriate FrameStatus set
for startup or invalid frames.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-12 17:26:54 +01:00
Naushir Patuck
b114c155a7 ipa: rpi: Replace dropFrameCount in the IPA -> PH interface
Replace the dropFrameCount parameter returned from ipa::start() to the
pipeline handler by startupFrameCount and invalidFrameCount. The former
counts the number of frames required for AWB/AGC to converge, and the
latter counts the number of invalid frames produced by the sensor when
starting up.

In the pipeline handler, use the sum of these 2 values to replicate the
existing dropFrameCount behaviour.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-12 17:26:54 +01:00
Naushir Patuck
c50eb1f04a libcamera: framebuffer: Add FrameMetadata::Status::FrameStartup
Add a new status enum, FrameStartup, used to denote that even though
the frame has been successfully captured, the IQ parameters set by the
IPA will cause the frame to be unusable and applications are advised to
not consume this frame. An example of this would be on a cold-start of
the 3A algorithms, and there will be large oscillations to converge to
a stable state quickly.

Additional, update the definition of the FrameError state to cover the
usage when the sensor is known to produce a number of invalid/error
frames after stream-on.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-12 17:26:54 +01:00
Barnabás Pőcze
8d168f3348 libcamera: process: Ensure that file descriptors are nonnegative
Return `-EINVAL` from `Process::start()` if any of the file descriptors
are negative as those most likely signal some kind of issue such as
missed error checking.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-09 15:26:11 +02:00
Barnabás Pőcze
fae2b506d7 libcamera: process: Return error if already running
Returning 0 when a running process is already managed can be confusing
since the parameters might be completely different, causing the caller
to mistakenly assume that the program it specified has been started.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-09 15:25:59 +02:00
Barnabás Pőcze
0a591eaf8c libcamera: process: Misc. cleanup around execv()
Firstly, get the number of arguments first, and use that to determine the
size of the allocation instead of retrieving it twice.

Secondly, use `const_cast` instead of a C-style cast when calling `execv()`.

Third, use `size_t` to match the type of `args.size()`.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-09 15:25:22 +02:00
Barnabás Pőcze
081554db34 libcamera: process: Disable copy/move
A `Process` object has address identity because a pointer to it is
stored inside the `ProcessManager`. However, copy/move special
methods are still generated by the compiler. So disable them to
avoid potential issues and confusion.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-06-09 15:25:18 +02:00
Barnabás Pőcze
633063e099 android: camera_device: Do not pass nullptr to Request::addBuffer()
The default argument already takes care of passing no fence to
`addBuffer()`, so there is no reason to specify `nullptr` explicitly.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-04 09:31:23 +02:00
Kieran Bingham
290d3f82e3 libcamera v0.5.1
The abi-compliance checker reports 100% compatibility in this release.
As such the SONAME is maintained at 0.5.

 Binary compatibility: 100%
 Source compatibility: 100%
 Total binary compatibility problems: 0, warnings: 0
 Total source compatibility problems: 0, warnings: 0

This release brings 93 commits with a large proportion of fixes and
cleanup againt earlier releases. Improvements have been made to the
Raspberry Pi Camera Tuning Tools, and the geometry, matrix and vector
class helpers have been expanded for greater reuse throughout the
project.

Notably for packagers - IPA modules now have their own subdirectory
which should prevent undesirable surrupticious error messages that would
occur if packagers choose to install the V4L2 adaptation layer in the
same folder as the IPA modules.

The RKISP1 can now adapt to more complex input pipelines, including
FPGAs and multiplexors, which has been beneficial for users on the
i.MX8MP, and the IPA algorithms for i.MX8MP and RKISP1 continue to get
improvements.

The software ISP has a new Saturation control (available when the CCM is
enabled).

The Documentation and pipeline handler writers guide has been
re-reviewed and cleaned up.

On the application and test side, lc-compliance now includes
multi-stream tests, and cam has extended support for display formats and
now prevents issues on non-display GPUs when rendering direct to DRM.

Contributors:

    36  Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
    15  Stefan Klug <stefan.klug@ideasonboard.com>
     5  David Plowman <david.plowman@raspberrypi.com>
     5  Kieran Bingham <kieran.bingham@ideasonboard.com>
     5  Laurent Pinchart <laurent.pinchart@ideasonboard.com>
     4  Milan Zamazal <mzamazal@redhat.com>
     4  Quentin Schulz <quentin.schulz@cherry.de>
     3  Daniel Scally <dan.scally@ideasonboard.com>
     3  Paul Elder <paul.elder@ideasonboard.com>
     2  Hou Qi <qi.hou@nxp.com>
     2  Julien Vuillaumier <julien.vuillaumier@nxp.com>
     2  Naushir Patuck <naush@raspberrypi.com>
     2  Niklas Söderlund <niklas.soderlund@ragnatech.se>
     2  Pavel Machek <pavel@ucw.cz>
     1  Benjamin Mugnier <benjamin.mugnier@foss.st.com>
     1  Nícolas F. R. A. Prado <nfraprado@collabora.com>
     1  Sven Püschel <s.pueschel@pengutronix.de>

 108 files changed, 3359 insertions(+), 528 deletions(-)

Integration overview:

The following commits in this release relate to either a bug fix or an
improvement to an existing commit.

 - meson: Do not automatically build documentation if sphinx-build-3 is found
   - Fixes: aba567338b ("Documentation: Move all dependencies into features")
 - Revert "libcamera: rkisp1: Eliminate hard-coded resizer limits"
   - Fixes: 761545407c ("pipeline: rkisp1: Filter out sensor sizes not supported by the pipeline")
 - pipeline: rkisp1: Fix vblank delay
   - Fixes: f72c76eb6e ("rkisp1: Honor the FrameDurationLimits control")
 - utils: raspberrypi: ctt: Fix NaNs in lens shading tables
   - Bug: https://github.com/raspberrypi/libcamera/issues/254
 - utils: raspberrypi: ctt: Fix NaNs in chromatic aberration tables
   - Bug: https://github.com/raspberrypi/libcamera/issues/254
 - utils: raspberrypi: ctt: Fix integer division error calculating LSC cell size
   - Bug: https://github.com/raspberrypi/libcamera/issues/260
 - apps: qcam: Push the viewfinder role to vector
   - Fixes: ee2b011b65 ("apps: cam: Try raw role if default viewfinder role fails")
 - ipa: Move IPA installations to a subdir
   - Bug: https://bugs.libcamera.org/show_bug.cgi?id=268
 - ipa: rkisp1: algorithms: awb: Fix wrong colour temperature reporting
   - Fixes: b60bd37b1a ("ipa: rkisp1: Move calculation of RGB means into own function")
 - ipa: rkisp1: ccm/lsc: Fix CCM/LSC based on manual color temperature
   - Fixes: 0230880954 ("ipa: rkisp1: awb: Implement ColourTemperature control")
 - libcamera: controls: Fix `ControlInfoMap::count(unsigned int)`
   - Fixes: 76b9923e55 ("libcamera: controls: Avoid exception in ControlInfoMap count() and find()")
 - apps: cam: capture_script: Disallow arrays of strings
   - Fixes: b35f04b3c1 ("cam: capture_script: Support parsing array controls")
 - libcamera: matrix: Fix compilation error in inverse() function
   - Fixes: 6287ceff5a ("libcamera: matrix: Add inverse() function")
 - ipa: rpi: controller: rpi: Fix colour gain typo in AGC
   - Fixes: 29892f1c56 ("ipa: libipa: colour: Use the RGB class to model RGB values")

And the following updates have been made in this release, grouped by category:

core:
 - meson: Make the default value of "documentation" feature explicit
 - meson: Do not automatically build documentation if sphinx-build-3 is found
 - libcamera: request: Avoid double map lookup
 - utils: rkisp1: gen-csc-table: Support printing CCM in decimal
 - libcamera: ipa_module: Avoid unnecessary copy when getting signature
 - libcamera: controls: Disallow arrays of arrays
 - libcamera: media_device: Add helper to return matching entities
 - libcamera: internal: Add MediaPipeline helper
 - libcamera: stream: Add color space to configuration string representation
 - README.rst: remove unnecessary dependency for qcam
 - libcamera: v4l2_videodevice: Log buffer count on allocation error
 - libcamera: matrix: Replace SFINAE with static_asserts
 - libcamera: matrix: Make most functions constexpr
 - libcamera: matrix: Add a Span based constructor
 - libcamera: vector: Add a Span based constructor
 - libcamera: matrix: Add inverse() function
 - libcamera: matrix: Extend multiplication operator to heterogenous types
 - libcamera: vector: Extend matrix multiplication operator to heterogenous types
 - libcamera: controls: Fix `ControlInfoMap::count(unsigned int)`
 - utils: codegen: Make users depend on `controls.py` in meson
 - libcamera: matrix: Fix compilation error in inverse() function
 - libcamera: sensor: Fix the gain delay for IMX283
 - treewide: Do not use `*NameValueMap` for known values
 - utils: codegen: ipc: Use `any()` instead of `len([]) > 0`
 - utils: codegen: ipc: Remove `namespace` argument
 - utils: codegen: ipc: Add `deserializer()` function
 - utils: codegen: ipc: Log error code when remote call fails
 - utils: codegen: ipc: Simplify `return` statements
 - libcamera: ipa_data_serializer: Remove some vector `reserve()` calls
 - libcamera: mali-c55: Remove tpgCodes_
 - libcamera: mali-c55: Remove tpgSizes_ member from MaliC55CameraData
 - libcamera: process: Use _exit in child process
 - libcamera: process: Pass stderr and reserve stdin and stdout fds
 - guides: pipeline-handler: Update name of pipeline handler stop function
 - libcamera: mali-c55: Fix error paths in ::init()

pipeline:
 - libcamera: software_isp: Add a clarification comment to AWB
 - libcamera: pipeline: uvcvideo: Expose `Gamma` control
 - libcamera: software_isp: Fix CCM multiplication
 - libcamera: pipeline: virtual: Fix typo in log message
 - libcamera: pipeline: imx8-isi: Remove unused variable
 - pipeline: rkisp1: Fix vblank delay
 - libcamera: pipeline: rkisp1: Convert to use MediaPipeline
 - libcamera: pipeline: uvcvideo: Report new AeEnable control as available
 - ipu3: cio2: Remove unused function definition
 - libcamera: software_isp: Add saturation control
 - Revert "libcamera: rkisp1: Eliminate hard-coded resizer limits"

apps:
 - apps: lc-compliance: Support multiple streams in helpers
 - apps: lc-compliance: Add multi-stream tests
 - apps: cam: capture_script: Simplify bool array parsing
 - gstreamer: Fixate colorimetry field during caps negotiation
 - apps: cam: Try raw role if default viewfinder role fails
 - apps: qcam: Push the viewfinder role to vector
 - py: Set `PYTHONPATH` in devenv
 - apps: cam: sdl_texture: Take list of buffers in span
 - apps: cam: sdl_texture: Drop `&rect_` from `SDL_Update{NV,}Texture()` call
 - apps: cam: sdl_texture: Add `SDLTexture1Plane`
 - apps: cam: sdl_sink: Support more single-plane formats
 - gstreamer: Add GstVideoMeta support
 - apps: cam: capture_script: Disallow arrays of strings
 - apps: cam: Skip non-display GPUs

ipa:
 - utils: ipc: Do not duplicate signals in proxy object
 - utils: ipc: Do not define variables in signal handler up front
 - ipa: rpi: common: Avoid warnings when AeEnable control is used
 - ipa: rpi: awb: Remove "fast" parameter
 - ipa: Move IPA installations to a subdir
 - ipa: rkisp1: awb: Declare ControlInfo in AWB
 - ipa: rkisp1: awb: Ignore empty AWB statistics
 - ipa: rkisp1: Refactor automatic/manual structure in IPAActiveState
 - ipa: rkisp1: algorithms: awb: Fix wrong colour temperature reporting
 - ipa: rkisp1: ccm/lsc: Fix CCM/LSC based on manual color temperature
 - ipa: rkisp1: Implement manual ColourCorrectionMatrix control
 - libipa: awb: Make result of gainsFromColourTemp optional
 - ipa: rkisp1: Damp color temperature regulation
 - ipa: rkisp1: awb: Take the CCM into account for the AWB gains calculation
 - ipa: rkisp1: awb: Avoid division by zero
 - ipa: rpi: controller: rpi: Fix colour gain typo in AGC
 - ipa: rpi: Add tuning for IMX283
 - ipa: rpi: Prevent segfault if AGC algorithm is absent

tuning:
 - utils: raspberrypi: ctt: Fix NaNs in lens shading tables
 - utils: raspberrypi: ctt: Fix NaNs in chromatic aberration tables
 - utils: raspberrypi: ctt: Fix integer division error calculating LSC cell size

documentation:
 - Documentation: guides: pipeline-handler: Fix camera creation
 - Documentation: guides: pipeline-handler: Fix property list file name
 - Documentation: guides: pipeline-handler: Fix configuration creation
 - Documentation: guides: pipeline-handler: Fix `Camera::create()` link
 - Documentation: guides: pipeline-handler: Simplify format collection
 - Documentation: guides: pipeline-handler: Query pixel formats once
 - Documentation: guides: application-developer: Remove unnecessary argument
 - Documentation: Fix `INCLUDE_PATH` doxygen configuration option
 - doc: Mention right meson version
 - doc: document libtiff dependency for cam

test:
 - test: Add minimal test for Matrix
 - lc-compliance: Move camera setup to CameraHolder class

Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-01 23:57:29 +01:00
Barnabás Pőcze
a8bc540653 Documentation: Fix INCLUDE_PATH doxygen configuration option
libcamera header files should be included using the `libcamera/...` prefix.
However, `INCLUDE_PATH` is currently set to `@TOP_SRCDIR@/include/libcamera`
meaning that doxygen, when encountering `libcamera/x.h`, will try to open
`@TOP_SRCDIR@/include/libcamera/libcamera/x.h`, which is not the correct
path.

Fix that by using `@TOP_{BUILD,SRC}DIR@/include`. This removes the extra
`libcamera` component from the path and adds the corresponding directory
from the build directory as well since that is an implicit include
directory added by meson.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-01 23:11:54 +01:00
Milan Zamazal
59ac34b728 libcamera: software_isp: Add saturation control
Saturation control is added on top of the colour correction matrix.  A
method of saturation adjustment that can be fully integrated into the
colour correction matrix is used.  The control is available only if Ccm
algorithm is enabled.

The control uses 0.0-2.0 value range, with 1.0 being unmodified
saturation, 0.0 full desaturation and 2.0 quite saturated.

The saturation is adjusted by converting to Y'CbCr colour space,
applying the saturation value on the colour axes, and converting back to
RGB.  ITU-R BT.601 conversion is used to convert between the colour
spaces, for no particular reason.

The colour correction matrix is applied before gamma and the given
matrix is suitable for such a case.  Alternatively, the transformation
used in libcamera rpi ccm.cpp could be used.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-01 23:08:01 +01:00
Daniel Scally
e342f050c2 libcamera: mali-c55: Fix error paths in ::init()
In the MaliC55CameraData::init() function there are two places that
return values they shouldn't; the ret variable is returned after
checking a pointer is not null instead of an explicit -ENODEV and later
the boolean value false is returned on failure instead of the error
value returned by V4L2Subdevice::open() - fix both problems.

Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-01 22:53:47 +01:00
Niklas Söderlund
fabee6055f guides: pipeline-handler: Update name of pipeline handler stop function
Since commit f6b6f15b54 ("libcamera: pipeline: Introduce
stopDevice()") the stop function needed to be implemented by pipeline
handlers was renamed to stopDevice().

Update the pipeline handler writers guide to match this.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Jai Luthra <jai.luthra@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-01 22:41:32 +01:00
Niklas Söderlund
4b5856533a ipu3: cio2: Remove unused function definition
The private function cio2BufferReady is defined but not implemented or
used, remove it for the class definition.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-01 22:30:49 +01:00
Milan Zamazal
663ab2ee8e apps: cam: Skip non-display GPUs
Device::openCard() in the cam DRM helpers looks for a /dev/dri/card*
device that can be opened and that doesn't fail when asked about
DRM_CAP_DUMB_BUFFER capability (regardless whether the capability is
supported by the device).

There can be matching devices that are not display devices.  This can
lead to selection of such a device and inability to use KMS output with
the `cam' application.  The ultimate goal is to display something on the
device and later the KMS sink will fail if there is no connector
attached to the device (although it can actually fail earlier, when
trying to set DRM_CLIENT_CAP_ATOMIC capability if this is not
supported).

Let's avoid selecting devices without connectors, CRTCs or encoders.
The added check makes the original check for DRM_CAP_DUMB_BUFFER API
most likely unnecessary, let's remove it.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Mattijs Korpershoek <mkorpershoek@kernel.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-05-30 12:26:53 +03:00
Benjamin Mugnier
1ee330c058 ipa: rpi: Prevent segfault if AGC algorithm is absent
Even without AGC definition in the tuning file, the application would
still dereference agc unconditionally, leading to a segmentation fault
if AGC is absent.
This is relevant for sensors already providing AGC/AEC by themselves.
Check if AGC is present prior to setting maximum exposure time.

Signed-off-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> # RPi4 + imx708_wide
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-29 15:00:09 +01:00
Julien Vuillaumier
5b7c83d8cc libcamera: process: Pass stderr and reserve stdin and stdout fds
When a child process is started from Process::start(), the file
descriptors inherited from the parent process are closed, except
the ones explicitly listed in the fds[] argument.

One issue is that the file descriptors for stdin, stdout and stderr
being closed, the subsequent file descriptors created by the child
process will reuse the values 0, 1 and 2 that are now available.
Thus, usage of printf(), assert() or alike may direct its output
to the new resource bound to one of these reused file descriptors.
The other issue is that the child process can no longer log on
the console because stderr has been closed.

To address the 2 issues, Process:start() is amended as below:
- Child process inherits from parent's stderr fd in order to share
the same logging descriptor
- Child process stdin, stdout and stderr fds are bound to /dev/null
if not inherited from parent. That is to prevent those descriptors
to be reused for any other resource, that could be corrupted by
the presence of printf(), assert() or alike.

Signed-off-by: Julien Vuillaumier <julien.vuillaumier@nxp.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-29 12:30:12 +01:00
Julien Vuillaumier
32905fdd0b libcamera: process: Use _exit in child process
Use _exit() in child process in case of execv() error. That is to
avoid interfering with the parent process as exit() may call its
atexit() handlers and flush its io buffers.

Signed-off-by: Julien Vuillaumier <julien.vuillaumier@nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-29 12:30:12 +01:00
Daniel Scally
f58077f073 libcamera: mali-c55: Remove tpgSizes_ member from MaliC55CameraData
The tpgSizes_ vector is only used within the initTPGData() function.
Drop it and use a local variable instead.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-05-29 11:56:23 +01:00
Daniel Scally
b55943714f libcamera: mali-c55: Remove tpgCodes_
MaliC55CameraData stores a vector of the TPG's mbus codes (if the
camera in question is a TPG). This is never used - remove it.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-05-29 11:56:23 +01:00
Barnabás Pőcze
4709f8442b libcamera: ipa_data_serializer: Remove some vector reserve() calls
`appendPOD()` does a single insertion, so if only a single `appendPOD()`
will be called on a vector before returning, then calling `reserve()`
is not that useful, so remove it.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-05-27 11:10:23 +02:00
Barnabás Pőcze
e633d85be9 utils: codegen: ipc: Simplify return statements
Returning an expression of type `void` from a function returning `void`
is legal, so do not handle those cases specially.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-05-27 11:10:23 +02:00
Barnabás Pőcze
4adefc100d utils: codegen: ipc: Log error code when remote call fails
The error code can be useful in diagnosing the underlying issue,
so log that as well, not just the existence of the issue.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-05-27 11:10:23 +02:00
Barnabás Pőcze
d58ccabab7 utils: codegen: ipc: Add deserializer() function
Add `deserializer()` in `serializer.tmpl` to have a single function
that generates all the necessary functions into the template specialization
like `serializer()`. This also avoids the duplication of some
conditional logic.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-05-27 11:10:23 +02:00
Barnabás Pőcze
0a1539a4f1 utils: codegen: ipc: Remove namespace argument
The `serializer()`, `deserializer_{fd,no_fd,simple}()` functions
take a string argument named "namespace", but they do not use it.
So remove the argument.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-05-27 11:10:23 +02:00
Barnabás Pőcze
d4ef160b1a utils: codegen: ipc: Use any() instead of len([]) > 0
Use `any()` with a generator expression instead of constructing
a list and checking its length.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-05-27 11:10:23 +02:00
Barnabás Pőcze
eecb270085 treewide: Do not use *NameValueMap for known values
When the value is known, do not look it up via the control's `NameValueMap`,
instead, just refer to the value directly.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-05-27 09:42:46 +02:00
Naushir Patuck
aca8b701ac libcamera: sensor: Fix the gain delay for IMX283
The IMX283 uses a gain delay of 1 instead of the current value of 2 as
defined in the sensor properties. Fix it.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Acked-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-23 11:52:31 +01:00
Naushir Patuck
eb9bb35d80 ipa: rpi: Add tuning for IMX283
Add calibrated tuning for the IMX283 sensor for pisp. Update the vc4
tuning file to match the new calibration.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Acked-by: Paul Elder <paul.elder@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-23 11:49:20 +01:00
David Plowman
ad5326c926 ipa: rpi: controller: rpi: Fix colour gain typo in AGC
A simple typo crept in where the red gain had been re-typed rather
than using the correct green gain. In particular, this was causing
very dark images for sensors that use large red gains, such as the
IMX477 outdoors.

Fixes: 29892f1c56 ("ipa: libipa: colour: Use the RGB class to model RGB values")
Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Tested-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-05-23 09:44:57 +02:00
Laurent Pinchart
516f365670 libcamera: matrix: Fix compilation error in inverse() function
Some gcc versions report uninitialized variable usage:

In member function ‘constexpr T& libcamera::Span<T, 4294967295>::operator[](size_type) const [with T = unsigned int]’,
    inlined from ‘void libcamera::matrixInvert(Span<const T>, Span<T, 4294967295>, unsigned int, Span<T, 4294967295>, Span<unsigned int>)::MatrixAccessor::swap(unsigned int, unsigned int) [with T = float]’ at ../../src/libcamera/matrix.cpp:194:13,
    inlined from ‘bool libcamera::matrixInvert(Span<const T>, Span<T, 4294967295>, unsigned int, Span<T, 4294967295>, Span<unsigned int>) [with T = float]’ at ../../src/libcamera/matrix.cpp:255:14:
../../include/libcamera/base/span.h:362:76: error: ‘row’ may be used uninitialized [-Werror=maybe-uninitialized]
  362 |         constexpr reference operator[](size_type idx) const { return data()[idx]; }
      |                                                                      ~~~~~~^
../../src/libcamera/matrix.cpp: In function ‘bool libcamera::matrixInvert(Span<const T>, Span<T, 4294967295>, unsigned int, Span<T, 4294967295>, Span<unsigned int>) [with T = float]’:
../../src/libcamera/matrix.cpp:232:30: note: ‘row’ was declared here
  232 |                 unsigned int row;
      |                              ^~~

This is a false positive. Fix it by initializing the variable when
declaring it.

Fixes: 6287ceff5a ("libcamera: matrix: Add inverse() function")
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Tested-by: Milan Zamazal <mzamazal@redhat.com>
2025-05-22 19:04:15 +02:00
Barnabás Pőcze
d997e97512 utils: codegen: Make users depend on controls.py in meson
Currently, modifying `controls.py` does not make those build targets dirty
that use a script that includes it (e.g. `gen-controls.py`) because meson
has no knowledge of this dependency. Add `depend_files` to each
`custom_target()` invocation to fix this.

Ideally it would be possible to attach this dependency to `gen_controls`,
`gen_gst_controls`, etc. objects themselves, so that repetition is
avoided, but this does not seem possible at the moment.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Acked-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-22 13:16:07 +02:00
Barnabás Pőcze
702af1a1d0 apps: cam: capture_script: Disallow arrays of strings
The current `ControlValue` mechanism does not support arrays
of strings, the assignment in the removed snippet will in fact
trigger an assertion failure in `ControlValue::set()` because
`sizeof(std::string) != ControlValueSize[ControlTypeString]`.

Fixes: b35f04b3c1 ("cam: capture_script: Support parsing array controls")
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-22 13:16:07 +02:00
Barnabás Pőcze
ffcecda4d5 libcamera: pipeline: uvcvideo: Report new AeEnable control as available
The `AeEnable` control is handled by the `Camera` class directly, but it
still has to be added because `ControlInfoMap`s are not easily modifiable.

See 338ba00e7a ("ipa: rkisp1: agc: Report new AeEnable control as available")
for more details and a similar change in rkisp1.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-22 12:34:54 +02:00
Barnabás Pőcze
efdbe39698 libcamera: controls: Fix ControlInfoMap::count(unsigned int)
The two overloads of `find()` and `at()` have the same behaviour
regardless of the argument type: `unsigned int` or `const ControlId *`.
However, `count()` is not so because `count(unsigned int)` only checks
the `ControlIdMap`, and it does not check if the given id is actually
present in the map storing the `ControlInfo` objects.

So `count()` returns 1 for every control id that is present in the
associated `ControlIdMap` regardless of whether there is an actual
entry for the `ControlId` associated with the given numeric id.

Fix that by simply using `find()` to determine the return value.

Fixes: 76b9923e55 ("libcamera: controls: Avoid exception in ControlInfoMap count() and find()")
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-22 11:22:34 +02:00
Stefan Klug
969df3db31 ipa: rkisp1: awb: Avoid division by zero
As the gains can also be specified manually, the regulation can run into
numeric instabilities by dividing by near zero. Mitigate that by
applying a small minium value.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 11:20:08 +02:00
Stefan Klug
7991293cec ipa: rkisp1: awb: Take the CCM into account for the AWB gains calculation
The AWB measurements are taken after the CCM. This can be seen by
enabling debug logging on AWB, disabling AWB (stats will still be
processed) and manually chaning the CCM.

This means that the estimated colour temperature and the corresponding
CCM also lead to changed rgbMeans which in turn leads to oscillations.
Fix that by applying the inverse transform on the rgbMeans.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 11:20:08 +02:00
Stefan Klug
71b680c863 ipa: rkisp1: Damp color temperature regulation
Damp the regulation of the color temperature with the same factor as the
gains.  Not damping the color temperature leads to visible flicker, as
the CCM changes too much.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 11:20:08 +02:00
Stefan Klug
c699d26573 libipa: awb: Make result of gainsFromColourTemp optional
In the grey world AWB case, if no colour gains are contained in the
tuning file, the colour gains get reset to 1 when the colour temperature
is set manually. This is unexpected and undesirable. Allow the
gainsFromColourTemp() function to return a std::nullopt to handle that
case.

While at it, remove an unnecessary import from rkisp1/algorithms/awb.h.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 11:20:08 +02:00
Stefan Klug
66e9604684 ipa: rkisp1: Implement manual ColourCorrectionMatrix control
Add a manual ColourCorrectionMatrix control. This was already discussed
while implementing manual colour temperature but was never implemented.
The control allows to manually specify the CCM when AwbEnable is false.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 11:20:07 +02:00
Stefan Klug
f1ac420eb1 ipa: rkisp1: ccm/lsc: Fix CCM/LSC based on manual color temperature
In RkISP1Awb::process(), the color temperature in the active state is
updated every time new statistics are available.  The CCM/LSC algorithms
use that value in prepare() to update the CCM/LSC. This is not correct
if the color temperature was specified manually and leads to visible
flicker even when AwbEnable is set to false.

To fix that, track the auto and manual color temperature separately in
active state. In Awb::prepare() the current frame context is updated
with the corresponding value from active state. Change the algorithms to
fetch the color temperature from the frame context instead of the active
state in prepare().

Fixes: 0230880954 ("ipa: rkisp1: awb: Implement ColourTemperature control")
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-05-20 11:16:36 +02:00
Stefan Klug
3fcc6b06c3 ipa: rkisp1: algorithms: awb: Fix wrong colour temperature reporting
In commit b60bd37b1a ("ipa: rkisp1: Move calculation of RGB means into
own function") the output of the current measured colour temperature as
metadata was incorrectly added. Remove it.

Fixes: b60bd37b1a ("ipa: rkisp1: Move calculation of RGB means into own function")
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 09:59:23 +02:00
Stefan Klug
5010b65a08 ipa: rkisp1: Refactor automatic/manual structure in IPAActiveState
Swap gains and automatic/manual in the IPAActiveState structure. This is
in preparation to adding another member, which is easier in the new
structure. The patch contains no functional changes.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-05-20 09:58:56 +02:00
Laurent Pinchart
1e67b96fb0 libcamera: vector: Extend matrix multiplication operator to heterogenous types
It is useful to multiply matrices and vectors of heterogeneous types, for
instance float and double. Extend the multiplication operator to support
this, avoiding the need to convert one of the operations. The type of the
returned vector is selected automatically to avoid loosing precision.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 09:49:09 +02:00
Laurent Pinchart
754798b664 libcamera: matrix: Extend multiplication operator to heterogenous types
It is useful to multiply matrices of heterogneous types, for instance
float and double. Extend the multiplication operator to support this,
avoiding the need to convert one of the matrices. The type of the
returned matrix is selected automatically to avoid loosing precision.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 09:49:09 +02:00
Stefan Klug
dacbcc7d77 test: Add minimal test for Matrix
Add a few tests for the Matrix class. This is not full fledged but at
least a starter.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 09:49:01 +02:00
Stefan Klug
6287ceff5a libcamera: matrix: Add inverse() function
For calculations in upcoming algorithm patches, the inverse of a matrix
is required. Add an implementation of the inverse() function for square
matrices.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 09:46:12 +02:00
Stefan Klug
bcba580546 libcamera: vector: Add a Span based constructor
When one wants to create a Vector from existing data, currently the only
way is via std::array. Add a Span based constructor to allow creation
from std::vectors and alike.

While at it, replace the manual loop with std::copy.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 09:46:12 +02:00
Stefan Klug
aca9042abd libcamera: matrix: Add a Span based constructor
When one wants to create a Matrix from existing data, currently the only
way is via std::array. Add a Span based constructor to allow creation
from vectors and alike.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 09:46:11 +02:00
Stefan Klug
5234e4936f libcamera: matrix: Make most functions constexpr
By zero-initializing the data_ member we can make most functions
constexpr which will come in handy in upcoming patches. Note that this
is due to C++17. In C++20 we will be able to leave data_ uninitialized
for constexpr.  The Matrix(std::array) version of the constructor can
not be constexpr because std::copy only became constexpr in C++20.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-20 09:46:11 +02:00
Stefan Klug
1d8a6db31c libcamera: matrix: Replace SFINAE with static_asserts
SFINAE is difficult to read and not needed in these cases. Replace it
with static_asserts. The idea came from [1] where it is stated:

"The use of enable_if seems misguided to me. SFINAE is useful for the
situation where we consider multiple candidates for something (overloads
or class template specializations) and try to choose the correct one,
without causing compilation to fail."

[1]: https://stackoverflow.com/questions/62109526/c-friend-template-that-use-sfinae

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
2025-05-20 09:46:11 +02:00
Stefan Klug
0069b9ceb1 ipa: rkisp1: awb: Ignore empty AWB statistics
When the AWB engine doesn't find a valid pixel because all pixels lie
outside the configured colour range it returns an AWB measurement value
of 255, 255, 255. This leaves the regulation in an unrecoverable state
noticeable by a completely green image. Fix that by skipping the AWB
calculation in case there were no valid pixels.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-19 15:35:47 +02:00
Hou Qi
848a3017b8 gstreamer: Add GstVideoMeta support
GStreamer video-info calculated stride and offset may differ from
those used by the camera.

For stride and offset mismatch, this patch adds video meta to buffer
if downstream supports VideoMeta through allocation query. Otherwise,
create a internal VideoPool using the caps, and copy video frame to
this system memory.

Signed-off-by: Hou Qi <qi.hou@nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Tested-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-19 09:28:19 +01:00
Barnabás Pőcze
e5442c3150 apps: cam: sdl_sink: Support more single-plane formats
With the newly introduced `SDLTexture1Plane` it is easy to handle
any single-plane format that has an SDL equivalent. So use it for
more YUV and RGB formats.

The mapping of RGB formats is not entirely straightforward because
`SDL_PIXELFORMAT_ZZZ...888...` defines a format where the order of
the components is endian dependent, while libcamera's `ZZZ...888...`
formats are derived from the matching DRM formats, and the RGB formats
in question are defined to be little-endian there. So the
endian-independent `SDL_PIXELFORMAT_{ZZZ24,ZZZZ32}` are used.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-05-15 17:41:36 +02:00
Barnabás Pőcze
b24cd12293 apps: cam: sdl_texture: Add SDLTexture1Plane
`SDLTextureYUYV` uses `SDL_PIXELFORMAT_YUY2`, which is a single plane
format. To support other single plane formats, replace `SDLTextureYUYV`
with `SDLTexture1Plane` that can be instantiated with an arbitrary SDL
pixel format and that uses `SDL_UpdateTexture()` to update the texture
using exactly a single plane.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-05-15 17:41:36 +02:00
Barnabás Pőcze
41b0997114 apps: cam: sdl_texture: Drop &rect_ from SDL_Update{NV,}Texture() call
If the entire texture is to be updated, there is no need to specify
the target area explicitly.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-05-15 17:41:36 +02:00
Barnabás Pőcze
02f60006cf apps: cam: sdl_texture: Take list of buffers in span
A non-owning span is sufficient, so use that instead of a vector.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-15 17:41:36 +02:00
Nícolas F. R. A. Prado
f3a12332f6 lc-compliance: Move camera setup to CameraHolder class
Different base classes can be used for different setups on tests, but
all of them will need to setup the camera for the test. To reuse that
code, move it to a separate CameraHolder class that is inherited by test
classes.

Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
2025-05-13 20:17:19 +02:00
Paul Elder
d01342f1dc ipa: rkisp1: awb: Declare ControlInfo in AWB
The ControlInfo information for AwbEnable and ColourGains are declared
and exposed in the top-level IPA. These should instead be exposed by the
AWB part of the IPA, as it doesn't make sense to support these controls
when AWB is disabled, for example.

Move the declaration of these controls out of the top-level IPA and into
AWB.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-05-13 11:57:24 +02:00
Kieran Bingham
37dccb4584 ipa: Move IPA installations to a subdir
IPAs are expected to live within a directory that is searched by the
IPAManager.  If other non-IPA so files are installed in the same
location, then the user may be presented with an error message reporting
that the module could not be parsed.

Move IPA modules to an ipa specific subdirectory to ensure we only parse
.so files that are expected to be IPA modules at load time.

Bug: https://bugs.libcamera.org/show_bug.cgi?id=268
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-12 16:13:06 +02:00
Barnabás Pőcze
54aeb0447c py: Set PYTHONPATH in devenv
If the python bindings are built, then set the `PYTHONPATH` environmental
variable in the meson devenv accordingly to make it easy to use.

  $ meson devenv -C build
  [libcamera] $ echo $PYTHONPATH
  /libcamera/build/src/py
  [libcamera] $ python
  Python 3.13.3 (main, Apr  9 2025, 07:44:25) [GCC 14.2.1 20250207] on linux
  Type "help", "copyright", "credits" or "license" for more information.
  >>> import libcamera
  >>> cm = libcamera.CameraManager.singleton()
  [...]
  [129:52:33.293860558] [4133380]  INFO Camera camera_manager.cpp:326 libcamera v0.5.0+169-7dbe74b5-dirty (2025-05-01)
  [...]

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-05-12 09:21:14 +02:00
Sven Püschel
fabfdd8559 libcamera: v4l2_videodevice: Log buffer count on allocation error
Log the actual and requested buffers count in case of a V4L2 buffers
allocation error, when the requested buffers count could not be
allocated.

Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-05-09 15:39:17 +02:00
Kieran Bingham
a799415017 apps: qcam: Push the viewfinder role to vector
In commit ee2b011b65 ("apps: cam: Try raw role if default viewfinder
role fails"), the viewfinder role is specified as the default if no role
is yet chosen.

This was unfortunately added by directly accessing the vector rather
than extending the size when the vector is empty. Fix the code to push
the default viewfinder role on to the back of the vector, increasing the
size appropriately.

Fixes: ee2b011b65 ("apps: cam: Try raw role if default viewfinder role fails")
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-08 10:51:11 +02:00
David Plowman
e0405b171e utils: raspberrypi: ctt: Fix integer division error calculating LSC cell size
The cell sizes must be cast to integers as the parameters that
were passed in may be floats.

Bug: https://github.com/raspberrypi/libcamera/issues/260
Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
Fixes: 36ba0e5515 ("utils: raspberrypi: ctt: Fix NaNs in lens shading tables")
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-08 10:51:10 +02:00
Barnabás Pőcze
2f62701e9e Documentation: guides: application-developer: Remove unnecessary argument
`required: true` is the default for meson's `dependency()` function.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-05 09:20:39 +02:00
Barnabás Pőcze
1200775986 Documentation: guides: pipeline-handler: Query pixel formats once
There is no reason to create an entire new copy of the same thing,
so use the already existing `formats` object.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-02 17:25:30 +02:00
Barnabás Pőcze
b03992e66f Documentation: guides: pipeline-handler: Simplify format collection
I believe a simple range based for loop is easier to understand
here than `std::transform()`. Furthermore, using a for loop enables
the easy filtering of invalid pixel formats.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-02 17:25:30 +02:00
Barnabás Pőcze
f83bab529c Documentation: guides: pipeline-handler: Fix Camera::create() link
Since 6b4771d460 ("libcamera: camera: Hide Camera::create() from the public API")
`Camera::create()` is documented in the internal documentation.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-02 17:25:30 +02:00
Barnabás Pőcze
28d2d4f43c Documentation: guides: pipeline-handler: Fix configuration creation
`PipelineHandler::generateConfiguration()` returns a `std::unique_ptr`.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-02 17:25:30 +02:00
Barnabás Pőcze
dd2ddea8bf Documentation: guides: pipeline-handler: Fix property list file name
It is `property_ids_core.yaml`.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-02 17:25:30 +02:00
Barnabás Pőcze
8e10804413 Documentation: guides: pipeline-handler: Fix camera creation
1. The unique_ptr containing the private data must be passed to
`Camera::create()`.

2. `registerCamera()` needs only the pointer to the `Camera`

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-02 17:25:30 +02:00
Quentin Schulz
ab508f2b55 README.rst: remove unnecessary dependency for qcam
The introducing commit (dff416a84b ("README: Add missing package for
Qt5 tools"); for Qt 5 originally) stated that without the dependency we
would get the following messages:

	Program /usr/lib/x86_64-linux-gnu/qt5/bin/lrelease found: NO
	Program lrelease-qt5 found: NO
	Program lrelease found: NO found  but need: '== 5.14.2'

That was the case for qt5 and is still true for qt6 but this actually
is neither breaking the build nor is it doing anything to the outcome
of the build (for both qt5 and qt6) as qcam is bit to bit identical
with and without that package.

Therefore, let's not mislead users to install an unnecessary package.

Signed-off-by: Quentin Schulz <quentin.schulz@cherry.de>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-05-01 16:48:48 +01:00
Barnabás Pőcze
92ed6140ee ipa: rpi: awb: Remove "fast" parameter
The "fast" parameter has not been used since it first appeared in the
source code. And not only is it not used, but its retrieval from
the configuration since c1597f9896 ("ipa: raspberrypi: Use YamlParser
to replace dependency on boost") has been incorrect. So remove it.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
2025-04-30 14:36:06 +02:00
David Plowman
e4677362a1 ipa: rpi: common: Avoid warnings when AeEnable control is used
The AeEnable control is now just a wrapper that is converted to
ExposureTimeMode and AnalogueGainMode controls instead. Therefore, it
should simply be ignored when we encounter it, without the need for
any warnings.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-29 16:14:49 +01:00
David Plowman
17e41b2a3a utils: raspberrypi: ctt: Fix NaNs in chromatic aberration tables
NaNs can appear if no black dots can be found and analysed in a
particular region of the calibration image. There needs to be at least
one such dot in every 8x8 cell covering the image.

This is now detected, and an error message issued. No CAC tables are
generated, so CAC is disabled.

Bug: https://github.com/raspberrypi/libcamera/issues/254
Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-29 16:14:48 +01:00
David Plowman
36ba0e5515 utils: raspberrypi: ctt: Fix NaNs in lens shading tables
The problem occurs when the calculation could lead to a final row (or
column) of grid squares with no pixels in them (and hence, NaNs).

One specific case is a Pi 5 with an image width (or height) of 1364,
so that's 682 Bayer quads. To give 32 grid squares it was calculating
22 quads per cell. However, 31 * 22 = 682 leaving nothing in the final
column.

The fix is to do a rounding-down division by the number of cells minus
one, rather than a rounding-up division by the number of cells. This
turns the corner case from one where the final row/column has no
pixels to one where we don't quite cover the full image, which is how
we have to handle these cases.

Bug: https://github.com/raspberrypi/libcamera/issues/254
Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-29 16:14:48 +01:00
Laurent Pinchart
9b50d3c23d libcamera: stream: Add color space to configuration string representation
Extend the string representation of StreamConfiguration, as returned by
the toString() and operator<<() functions, with color space information.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
2025-04-29 17:32:19 +03:00
Kieran Bingham
8751369c5b libcamera: pipeline: rkisp1: Convert to use MediaPipeline
Use the new MediaPipeline to manage and identify all sensors connected
to complex pipelines that can connect to the CSI2 receiver before the
ISP.

This can include chained multiplexors that supply multiple cameras, so
make use of the MediaDevice::locateEntities to search for all cameras
and construct a pipeline for each.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Acked-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-04-29 02:45:21 +09:00
Kieran Bingham
f1721c2f9f libcamera: internal: Add MediaPipeline helper
Provide a MediaPipeline class to help identifing and managing pipelines across
a MediaDevice graph.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-04-29 02:45:21 +09:00
Kieran Bingham
0785f5f99a libcamera: media_device: Add helper to return matching entities
Provide a helper on the MediaDevice to return a list of all
available entities which match a given function in the graph.

As a drive by, also fix a whitespace error in the documentation of
MediaDevice::setupLink.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-04-29 01:20:50 +09:00
Paul Elder
ee2b011b65 apps: cam: Try raw role if default viewfinder role fails
cam currently defaults to the viewfinder role when no role is specified.
This means that on platforms that only support the raw role (such as a
raw sensor with no softISP on a simple pipeline platform),
generateConfiguration() would return nullptr and cam would bail out.

At least this is what is supposed to happen based on the little
documentation that we have written regarding generateConfiguration(),
specifically in the application writer's guide, which is probably the
most influential piece of documentation:

  The ``Camera::generateConfiguration()`` function accepts a list of
  desired roles and generates a ``CameraConfiguration`` with the best
  stream parameters configuration for each of the requested roles. If the
  camera can handle the requested roles, it returns an initialized
  ``CameraConfiguration`` and a null pointer if it can't.

Currently the simple pipeline handler will return a raw configuration
anyway (if it only supports raw) even if a non-raw role was requested.
Thus cam receives a raw configuration instead of a nullptr when no role
is specified and viewfinder is requested.

However, in the near future, support for raw streams with softISP on the
simple pipeline handler will be merged. This will notably change the
behavior of the simple pipeline handler to return nullptr if a non-raw
role was requested on a platform that only supports raw. This is proper
behavior according to documentation, but changes cam's behavior as it
used to capture fine with no parameters but will no longer be able to.

Technically this is an issue with the roles API, as we are mixing
roles in the sense of "configuration hints" (eg. viewfinder vs recording
vs still capture) with roles in the sense of "platform capabilities"
(raw vs everything else). In the long term the proper solution is to
rework the roles API.

In the meantime, fix cam so that it will try the raw role if the default
viewfinder role returns no configuration. cam is an app that is capable
of using the raw stream, so this is appropriate behavior. If roles are
specified, then do not retry, as in this situation the user knows what
streams they can use and what they want.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
2025-04-29 00:57:54 +09:00
Barnabás Pőcze
72c3deffbb libcamera: controls: Disallow arrays of arrays
Arrays of arrays, even arrays of strings, are not supported by
the current `ControlValue` mechanism, so disable them for now
to trigger compile time errors if attempts are made to use them.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-25 18:06:05 +02:00
Hou Qi
3569fed7af gstreamer: Fixate colorimetry field during caps negotiation
When libcamerasrc is negotiating with downstream element, it first
extracts colorimetry field from downstream supported caps, then set
this colorimetry to its stream configuration and propagates the
colorimetry downstream.

Currently libamerasrc only considers the case there is one colorimetry
in colorimetry field of downstream caps. But the issue is that
downstream caps may report a list of supported colorimetry, which
causes libcamerasrc to set unknown colorimetry to stream configuration
and negotiate fail with downstream element.

In order to fix the issue, need to fixate colorimetry field before
getting colorimetry string.

Signed-off-by: Hou Qi <qi.hou@nxp.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-23 13:50:53 +01:00
Barnabás Pőcze
e1818265ae utils: ipc: Do not define variables in signal handler up front
Defining the variables at the beginning of the function forces the types
to be default constructible, which may not be desirable; furthermore, it
also forces the move/copy assignment operator to be used when the
deserialized value is retrieved.

Having `T val = f()` has the advantage of benefitting from potential RVO
as well as not requiring `T` to be default constructible, so generate
code in that form by calling `deserialize_call()` with `declare=true`.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-22 20:52:42 +02:00
Barnabás Pőcze
f31da7272e libcamera: ipa_module: Avoid unnecessary copy when getting signature
The `signature()` getter can just return a reference to the private vector
member variable, and let the caller make a copy if needed. Since the
return type is const qualified, this was likely the original intention.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-04-22 14:49:10 +02:00
Paul Elder
86c45c8fdf pipeline: rkisp1: Fix vblank delay
The vblank delay for delayed controls was incorrectly hardcoded to 1.
Get it from the camera sensor properties instead.

Fixes: f72c76eb6e ("rkisp1: Honor the FrameDurationLimits control")
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-22 20:18:11 +09:00
Quentin Schulz
6e24360d3f Revert "libcamera: rkisp1: Eliminate hard-coded resizer limits"
This reverts commit e85c7ddd38.

Linux kernel predating 6.4 (specifically commit 7cfb35d3a800 ("media:
rkisp1: Implement ENUM_FRAMESIZES") do not have the ioctl in rkisp1
driver required to dynamically query the resizer limits.

Because of that, maxResolution and minResolution are both {0, 0}
(default value for Size objects) which means filterSensorResolution()
will create an entry for the sensor in sensorSizesMap_ but because the
sensor resolution cannot fit inside the min and max resolution of the
rkisp1, no size is put into this entry in sensorSizesMap_.
On the next call to filterSensorResolution(),
sensorSizesMap_.find(sensor) will return the entry but when attempting
to call back() on iter->second, it'll trigger an assert because the size
array is empty.

Linux kernel 6.1 is supported until December 2027, so it seems premature
to get rid of those hard-coded resizer limits before this happens.

Let's restore the hard-coded resizer limits as fallbacks, actual limits
are still queried from the driver on recent enough kernels.

Fixes: 761545407c ("pipeline: rkisp1: Filter out sensor sizes not supported by the pipeline")
Signed-off-by: Quentin Schulz <quentin.schulz@cherry.de>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-22 13:24:01 +03:00
Barnabás Pőcze
5b73d25967 utils: ipc: Do not duplicate signals in proxy object
The specific proxy type (see `module_ipa_proxy.h.tmpl`) inherits `IPAProxy`,
the specific interface type, and `Object`. The interface type already
provides public definitions of the necessary `Signal<>` objects (see
`module_ipa_interface.h.tmpl`), so do not duplicate them.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-21 16:33:40 +02:00
Barnabás Pőcze
3e4de5f54e apps: cam: capture_script: Simplify bool array parsing
`std::vector<bool>` is a specialization that implements a dynamic
bit vector, therefore it is not suitable to provide storage for
an array of `bool`. Hence a statically sized array is used when
parsing an array of boolean values.

Instead, use the array overload of `std::make_unique` since the
size is known beforehand.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-04-21 16:24:16 +02:00
Barnabás Pőcze
83543f08d5 libcamera: pipeline: imx8-isi: Remove unused variable
The `mbusCodes` variable in `ISICameraConfiguration::validateRaw()`
has been unused since

  87fed43253 ("libcamera: imx8-isi: Break out RAW format selection"),

so remove it.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-21 16:15:21 +02:00
Barnabás Pőcze
ee92b5211c libcamera: pipeline: virtual: Fix typo in log message
pass -> parse

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-21 16:05:43 +02:00
Laurent Pinchart
5d1380f7df utils: rkisp1: gen-csc-table: Support printing CCM in decimal
Add an option to the gen-csc-table.py script to output the CCM matrix in
decimal format instead of hexadecimal. This makes no functional
difference, but is useful to adapt to different coding styles.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-17 14:50:44 +03:00
Pavel Machek
50d143ad1d doc: document libtiff dependency for cam
DNG writing is useful when working with bayer data, but libtiff is
needed for that.

Signed-off-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
[Kieran: Updated text to match other entries]
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-15 18:52:31 +01:00
Milan Zamazal
026ed62739 libcamera: software_isp: Fix CCM multiplication
A colour correction matrix (CCM) is applied like this to an RGB pixel
vector P:

  CCM * P

White balance must be applied before CCM.  If CCM is used, software ISP
makes a combined matrix by multiplying the CCM by a white balance gains
CCM-like matrix (WBG).  The multiplication should be as follows to do it
in the correct order:

  CCM * (WBG * P) = (CCM * WBG) * P

The multiplication order in Lut software ISP algorithm is reversed,
resulting in colour casts.  Let's fix the order.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-15 18:47:17 +01:00
Barnabás Pőcze
78d9f7bb75 libcamera: pipeline: uvcvideo: Expose Gamma control
Commit 294ead848c ("libcamera: Add gamma control id")
introduced the "Gamma" control, so expose it for UVC
cameras as well using the `V4L2_CID_GAMMA` control.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-15 13:07:21 +02:00
Barnabás Pőcze
5553efc6b1 libcamera: request: Avoid double map lookup
Use `try_emplace()` that more or less combines `find()` and `operator[]`
in one function.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-15 12:11:40 +02:00
Barnabás Pőcze
7fd317adf0 apps: lc-compliance: Add multi-stream tests
Rename the `SingleStream` test to `SimpleCapture`, and extend it
to support using multiple roles. And instantiate another test suite
from the `SimpleCapture` test that tests multiple streams in one
capture session.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-04-15 09:54:18 +02:00
Barnabás Pőcze
13cca98046 apps: lc-compliance: Support multiple streams in helpers
Prepare to add a test suite for capture operations with multiple
streams.

Modify the Capture helper class to support multiple roles and streams
in the configure() and capture() operations. The buffer count
of each stream is asserted to be the same.

Multi-stream support will be added in next patches.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-04-15 09:54:18 +02:00
Pavel Machek
886f877dd3 doc: Mention right meson version
Documentation says 0.60, but in fact 0.63 is required.

Signed-off-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-14 22:07:31 +03:00
Quentin Schulz
ae2b6cb3ca meson: Do not automatically build documentation if sphinx-build-3 is found
Commit aba567338b ("Documentation: Move all dependencies into
features") did an incomplete migration of the documentation boolean
option into a documentation feature.

If sphinx-build-3 binary is found on the host system, the documentation
is built, regardless of the value of the feature option.

This makes sure that sphinx-build-3 presence is only checked if the
documentation feature is not disabled (which is the default, as it's
"auto" by default).

This is essential for reproducibility for build systems where
sphinx-build-3 may or may not be present when libcamera is built, and
also to declutter the generated package if documentation isn't desired.

Fixes: aba567338b ("Documentation: Move all dependencies into features")
Signed-off-by: Quentin Schulz <quentin.schulz@cherry.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Quentin Schulz <quentin.schulz@cherry.de>
Tested-by: Quentin Schulz <quentin.schulz@cherry.de>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-14 22:07:17 +03:00
Quentin Schulz
eea723ad72 meson: Make the default value of "documentation" feature explicit
The meson documentation on the feature build options isn't clear if a
missing "value" is legal and if it is, what its default value is ([1]).

Therefore, let's make it explicit by using what is experimentally the
default: auto.

[1] https://mesonbuild.com/Build-options.html#features

Signed-off-by: Quentin Schulz <quentin.schulz@cherry.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-14 22:07:11 +03:00
Milan Zamazal
21088e605c libcamera: software_isp: Add a clarification comment to AWB
The computed AWB gains are applied when constructing LUT tables rather
than in awb.cpp itself.  This can look confusing when reading awb.cpp,
let's add a clarifying comment.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
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>
2025-04-10 00:09:40 +03:00
Kieran Bingham
058f589ae3 libcamera v0.5.0
The abi-compliance-checker reports there are both ABI and API changes in this
release:

  Binary compatibility: 99%
  Source compatibility: 99.5%
  Total binary compatibility problems: 5, warnings: 1
  Total source compatibility problems: 6, warnings: 1

Substantially less than the previous release, and ultimately quite minor but
unfortunately there nonetheless and so the SONAME is updated to 0.5
accordingly. I do not anticipate anything there that cannot be solved for
applications without just a recompile. I had hoped to get a longer run for 0.4
series...

A full and detailed ABI report for those interested can always be generated
between any two versions with the internal tooling:

 "./utils/abi-compat.sh v0.4.0 v0.5.0"

Integration overview:

This release brings in 201 commits with a huge list of fixes and code clean up
which I'm very happy to see, including interesting fixes to the AGC and AWB
handling in libipa.

In regards to new features, libcamera-0.5 has aptly now got the core Raspberry
Pi 5 support merged!. There are still patches that are currently maintained by
Raspberry Pi for additional features, and while the transition to upstream
API's continue, but I think we're all happy to see this support getting in
directly, and Raspberry Pi continue to lead the way in upstream camera
development. I look forward to the kernel API's for streams being fully
utilised by the PiSP platform for upstream camera metadata handling. This
upcoming work is also supported by the CameraSensor factory and CameraSensorRaw
support that is now also merged in this release.

Further more in the platform support, the software_isp continues to be
developed and is now able to measure colour temperature, which will bring in
improvements for AWB, and a CCM can be applied while peforming debayering (at a
CPU cost) which will allow us to finally apply color tuning for sensors on
devices that need to fall back to the software ISP.

New sensor support seems fairly short in this release, with the IMX415 being
the prominent addition.

In libipa, and algorithm developments, along with many fixes and improvements
there is a substantial new feature that the Baysian AWB algorithm from
Raspberry Pi can now also be used on all libipa supported IPA modules, and has
shown good impovements for the RkISP1 supported devices.

There is minimal changes to the application support side, but it is notable
that now the Y444 format has been mapped to be usable by the gstreamer src
element. lc-compliance has seen some progress which I hope will bring this to
being a more central part of the test infrastructure.

The following commits in this release relate to either a bug fix or an
improvement to an existing commit.

 - DmaBufAllocator: Make DmaSyncer non-copyable
   - Fixes: 39482d59fe ("DmaBufAllocator: Add Dma Buffer synchronization function & helper class")
 - utils: codegen: controls.py: Fix missing direction error message
   - Fixes: 39fe4ad968 ("utils: codegen: controls.py: Parse direction information")
 - Thread: Fix setThreadAffinity race condition in start
   - Fixes: 4d9db06d66 ("libcamera: add method to set thread affinity")
 - meson: Don't override pipeline list when `auto` is selected
   - Bug: https://bugs.libcamera.org/show_bug.cgi?id=247
 - DmaBufAllocator: Avoid syncing with an invalid file descriptor
   - Fixes: 545046a41e ("DmaBufAllocator: Make DmaSyncer non-copyable")
 - controls: Introduce AEGC-related controls
   - Bug: https://bugs.libcamera.org/show_bug.cgi?id=42
 - Documentation: guides: application-developer: Fix variable shadowing
   - Bug: https://bugs.libcamera.org/show_bug.cgi?id=252
 - libcamera: pipeline: virtual: Fill buffer's metadata
   - Bug: https://bugs.libcamera.org/show_bug.cgi?id=245
 - ipa: rkisp1: agc: Fix build on debian 11 (gcc-9)
   - Fixes: ee918b370a ("ipa: rkisp1: agc: Initialize enum controls with a list of values")
 - ipa: rpi: Apply default ControlInfo values for sensor controls
   - Bug: https://bugs.libcamera.org/show_bug.cgi?id=253
 - ipa: rpi: Fix incorrect cast for ExposureTime ControlInfo
   - Fixes: bea2db5e61 ("ipa: rpi: Apply default ControlInfo values for sensor controls")
 - ipa: rkisp1: algorithms: agc: Fix whitespace
   - Fixes: 0e0e32b189 ("ipa: rkisp1: algorithms: agc: Check for correct stats type")
 - libcamera: pipeline: Fix LIBCAMERA_<NAME>_TUNING_FILE handling
   - Fixes: f5da05ed03 ("libcamera: pipeline: Move tuning file override handling to IPAProxy")
 - ipa: rkisp1: algorithms: awb: Fix AWB means vector order in RGB mode
   - Fixes: 29892f1c56 ("ipa: libipa: colour: Use the RGB class to model RGB values")
 - libipa: awb: Fix non-virtual destructor warning in AwbStats
   - Fixes: 6f663990a0 ("libipa: Add AWB algorithm base class")
 - ipa: rkisp1: Allow exposure time to be shorter than minimum frame duration limit
   - Fixes: f72c76eb6e ("rkisp1: Honor the FrameDurationLimits control")
 - ipa: libipa: Fix bug in ExposureModeHelper that leads to oscillations in AEGC
   - Fixes: 34c9ab6282 ("ipa: libipa: Add ExposureModeHelper")
 - libcamera: software_isp: Emit ispStatsReady only if IPA is running
   - Reported-by: Stanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
 - libcamera: base: log: Fix uninitialized variable warning
   - Fixes: 8fa119e0b5 ("libcamera: base: log: Use `std::from_chars()`")
 - libcamera: media_object: Fix unnecessary copy
   - Fixes: 9490c664b5 ("libcamera: Add members to MediaEntity to support ancillary entities")
 - ipa: rkisp1: agc: Fix metering modes
   - Fixes: 4c5152843a ("ipa: rkisp1: Derive rkisp1::algorithms::Agc from AgcMeanLuminance")
 - libcamera: software_isp: Reset stored exposure in black level
   - Bug: https://bugs.libcamera.org/show_bug.cgi?id=259
 - libcamera: pipeline: uvcvideo: Fix `ExposureTimeMode` control setup
   - Fixes: bad8d591f8 ("libcamera: uvcvideo: Register ExposureTimeMode control")
 - libcamera: pipeline: uvcvideo: Fix `ExposureTimeMode` control setting
   - Fixes: bad8d591f8 ("libcamera: uvcvideo: Register ExposureTimeMode control")
 - ipa: simple: Initialize ccmEnabled to false
   - Fixes: ac30686556 ("libcamera: software_isp: Track whether CCM is enabled")
 - gstreamer: Use `Control<>` objects when setting controls
   - Bug: https://bugs.libcamera.org/show_bug.cgi?id=261
 - gstreamer: Restore `AeEnable` control
   - Fixes: 187f2d537b ("gstreamer: Generate the new AEGC controls")
 - pipeline: rpi: Fix potential empty optional read
   - Fixes: 6c71ee1f15 ("pipeline: raspberrypi: Introduce PipelineHandlerBase class")

And the following updates have been made in this release, grouped by category:

core:
 - libcamera: virtual: Avoid some copies
 - libcamera: virtual: Query number of planes correctly
 - libcamera: virtual: Speed up test pattern animation
 - libcamera: camera_sensor_properties: Add delays for imx415
 - include: linux: Update headers for line-based embedded data support
 - include: linux: videodev2: Add generic line based pixel formats
 - libcamera: v4l2_subdevice: Add new metadata formats
 - libcamera: v4l2_videodevice: Update to the new kernel metadata API
 - libcamera: Add CameraSensor implementation for raw V4L2 sensors
 - libcamera: camera_sensor: Add support for embedded data
 - libcamera: base: Remove custom __nodiscard attribute
 - libcamera: Add missing <stdint.h> include to dma_buf_allocator.h
 - libcamera: include: Include missing stdint.h header
 - utils: codegen: controls.py: Fix missing direction error message
 - meson: Convert `v4l2` into a feature option
 - meson: Don't override pipeline list when `auto` is selected
 - libcamera: pipeline_handler: Enable silent configuration file lookup
 - libcamera: virtual: Install configuration file
 - libcamera: v4l2_subdevice: Work around false positive warning
 - libcamera: uvcvideo: Register ExposureTimeMode control
 - libcamera: camera: Pre-process AeEnable control
 - libcamera: base: object,thread: Disable copy/move
 - libcamera: matrix: Add read-only accessor to internal data
 - libcamera: log: Match whole category in LIBCAMERA_LOG_LEVELS
 - libcamera: Copy Vector class files from libipa
 - libcamera: Adapt Vector class to new location
 - libcamera: request: addBuffer(): Do fence check earlier
 - libcamera: Drop spurious colon after doxygen \todo directive
 - meson: Enable the -Wnon-virtual-dtor compiler option
 - libcamera: formatting: Avoid spaces in for loops without expression
 - libcamera: base: log: Remove move constructor
 - libcamera: base: log: Use `std::from_chars()`
 - libcamera: base: log: Remove `LogMessage::init()`
 - libcamera: base: log: Make `LogCategory::severity_` atomic
 - libcamera: base: log: Use `std::string_view` to avoid some copies
 - libcamera: base: log: Pass dynamic prefix through
 - libcamera: base: log: Protect log categories with lock
 - libcamera: base: log: Avoid manual `LogCategory` deletion
 - libcamera: meson: Fix libyuv detection
 - libcamera: base: thread: Support dispatching for a specific receiver
 - libcamera: base: log: Fix uninitialized variable warning
 - libcamera: base: signal: Drop pre-C++17 support
 - meson: Add libpisp.wrap
 - libcamera: media_object: Fix unnecessary copy
 - libcamera: ipa_manager: Store `IPAModule`s in `std::unique_ptr`
 - libcamera: base: mutex: Remove unnecessary constructors
 - libcamera: media_device: Ignore `lockf()` return value
 - libcamera: v4l2_videodevice: `lastUsedCounter_` need not be atomic
 - libcamera: camera: Ensure correct id maps are always set
 - libcamera: base: span: Explicitly default copy assignment
 - libcamera: controls: Check size of enum
 - libcamera: camera_manager: Do not emit signals while holding lock
 - libcamera: camera_manager: Simplify camera lookup
 - libcamera: camera_manager: Take camera id in `std::string_view`
 - libcamera: base: object: Forward arguments when invoking
 - libcamera: base: bound_method: Simplify `invokePack()`
 - libcamera: v4l2_device: add frame start event helpers
 - DmaBufAllocator: Make DmaSyncer non-copyable
 - Thread: Fix setThreadAffinity race condition in start
 - DmaBufAllocator: Avoid syncing with an invalid file descriptor
 - controls: Introduce AEGC-related controls
 - controls: Remove AeLocked
 - controls: Redefine AeEnable

ipa:
 - ipa: rpi: Add cam_helper for imx415
 - ipa: rpi: Add vc4 tuning files for imx415
 - ipa: rpi: Use r-value references in the set()/setLocked() functions
 - ipa: rpi: Add erase()/eraseLocked() to RPiController::Metadata
 - ipa: rpi: Add a HW property to determine if the data buffer is strided
 - ipa: rpi: Provide the camera helper with the hardware configuration
 - ipa: raspberry: Port to the new AEGC controls
 - ipa: rkisp1: Port to the new AEGC controls
 - ipa: rkisp1: agc: Report new AeEnable control as available
 - ipa: raspberry: Report new AeEnable control as available
 - ipa: libipa: lux: Fix indentation
 - ipa: rkisp1: agc: Initialize enum controls with a list of values
 - ipa: rkisp1: agc: Fix build on debian 11 (gcc-9)
 - ipa: rpi: Apply default ControlInfo values for sensor controls
 - ipa: Use Vector class from libcamera
 - libipa: Drop Vector class
 - ipa: rpi: Fix incorrect cast for ExposureTime ControlInfo
 - ipa: rkisp1: algorithms: agc: Fix whitespace
 - libipa: interpolator: Add accessor to internal data
 - libipa: pwl: Add clear() function
 - libipa: Add AWB algorithm base class
 - libipa: awb: Add helper functions for AWB mode support
 - libipa: Add grey world AWB algorithm
 - ipa: rkisp1: Move calculation of RGB means into own function
 - ipa: rkisp1: Use grey world algorithm from libipa
 - libipa: Add bayesian AWB algorithm
 - ipa: rkisp1: Add support for bayes AWB algorithm from libipa
 - ipa: rkisp1: awb: Apply gains based on default colour temperature on start
 - libipa: lux: Normalize referenceY to 1
 - libipa: awb_bayes: Add logging of value limits
 - libipa: awb_bayes: Remove overly verbose log messages
 - libipa: awb_bayes: Change the probabilities from log space to linear space
 - libipa: awb: Sort class member documentation according to header order
 - libipa: awb: Capitalize AWB
 - libipa: awb: Follow function names with '()' in doxygen documentation
 - libipa: awb: Standardize spelling on 'grey' world
 - libipa: awb: Replace reference to pipeline handle with IPA module
 - libipa: awb: Pass lux value to calculateAwb() as unsigned int
 - ipa: rkisp1: awb: Fix wrong indentation in comment
 - libipa: awb: Rename AwbStats::getRGBMeans() to rgbMeans()
 - libipa: awb: Tidy up includes
 - libipa: awb_grey: Minor comment fixes
 - ipa: rkisp1: awb: Don't calculate RGB means if stats are missing
 - ipa: rkisp1: awb: Capitalize AWB
 - ipa: rkisp1: algorithms: awb: Fix AWB means vector order in RGB mode
 - libipa: awb: Fix non-virtual destructor warning in AwbStats
 - ipa: rkisp1: Initialise AGC from FrameDurationLimits controls
 - ipa: rkisp1: Alias lineDuration
 - ipa: rkisp1: Allow exposure time to be shorter than minimum frame duration limit
 - ipa: libipa: Fix bug in ExposureModeHelper that leads to oscillations in AEGC
 - utils: ipc: Only dispatch messages for proxy when stopping thread
 - ipa: rpi: Add support for Raspberry Pi 5
 - ipa: simple: lut: Fix include path
 - ipa: rkisp1: agc: Fix metering modes
 - ipa: rkisp1: agc: Set measurement window to full frame
 - ipa: rkisp1: Add debug log for the sensor controls being set
 - ipa: libipa: agc_mean_luminance: Error out when effectiveExposureValue is zero
 - ipa: rksip1: Remove setControls(0) to reduce startup oscillations
 - ipa: simple: softisp: Extend to pass metadata
 - ipa: simple: Report the ColourGains in metadata
 - ipa: simple: Report black levels in metadata
 - ipa: simple: Report contrast in metadata
 - ipa: simple: Report exposure in metadata
 - ipa: simple: Initialize ccmEnabled to false
 - libipa: histogram: Fix quantile() calculation for fractional results
 - libipa: histogram: Fix interQuantileMean() for small ranges

apps:
 - gstreamer: allocator: gst_libcamera_allocator_new(): Recognize errors
 - gstreamer: allocator: gst_libcamera_allocator_new(): Fix memory leak
 - gstreamer: Generate the new AEGC controls
 - apps: ppm_writer: Add a missing include
 - apps: ppm_writer: Return EIO on I/O errors
 - gstreamer: Fix scaler-crop property get
 - apps: common: event_loop: Take callbacks by rvalue ref
 - apps: common: event_loop: Disable copy/move
 - apps: common: event_loop: Use `std::deque` instead of `std::list`
 - apps: common: event_loop: Use single event source for deferred calls
 - apps: common: event_loop: Remove unused type alias
 - apps: lc-compliance: Initialize `CameraManager` pointer in `Environment`
 - apps: lc-compliance: Put tests into anonymous namespace
 - apps: lc-compliance: Optimize `std::shared_ptr` usage
 - apps: lc-compliance: Remove redundant getter call
 - apps: lc-compliance: Don't allocate `FrameBufferAllocator` dynamically
 - apps: lc-compliance: Use `std::vector` for argument array
 - apps: lc-compliance: Use array instead of `std::vector`
 - apps: lc-compliance: Add message to `GTEST_SKIP()`
 - apps: lc-compliance: Merge `CaptureBalanced` and `CaptureUnbalanced`
 - gstreamer: Add Y444 format support to the YUV list
 - apps: cam: Fix include order
 - apps: qcam: Simplify `PixelFormat` search
 - apps: cam: Highlight default enumerator
 - gstreamer: Use `Control<>` objects when setting controls
 - gstreamer: Restore `AeEnable` control

pipeline:
 - libcamera: pipeline: virtual: Demote config file error message to debug
 - libcamera: pipeline: Move tuning file override handling to IPAProxy
 - libcamera: software_isp: Move a non-loop condition out of the loop
 - libcamera: software_isp: Handle signals in the proper thread
 - libcamera: pipeline: virtual: Simplify error return
 - libcamera: pipeline: virtual: Fill buffer's metadata
 - libcamera: pipeline: virtual: Set `FrameError` on error
 - libcamera: pipeline: Fix LIBCAMERA_<NAME>_TUNING_FILE handling
 - rkisp1: Honor the FrameDurationLimits control
 - libcamera: software_isp: Emit ispStatsReady only if IPA is running
 - libcamera: software_isp: Handle queued output buffers on stop
 - libcamera: software_isp: Handle queued input buffers on stop
 - libcamera: software_isp: Dispatch messages on stop
 - pipeline: rpi: Add new stream flags for PiSP
 - pipeline: rpi: Add support for Raspberry Pi 5
 - libcamera: software_isp: Determine color temperature
 - libcamera: software_isp: Use RGB type to represent gains
 - libcamera: software_isp: Store color temperature to metadata
 - libcamera: software_isp: lut: Remove maybe_unused on a used argument
 - libcamera: software_isp: Use common code to store debayered pixels
 - libcamera: software_isp: Use a macro to assign debayering methods
 - libcamera: software_isp: Add CCM algorithm
 - libcamera: software_isp: Add an example CCM to uncalibrated.yaml
 - libcamera: software_isp: Track whether CCM is enabled
 - libcamera: software_isp: Apply CCM in debayering
 - pipeline: rpi: pisp: Fix uninitialized variable warning
 - libcamera: software_isp: Track frames and requests
 - libcamera: software_isp: Reset stored exposure in black level
 - libcamera: pipeline: uvcvideo: Fix `ExposureTimeMode` control setup
 - libcamera: pipeline: uvcvideo: Fix `ExposureTimeMode` control setting
 - pipeline: rpi: Fix potential empty optional read
 - pipeline: simple: Connect/disconnect frameStart signal at start/stop time
 - pipeline: simple: Enable frame start events
 - pipeline: simple: Create DelayedControls instance once only
 - pipeline: simple: Reset delayedCtrls at start

documentation:
 - Documentation: design: ae: Document the design for AE controls
 - Documentation: guides: application-developer: Fix variable shadowing
 - Revert "README.rst: Report py dependencies"

test:
 - test: ipa_data_serialization: Use DebugMetadataEnable
 - test: threads: Use `pthread_testcancel()`
 - test: ipa: libipa: Add histogram tests
 - test: ipa: libipa: histogram: Add tests for quantile() returning a fraction
 - test: ipa: libipa: histogram: Add tests for small inter quantile mean ranges

tuning:
 - libtuning: module: awb: Add bayes AWB support
 - libtuning: Add module for lux calibration
 - utils: tuning: rkisp1: Add lux module

Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-03 14:38:25 +01:00
Stefan Klug
80ac19a507 libipa: histogram: Fix interQuantileMean() for small ranges
The interQuantileMean() is supposed to return a weighted mean value
between two quantiles. This works for fine histograms, but fails for
coarse histograms and small quantile ranges because the weight is always
taken from the lower border of the bin.

Fix that by rewriting the algorithm to calculate a lower and upper bound
for every (partial) bin that goes into the mean calculation and weight
the bins by the middle of these bounds.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-03 12:36:07 +02:00
Stefan Klug
3b9c432920 test: ipa: libipa: histogram: Add tests for small inter quantile mean ranges
Add tests for small inter quantile mean ranges. As these cases fail at
the moment, mark the test as should_fail.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-03 12:36:07 +02:00
Stefan Klug
8936e81e3f libipa: histogram: Fix quantile() calculation for fractional results
The calculation of the frac variable is based solely on integers and
therefore results in the fractional part being either 0 or 1.

In the original code from RaspberryPi this is mitigated by casting the
nominator to a double. This works for most cases, but fails when q is
very small because of the quantization introduced by item being an
integer.

Fix both issues by doing the full calculation in double and remove the
should_fail tag.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-03 12:36:07 +02:00
Stefan Klug
781e2f4d0c test: ipa: libipa: histogram: Add tests for quantile() returning a fraction
Add tests for quantile() returning a fractional value. These cases will
get fixed in the next commit. Therefore mark the test as should_fail.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-03 12:36:07 +02:00
Stefan Klug
1a17a6aac7 test: ipa: libipa: Add histogram tests
Add some basic tests for the histogram class.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-03 12:36:07 +02:00
Stanislaw Gruszka
18792b81cb pipeline: simple: Reset delayedCtrls at start
Similar like in other pipelines (IPU3, rpi) avoid using stale
values of DelayedControls class when the same camera is started
second time.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Co-developed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Stanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-03 10:28:30 +01:00
Laurent Pinchart
8a79800089 pipeline: simple: Create DelayedControls instance once only
The DelayedControls instance for the camera sensor is created in
SimplePipelineHandler::configure(). Constant deletion and reconstruction
of a new object is unnecessary, as the control delays are an intrinsic
property of the sensor and are known at initialization time. Move the
DelayedControls creation to the SimpleCameraData class constructor.

Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> # v6
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Stanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-03 10:28:29 +01:00
Stanislaw Gruszka
183bab1643 pipeline: simple: Enable frame start events
The simple pipeline handler uses frame start events to apply sensor
controls through the DelayedControls class. The setSensorControls()
function applies the controls directly, which would result in controls
being applied twice, if it wasn't for the fact that the pipeline handler
forgot to enable the frame start events in the first place. Those two
issues cancel each other, but cause controls to always be applied
directly.

Fix the issue by only applying controls directly in setSensorControls()
if no frame start event emitter is available, and by enabling the frame
start events in startDevice() otherwise. Disable them in stopDevice()
for symmetry.

Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> # v6
Co-developed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Co-developed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Stanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-03 10:28:24 +01:00
Stanislaw Gruszka
2f7bece17b pipeline: simple: Connect/disconnect frameStart signal at start/stop time
The frameStart signal from the frame start emitter is connected in the
configure() function, and is never disconnected. This means that each
time the camera is configured a new connection is made, causing the
DelayedControls::applyControls() to be called multiple times. Fix it by
connecting and disconnecting the signal when starting and stopping the
camera.

Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> # v6
Co-developed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Co-developed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Stanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-03 09:36:11 +01:00
Stanislaw Gruszka
b2eccef711 libcamera: v4l2_device: add frame start event helpers
Add helper to check if frame start event are supported by subdevice.
Since kernel does not have interface to query supported events
use subscribe interface.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> # v3
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> # v5
Signed-off-by: Stanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-03 09:36:11 +01:00
Barnabás Pőcze
a2a7f4fc2d pipeline: rpi: Fix potential empty optional read
If `!target`, then `*target` is undefined behaviour, so check if the optional
is empty when printing the error message. Simplify the check as well.

Fixes: 6c71ee1f15 ("pipeline: raspberrypi: Introduce PipelineHandlerBase class")
Fixes: 841ef2b4bb ("pipeline: rpi: Add support for Raspberry Pi 5")
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
2025-04-02 19:37:38 +02:00
Barnabás Pőcze
61d93434f5 gstreamer: Restore AeEnable control
Commit "gstreamer: Generate the new AEGC controls" removed the
`AeEnable` control from gen-gst-controls.py. However, the patch
set it was part of did not end up removing the `AeEnable`
control after all. So restore it for gstreamer users.

See 85cb179f28 ("controls: Redefine AeEnable").

Fixes: 187f2d537b ("gstreamer: Generate the new AEGC controls")
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-02 17:41:14 +02:00
Barnabás Pőcze
66fc6d2656 gstreamer: Use Control<> objects when setting controls
`g_value_get_boolean()` returns `gboolean`, which is actually `int`. Thus

  // ControlValue x;
  auto val = g_value_get_boolean(...);
  x.set(val);

will cause `ControlValue::set<int, ...>(const int&)` to be called, which
will save the value as `ControlTypeInteger32`, not `ControlTypeBoolean`.

Then, if something tries to retrieve the boolean value, it will run into an
assertion failure:

  Assertion `type_ == details::control_type<std::remove_cv_t<T>>::value' failed.

in `ControlValue::set()`.

Fix this by using the appropriately typed `Control<>` object when setting
the value in the `ControlList` as this ensures that the value will be
converted to the actual type of the control.

Bug: https://bugs.libcamera.org/show_bug.cgi?id=261
Fixes: 27cece6653 ("gstreamer: Generate controls from control_ids_*.yaml files")
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-02 17:14:54 +02:00
Stanislaw Gruszka
7cd8818da8 ipa: simple: Initialize ccmEnabled to false
ccmEnabled variable is not initialized by default, which results in
usage of CCM when the algorithm itself is not enabled and configured.

The bug manifests itself as seldom reproducible corrupted video stream.
Fix by initialize ccmEnabled member where it is declared.

Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Fixes: ac30686556 ("libcamera: software_isp: Track whether CCM is enabled")
Signed-off-by: Stanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-02 09:35:52 +01:00
Barnabás Pőcze
8b2533d0ac libcamera: pipeline: uvcvideo: Fix ExposureTimeMode control setting
The mapping in `UVCCameraData::processControl()` is not entirely correct
because the control value is retrieved as a `bool` instead of `int32_t`.
Additionally, the available modes are not taken into account.

Retrieve the control value with the right type, `int32_t`, check if the
requested mode is available, and if so, set the appropriate V4L2 control
value selected by `addControl()` earlier.

Fixes: bad8d591f8 ("libcamera: uvcvideo: Register ExposureTimeMode control")
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-04-01 16:13:00 +02:00
Barnabás Pőcze
799982b646 libcamera: pipeline: uvcvideo: Fix ExposureTimeMode control setup
`ControlInfo(Span<const int32_t>{...})` calls the incorrect constructor
of `ControlInfo`. The intended constructor to be called is
`ControlInfo(Span<const ControlValue>, ...)` however that is not called
because a span of `const int32_t` is passed. Instead, the constructor
`ControlInfo(const ControlValue &min, const ControlValue &max, ...)`
will be called.

Furthermore, since `values.back()` is used, only the last element of
the array is actually set.

To fix this, convert the array to contain `ControlValue` objects and use
a separate variable to keep track of which element to set next.

For each of `ExposureTimeMode{Auto,Manual}` save the V4L2 control value
that is to be used when the libcamera control is set.

Fixes: bad8d591f8 ("libcamera: uvcvideo: Register ExposureTimeMode control")
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-04-01 16:13:00 +02:00
Barnabás Pőcze
5646307b71 libcamera: base: bound_method: Simplify invokePack()
Use `if constexpr` instead of SFINAE to handle return values of type `void`.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-01 13:57:45 +02:00
Barnabás Pőcze
7dd548f678 libcamera: base: object: Forward arguments when invoking
Use `std::forward()` to forward the received arguments to enable the
potential use of move constructors instead of copy constructors.

Commit 0eacde623b ("libcamera: object: Avoid argument copies in invokeMethod()")
added the forwarding references to `invokeMethod()` but it did not add the
appropriate `std::forward()` calls, so copying could still take place
even if not necessary.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-01 12:58:45 +02:00
Barnabás Pőcze
37283b68ea libcamera: camera_manager: Take camera id in std::string_view
Do not force the caller to have an `std::string` object as a
simple string view is sufficient to do the lookup.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-01 12:08:24 +02:00
Barnabás Pőcze
056ebf0b6e libcamera: camera_manager: Simplify camera lookup
`std::find()` works just fine because `std::shared_ptr` has
`operator==()` with the expected semantics.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-04-01 11:42:00 +02:00
Milan Zamazal
ceea066fa2 libcamera: software_isp: Reset stored exposure in black level
Automatic black level setting in software ISP updates the determined
black level value when exposure or gain change.  It stores the last
exposure and gain values to detect the change.

BlackLevel::configure() resets the stored black level value but not the
exposure and gain values.  This can prevent updating the black value and
cause bad image output, e.g. after suspending and resuming a camera, if
exposure and gain remain unchanged.

Let's store exposure and gain in IPAActiveState.  Although the values
are not supposed to be used outside BlackLevel class, storing them in
the context has the advantage of their automatic reset together with the
other context contents and having them in `blc' struct indicates their
relationship to the black value computation.

Bug: https://bugs.libcamera.org/show_bug.cgi?id=259
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Tested-by: Robert Mader <robert.mader@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-04-01 10:47:39 +03:00
Milan Zamazal
485a807dcb ipa: simple: Report exposure in metadata
Report exposure and gain in metadata.

This is more complicated than it could be expected because the exposure
value should be in microseconds but it's handled using V4L2_CID_EXPOSURE
control, which doesn't specify the unit, see
https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html.
So the unit conversion is done in the way rkisp1 IPA uses.

This requires getting and passing IPACameraSensorInfo around.  To avoid
naming confusion and to improve consistency with rkisp1 IPA,
sensorCtrlInfoMap parameter is renamed to sensorControls.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-28 02:09:16 +02:00
Milan Zamazal
1375b07ede ipa: simple: Report contrast in metadata
Provide the requested contrast value, if any, in the metadata to add to
the completed requests.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-28 02:09:15 +02:00
Kieran Bingham
8a4c2682be ipa: simple: Report black levels in metadata
Provide the determined black level values in the metadata
to add to the completed requests.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-28 02:09:15 +02:00
Kieran Bingham
a0b97475b1 ipa: simple: Report the ColourGains in metadata
Provide the determined colour gains back into the metadata
to add to completed requests.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-28 02:09:14 +02:00
Kieran Bingham
fb99081586 ipa: simple: softisp: Extend to pass metadata
Extend the Simple IPA IPC to support returning a metadata ControlList
when the process call has completed.

A new signal from the IPA is introduced to report the metadata,
similarly to what the hardware pipelines do.

Merge the metadata reported by the ISP into any completing request to
provide to the application.  Completion of a request is delayed until
this is done; this doesn't apply to canceled requests.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-28 02:09:13 +02:00
Milan Zamazal
4e343af7df libcamera: software_isp: Track frames and requests
Hardware pipelines track requests and other information related to
particular frames.  This hasn't been needed in software ISP so far.  But
in order to be able to track metadata corresponding to a given frame,
frame-request tracking mechanism starts being useful.  This patch
introduces the basic tracking structure, actual metadata handling is
added in the following patch.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com> # Lenovo X13s
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-28 02:09:13 +02:00
Stefan Klug
4adf0e0b1c ipa: rksip1: Remove setControls(0) to reduce startup oscillations
The call to setControls(0) is counter productive. At start() time, no
requests were queued and no stats were received. So setControls(0)
accesses a zeroed frame context and in turn sends 0 as gain, exposure
and vblank to the pipeline handler and DelayedControls. This leads to
strong oscillations on every start of the camera.

A proper fix for handling the startup controls still needs to be done
and was already started in [1] and [2].

From a DelayedControls point of view the call to setControls(0) is also
unnecessary as DelayedControls treat frame 0 as already being queued in
after initialization.

So it is safe to just remove it and the removal fixes the zero
effectiveExposureValue discussed in the previous patch for rkisp1.

[1]: https://patchwork.libcamera.org/patch/21708/
[2]: https://patchwork.libcamera.org/patch/22445/

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-26 17:16:39 +01:00
Stefan Klug
03bae6b924 ipa: libipa: agc_mean_luminance: Error out when effectiveExposureValue is zero
In a proper system it never happens that the effectiveExposureValue
drops to zero. If that still happens due to a bug outside of
agc_mean_luminance, the calculated gain goes towards infinity but the
newExposureValue is still 0 because it is the result of multiplying the
effectiveExposureTime with the gain, leading to wild oscillations.

Catch that condition, print an error message and set the new effective
exposure value to an arbitrary 10ms.

Note that in any case the underlying problem must be fixed. The
important change is the added error message to be able to detect such a
situation.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-26 17:16:39 +01:00
Stefan Klug
94e94c6e8d ipa: rkisp1: Add debug log for the sensor controls being set
In the algorithm code a lot of information is logged in debug log level,
but there is no place where the values sent to the sensor get logged.
Add such a log message.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-26 17:16:39 +01:00
Laurent Pinchart
bb3b8f0fec pipeline: rpi: pisp: Fix uninitialized variable warning
gcc 13.3.0 from buildroot 2024.11.1 complains about an uninitialized
variable. This is a false positive as the cfe_ array can't be empty.
Nonetheless, it breaks builds, so initialize the variable to work around
the issue.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 13:41:17 +02:00
Stefan Klug
2c66de06a0 ipa: rkisp1: agc: Set measurement window to full frame
With the availability of metering modes and the corresponding weights,
there is a flexible way of defining the area that gets taken into
account when AEGC is calculated. There is no need to reduce that window
to an arbitrary region anymore. If need arises we can make this
parameter user configurable or add a control for it.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 12:31:11 +01:00
Stefan Klug
0539e88679 ipa: rkisp1: agc: Fix metering modes
The weights for a given metering mode are applied to the histogram data
inside the histogram statistics block. The AE statistics do not contain
any weights. Therefore the weights are honored when AgcMeanLuminance
calculates the upper or lower constraints, but ignored in the
calculation of the frame luminance. Fix that by manually applying the
weights in the luminance calculation.

Fixes: 4c5152843a ("ipa: rkisp1: Derive rkisp1::algorithms::Agc from AgcMeanLuminance")
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 12:31:10 +01:00
Milan Zamazal
e2b4000dc9 libcamera: software_isp: Apply CCM in debayering
This patch applies color correction matrix (CCM) in debayering if the
CCM is specified.  Not using CCM must still be supported for performance
reasons.

The CCM is applied as follows:

  [r1 g1 b1]   [r]
  [r2 g2 b2] * [g]
  [r3 g3 b3]   [b]

The CCM matrix (the left side of the multiplication) is constant during
single frame processing, while the input pixel (the right side) changes.
Because each of the color channels is only 8-bit in software ISP, we can
make 9 lookup tables with 256 input values for multiplications of each
of the r_i, g_i, b_i values.  This way we don't have to multiply each
pixel, we can use table lookups and additions instead.  Gamma (which is
non-linear and thus cannot be a part of the 9 lookup tables values) is
applied on the final values rounded to integers using another lookup
table.

Because the changing part is the pixel value with three color elements,
only three dynamic table lookups are needed.  We use three lookup tables
to represent the multiplied matrix values, each of the tables
corresponding to the given matrix column and pixel color.

We use int16_t to store the precomputed multiplications.  This seems to
be noticeably (>10%) faster than `float' for the price of slightly less
accuracy and it covers the range of values that sane CCMs produce.  The
selection and structure of data is performance critical, for example
using bytes would add significant (>10%) speedup but would be too short
to cover the value range.

The color lookup tables can be represented either as unions,
accommodating tables for both the CCM and non-CCM cases, or as separate
tables for each of the cases, leaving the tables for the other case
unused.  The latter is selected as a matter of preference.

The tables are copied (as before), which is not elegant but also not a
big problem.  There are patches posted that use shared buffers for
parameters passing in software ISP (see software ISP TODO #5) and they
can be adjusted for the new parameter format.

Color gains from white balance are supposed not to be a part of the
specified CCM.  They are applied on it using matrix multiplication,
which is simple and in correspondence with future additions in the form
of matrix multiplication, like saturation adjustment.

With this patch, the reported per-frame slowdown when applying CCM is
about 45% on Debix Model A and about 75% on TI AM69 SK.

Using std::clamp in debayering adds some performance penalty (a few
percent).  The clamping is necessary to eliminate out of range values
possibly produced by the CCM.  If it could be avoided by adjusting the
precomputed tables some way then performance could be improved a bit.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 10:45:01 +00:00
Milan Zamazal
ac30686556 libcamera: software_isp: Track whether CCM is enabled
Applying color correction matrix (CCM) in software ISP is optional due
to performance reasons.  CCM is applied if and only if `Ccm' algorithm
is present in the tuning file.

Software ISP debayering is a performance critical piece of code and we
do not want to use dynamic conditionals there.  Therefore we pass
information about CCM application to debayering configuration and let it
select the right versions of debayering functions using templates.  This
is a trick similar to the previously used one for adding or not adding
an alpha channel to the output.

Debayering gets this information but it ignores it in this patch.
Actual processing with CCM is added in the followup patch.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 10:45:01 +00:00
Milan Zamazal
0c53fe5f2f libcamera: software_isp: Add an example CCM to uncalibrated.yaml
For performance reasons, color correction matrix (CCM) is not applied by
default in software ISP.  But let's add a commented out example how to
define it to the default tuning file.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 10:45:01 +00:00
Milan Zamazal
38ec74fb40 libcamera: software_isp: Add CCM algorithm
This patch adds color correction matrix (CCM) algorithm to software ISP.
It is based on the corresponding algorithm in rkisp1.

The primary difference against hardware pipelines is that applying the
CCM is optional.  Applying CCM causes a significant slowdown, time
needed to process a frame raises by 40-90% on tested platforms.  If CCM
is really needed, it can be applied, if not, it's better to stick
without it.  This can be configured by presence or omission of Ccm
algorithm in the tuning file.

CCM is changed only if the determined temperature changes by at least
100 K (an arbitrarily selected value), to avoid recomputing the matrices
and lookup tables all the time.

Since the CCM is float, rather than double, to use the same type as in
the rkisp1 pipeline, the type of color gains is changed from double to
float.

The outputs of the algorithm are not used yet, they will be enabled in
followup patches.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 10:45:01 +00:00
Milan Zamazal
23dfd69081 libcamera: software_isp: Use a macro to assign debayering methods
Assignments of the debayering methods to be used is a repetitive pattern
that can be (arguably) better expressed by using a macro.  This removes
some duplication and also makes easier to introduce more complex
assignment patterns.  This will be useful once color correction matrix
support is added.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 10:45:01 +00:00
Milan Zamazal
f1955a0058 libcamera: software_isp: Use common code to store debayered pixels
The debayering macros use the same pattern, let's extract it to a common
macro.  This reduces code duplication a bit now and it'll make changes
of debayering easier when color correction matrix is introduced.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 10:45:01 +00:00
Milan Zamazal
148ac13043 libcamera: software_isp: lut: Remove maybe_unused on a used argument
`params' argument of Lut::prepare is actually used, let's remove
maybe_unused from it.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 10:45:01 +00:00
Milan Zamazal
7d4b4a1a79 libcamera: software_isp: Store color temperature to metadata
Image color temperature is a piece of information that should be
reported in metadata, let's put it there.

Metadata is currently not reported in simple pipeline but we should make
at least newly added information ready to be reported.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 10:45:01 +00:00
Milan Zamazal
94e849bcf7 libcamera: software_isp: Use RGB type to represent gains
Rather than using a custom struct to represent RGB values, let's use the
corresponding type and its facilities.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 10:45:01 +00:00
Milan Zamazal
84f82c6b3c libcamera: software_isp: Determine color temperature
The AWB algorithm has data to determine color temperature of the image.
Let's compute the temperature from it and store it into the context.
This piece of information is currently unused but it will be needed in a
followup patch introducing support for color correction matrix.

Let's store the white balance related information under `awb' subsection
of the active state, as the hardware pipelines do.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-26 10:45:01 +00:00
Barnabás Pőcze
d505bd5360 libcamera: camera_manager: Do not emit signals while holding lock
Both `CameraManager::Private::{add,remove}Camera()` emit the
`camera{Added,Removed}` signals, respectively, while holding the
lock protecting the list of cameras.

This is problematic because if a callback tries to call `cameras()`,
then the same (non-recursive) lock would be locked again.

Furthermore, there is no real need to hold the lock while user code
is running, so release the lock as soon as possible.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Harvey Yang <chenghaoyang@chromium.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-25 12:54:50 +01:00
Barnabás Pőcze
b3272f7827 libcamera: controls: Check size of enum
Only enums whose sizes match that of `int32_t` can be directly
supported. Otherwise the expected size and the real size would
disagree, leading to an assertion failure in `ControlValue::set()`.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-25 12:41:17 +01:00
Barnabás Pőcze
a17df1be5e libcamera: base: span: Explicitly default copy assignment
The `dynamic_extent` specialization is currently not trivially copyable
unlike its standard counterpart, `std::span`. This is because the copy
assignment operator is user-defined. Explicitly default it just like
it is done in the main template definition.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-25 12:21:21 +01:00
Barnabás Pőcze
22dcaeacd0 libcamera: camera: Ensure correct id maps are always set
`Camera::Private::properties_` is a default constructed `ControlList`,
therefore it does not have an associated `ControlIdMap`. `controlInfo_`
is in a similar situation.

Extend the `Camera::Private` constructor to initialize the control id map
of both properly.

Multiple pipeline handlers copy the sensor's property list and
set that as camera properties, and since the `CameraSensor{Legacy,Raw}`
classes set the proper id map, the camera properties will have it too.

However, some pipelines, e.g. `uvcvideo` or `virtual`, do not do so,
and thus there will be no id map set. To fix this, extend the
`Camera::Private` constructor to set `properties::properties`.

As for `controlInfo_`, all pipeline handlers overwrite it during
camera initialization (and thus it will have the correct id map),
but still initialize the id map so that it is set at all times.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-21 18:28:28 +01:00
Barnabás Pőcze
12931e304a ipa: simple: lut: Fix include path
Use the proper path to include `libcamera/control_ids.h`.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-21 17:53:46 +01:00
Barnabás Pőcze
ce333ad0d2 test: threads: Use pthread_testcancel()
`pthread_testcancel()` is a guaranteed cancellation point,
specifically for testing if cancellation has been requested,
so use it.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-21 17:35:41 +01:00
Barnabás Pőcze
1df8091e63 libcamera: v4l2_videodevice: lastUsedCounter_ need not be atomic
The `V4L2BufferCache` type is not thread-safe. Its `lastUsedCounter_`
member is not used in contexts where its atomicity would matter.
So it does not need to be have an atomic type.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-21 16:56:58 +01:00
Barnabás Pőcze
90208694c8 apps: cam: Highlight default enumerator
Print "[default]" after the default enumerator when listing controls.

Example:

  $ cam -c 1 --list-controls
  [...]
  Control: [inout] libcamera::ExposureTimeMode:
    - ExposureTimeModeAuto (0) [default]
    - ExposureTimeModeManual (1)

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-21 16:40:50 +01:00
Barnabás Pőcze
fbb67a73c4 libcamera: media_device: Ignore lockf() return value
When `_FORTIFY_SOURCE` is enabled, the `lockf()` function might be marked
with the `warn_unused_result` attribute, leading to compilation failure.
Fix that by explicitly ignoring the return value.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-21 16:26:35 +01:00
Barnabás Pőcze
bb7f702b48 apps: qcam: Simplify PixelFormat search
Since `PixelFormat` has `operator==()`, `std::find()` can be used
directly, so do that to simplify.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-21 16:09:39 +01:00
Barnabás Pőcze
314ecb5400 libcamera: base: mutex: Remove unnecessary constructors
The compiler defined default constructor works perfectly fine.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-21 15:56:27 +01:00
Barnabás Pőcze
d716200d2b libcamera: ipa_manager: Store IPAModules in std::unique_ptr
Express the ownership more clearly by using a smart pointer type.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-21 15:43:41 +01:00
Barnabás Pőcze
4a5ad4e9b0 libcamera: media_object: Fix unnecessary copy
`MediaEntity::ancillaryEntities()` can just return a const lvalue
reference to the underlying array, a copy need not be made. That
was likely the original intention.

Fixes: 9490c664b5 ("libcamera: Add members to MediaEntity to support ancillary entities")
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-21 14:46:44 +01:00
Laurent Pinchart
330cd1c560 apps: cam: Fix include order
Several .cpp files in the cam application don't include their
corresponding header first, as usually done by libcamera to ensure that
headers are self-contained. Reorder headers to fix it. This shows
through a compilation error that file_sink.h is missing
libcamera/controls.h, fix it as well.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-03-20 16:03:27 +02:00
Naushir Patuck
841ef2b4bb pipeline: rpi: Add support for Raspberry Pi 5
Add the Raspberry Pi 5 ISP (PiSP) pipeline handler to libcamera. To
include this pipeline handler in the build, set the following meson
option:

meson configure -Dpipelines=rpi/pisp

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-20 12:03:28 +00:00
Naushir Patuck
332b04ce20 pipeline: rpi: Add new stream flags for PiSP
Add the following new stream flags:

Needs16bitEndianSwap - Indicates that a 16-bit endian swap needs to be
performed on the framebuffer in software.

Needs14bitUnpack - Indicates that a CSI-2 14-bit unpacking (to 16-bits)
needs to be performed on the framebuffer in software.

These are to workaround hardware restrictions in the CFE hardware that
will be supported in a future commit.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-20 12:03:28 +00:00
Naushir Patuck
dccdf87af3 ipa: rpi: Add support for Raspberry Pi 5
Add the Raspberry Pi 5 ISP (PiSP) IPA to libcamera. To include this IPA
in the build, set the following meson option:

meson configure -Dipas=rpi/pisp

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-20 12:03:28 +00:00
Naushir Patuck
e9807f5b6a meson: Add libpisp.wrap
Add a new subpoject wrap file for the libpisp library located at
https://github.com/raspberrypi/libpisp

The libpisp library is used to configure the Raspberry Pi 5 Frontend
and Backend ISP components.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-20 12:00:03 +00:00
Antoine Bouyer
06269e9584 gstreamer: Add Y444 format support to the YUV list
'imx8-isi' pipeline provides support for 'YUV444' PixelFormat with YUV
streams, but it cannot be played with gstreamer adapter whereas
gstreamer's video format 'Y444' value suggests that it also supports
this format.

To add support of Planar 4:4:4 YUV format in gstreamer adapter, this patch
maps 'Y444' gstreamer video format with 'YUV444' libcamera PixelFormat.

Then below command example can be used to capture a stream with imx8-isi
pipeline:

  gst-launch-1.0 \
      libcamerasrc camera-name=<your_camera_name> ! \
      video/x-raw, format=Y444, width=1280, height=800 ! \
      queue ! \
      filesink location=/tmp/output

Signed-off-by: Antoine Bouyer <antoine.bouyer@nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-19 08:59:47 +00:00
Jacopo Mondi
39419ce431 Revert "README.rst: Report py dependencies"
This reverts commit 36753f5cbb.

Commit 36753f5cbb ("README.rst: Report py dependencies")
duplicated the entries for the Python bindings dependencies,
and does not list libpython3-dev which is still a requirement.

Revert it.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-03-04 18:02:14 +01:00
Laurent Pinchart
5e6872740d libcamera: base: signal: Drop pre-C++17 support
The libcamera public API requires C++17, drop support for older
standards.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-03 14:53:35 +02:00
Laurent Pinchart
bb1d216113 libcamera: base: log: Fix uninitialized variable warning
gcc 13.3.0, cross-compiling from amd64 to arm64, warns about a possibly
uninitialized variable in Logger::parseLogLevel():

src/libcamera/base/log.cpp: In static member function ‘static libcamera::LogSeverity libcamera::Logger::parseLogLevel(std::string_view)’:
../../src/libcamera/base/log.cpp:694:55: error: ‘severity’ may be used uninitialized [-Werror=maybe-uninitialized]
  694 |                 if (ec != std::errc() || *end != '\0' || severity > LogFatal)
      |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
src/libcamera/base/log.cpp:690:22: note: ‘severity’ was declared here
  690 |         unsigned int severity;
      |                      ^~~~~~~~

This appears to be a false positive, as the std::from_chars() function
should set severity value when it returns without an error. Still, the
warning is easy to solve, so fix it by initializing the severity
variable.

Fixes: 8fa119e0b5 ("libcamera: base: log: Use `std::from_chars()`")
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-03 14:52:59 +02:00
Milan Zamazal
c0a58b9798 utils: ipc: Only dispatch messages for proxy when stopping thread
When stopping the proxy thread, all messages of InvokeMessage type
posted to the pipeline handler thread are dispatched, to ensure that all
signals emitted from the proxy thread and queued for delivery to the
proxy are delivered synchronously. This unnecessarily delivers queued
signals for other objects in the pipeline handler thread, possibly
delaying processing of higher priority events.

Improve the implementation by limiting synchronous delivery to messages
posted for the proxy.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-01 23:10:05 +00:00
Milan Zamazal
86ffaf936d libcamera: software_isp: Dispatch messages on stop
There may be pending messages in SoftwareIsp message queue when
SoftwareIsp stops.  The call to IPAProxySoft::stop() will dispatch them
before SoftwareIsp::stop() finishes.  But this is dependent on
IPAProxySoft::stop() implementation, let's break this dependency and
dispatch messages to SoftwareIsp explicitly in SoftwareIsp::stop().

This also allows dropping `running_' flag.  Since the SoftwareIsp
messages get processed and invoke IPA calls before the IPA proxy is set
to ProxyStopping state and the SoftwareIsp worker thread is no longer
running, it's guaranteed that no new messages come to SoftwareIsp and
attempt to call the stopped IPA proxy.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-01 23:10:05 +00:00
Milan Zamazal
72a890e11a libcamera: base: thread: Support dispatching for a specific receiver
The Thread::dispatchMessage() function supports filtering messages based
on their type. It can be useful to also dispatch only messages posted
for a specific receiver. Add an optional receiver argument to the
dispatchMessage() function to do so. When set to null (the default
value), the behaviour of the function is not changed.

This facility is actually used in followup patches.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-01 23:10:05 +00:00
Milan Zamazal
cd32e069ec libcamera: software_isp: Handle queued input buffers on stop
When SoftwareIsp stops, input and output buffers queued to it may not
yet be fully processed.  They will be eventually returned but stop means
stop, there should be no processing related actions invoked afterwards.

Let's stop forwarding processed input buffers from SoftwareIsp slots
when SoftwareIsp is stopped.  Let's track the queued input buffers and
return them back for capture in SoftwareIsp::stop().

The returned input buffers are marked as cancelled.  This is not
necessary at the moment but it gives the pipeline handlers chance to
deal with this if they need to.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-01 23:09:56 +00:00
Milan Zamazal
ba4715ffed libcamera: software_isp: Handle queued output buffers on stop
When SoftwareIsp stops, input and output buffers queued to it may not
yet be fully processed.  They will be eventually returned but stop means
stop, there should be no processing related actions invoked afterwards.

Let's stop forwarding processed output buffers from the SoftwareIsp
slots once SoftwareIsp is stopped.  Let's track the queued output
buffers and mark those still pending as cancelled in SoftwareIsp::stop
and return them to the pipeline handler.

Dealing with input buffers is addressed in a separate patch.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-01 23:07:15 +00:00
Milan Zamazal
b72d789475 libcamera: software_isp: Emit ispStatsReady only if IPA is running
Software ISP runs debayering in a separate thread and debayering may
emit statsReady when software ISP (including the IPA) is being stopped.
The signal waits in a queue and gets invoked later, resulting in an
assertion error when attempting to invoke a method on the stopped IPA:

  FATAL default soft_ipa_proxy.cpp:456 assertion
  "state_ == ProxyRunning" failed in processStatsThread()

Let's prevent this problem by forwarding the ISP stats signal from
software ISP only when the IPA is running.  To track this,
SoftwareISP::running_ variable is introduced.

Making processing of the other signals in SoftwareISP more robust is
addressed in the followup patches.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reported-by: Stanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-01 23:06:12 +00:00
Dylan Aïssi
1ea124c7a3 libcamera: meson: Fix libyuv detection
We already fall back to a subproject to support the libyuv package when
it can not be discovered through the usual dependency() mechanism.

Unfortunately libyuv may be packaged without any corresponding
pkg-config support as can be seen at [0], so further extend the
dependency search by using an explicit cxx.find_library() call.

[0] https://packages.debian.org/bookworm/amd64/libyuv-dev/filelist

Signed-off-by: Dylan Aïssi <dylan.aissi@collabora.com>
Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-03-01 22:36:24 +00:00
Stefan Klug
eb550486c7 ipa: libipa: Fix bug in ExposureModeHelper that leads to oscillations in AEGC
The ExposureModeHelper::splitExposures() runs through the configured
stages to find the best gain/exposure time pair. It first raises the
exposure time until it reaches the limit of the current stage. Then it
raises the gain until that also reaches the limit of the current stage.
After that it continues with the next stage until a match is found.

Due to a slight mistake in the initial code, the second step doesn't
work as expected because the exposure time gets divided by the gain of
the current stage, effectively leading to a jump of the gain value from
the maximum gain of the last stage to the maximum gain of the current
stage instead of gradually increasing the gain value.

Depending on the tuning file this leads to very visible oscillations and
jumps in the brightness.

Fix by clamping the exposure time in the second step to the maximum
exposure time of the current stage.

While at it, add two comments for easier understanding.

Fixes: 34c9ab6282 ("ipa: libipa: Add ExposureModeHelper")
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-28 10:00:51 +01:00
Stefan Klug
d748bdc66d ipa: rkisp1: Allow exposure time to be shorter than minimum frame duration limit
The minimum FrameDurationLimit also limits the min exposure time and
results in overly bright AE regulation. Remove the limit on the minimum
exposure time as the vertical blanking ensures the minimum frame
duration limit.

Fixes: f72c76eb6e ("rkisp1: Honor the FrameDurationLimits control")
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 20:59:56 +01:00
Barnabás Pőcze
74c0e8cbf1 apps: lc-compliance: Merge CaptureBalanced and CaptureUnbalanced
The above two classes have very similar implementations, in fact, the
only essential difference is how many requests are queued. `CaptureBalanced`
queues a predetermined number of requests, while `CaptureUnbalanced`
queues requests without limit.

This can be addressed by introducing a "capture" and a "queue" limit
into the `Capture` class, which determine at most how many requests
can be queued, and how many request completions are expected before
stopping.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 17:31:04 +01:00
Barnabás Pőcze
995bb7e507 apps: lc-compliance: Add message to GTEST_SKIP()
Just like other gtest macros, `GTEST_SKIP()` returns an object
to which a formatted message can be added using the usual `<<`
stream operator. So use it instead of printing to `std::cout`.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-27 17:31:04 +01:00
Barnabás Pőcze
6fd3ac82b5 apps: lc-compliance: Use array instead of std::vector
There is no reason to use `std::vector` for this static data,
a simple array will do fine.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-27 17:31:04 +01:00
Barnabás Pőcze
5c3eb98374 apps: lc-compliance: Use std::vector for argument array
Just use an `std::vector` to store the arguments passed to
`InitGoogleTest()`. This removes the need for the map and
the separate `argc` variable used for size-keeping.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-02-27 17:31:04 +01:00
Barnabás Pőcze
1f02966f3f apps: lc-compliance: Don't allocate FrameBufferAllocator dynamically
There is no reason to do so.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-27 17:31:04 +01:00
Barnabás Pőcze
d8645b5f67 apps: lc-compliance: Remove redundant getter call
Smart pointers overload `operator->()`, no reason to use `get()`.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-27 17:31:04 +01:00
Barnabás Pőcze
d338fe9336 apps: lc-compliance: Optimize std::shared_ptr usage
Avoid unnecessary copies and try to move construct `std::shared_ptr`
whenever possible.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-27 17:31:04 +01:00
Barnabás Pőcze
6719ae34cc apps: lc-compliance: Put tests into anonymous namespace
There is no reason for these symbols to be global.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-27 17:31:04 +01:00
Barnabás Pőcze
8c6d0106d0 apps: lc-compliance: Initialize CameraManager pointer in Environment
Do not leave it unitialized.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-27 17:31:04 +01:00
Barnabás Pőcze
3939c316f0 apps: common: event_loop: Remove unused type alias
The type alias `duration` is not used anywhere, so remove it.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 17:29:28 +01:00
Barnabás Pőcze
234eb60546 apps: common: event_loop: Use single event source for deferred calls
Instead of calling `event_base_once()` every time a deferred call
is added to the loop, create an event source at construction, and
simply trigger that when a new deferred call is scheduled.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 17:29:28 +01:00
Barnabás Pőcze
54055dd0c2 apps: common: event_loop: Use std::deque instead of std::list
Deque has fast pop_front and push_back operations while making
fewer allocations for the same number of elements as an `std::list`.
So use an `std::deque` for storing the deferred calls of the loop.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-27 17:29:28 +01:00
Barnabás Pőcze
a0f4092c6c apps: common: event_loop: Disable copy/move
The compiler generated functions are not appropriate, so
delete the copy/move constructor/assignment to avoid
potential issues.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 17:29:28 +01:00
Barnabás Pőcze
b1b99f4d66 apps: common: event_loop: Take callbacks by rvalue ref
Using a const lvalue reference to `std::function<>` is not ideal
because it forces a copy to happen. Use an rvalue reference and
`std::move()` to avoid that.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-27 17:29:28 +01:00
Barnabás Pőcze
0fc00eacdb libcamera: base: log: Avoid manual LogCategory deletion
Wrap the `LogCategory` pointers in `std::unique_ptr` to avoid
the need for manual deletion in the destructor.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 11:30:23 +01:00
Barnabás Pőcze
9ac914c634 libcamera: base: log: Protect log categories with lock
Log categories may be added from any thread, so it is important to
synchronize access to the `Logger::categories_` list between its two
users: category creation (by LogCategory::create(), which calls
Logger::findCategory() and Logger::registerCategory()); and log level
setting (by Logger::logSetLevel()).

The LogCategory::create() function uses a mutex to serialize category
creation, but Logger::logSetLevel() can access `Logger::categories_`
concurrently without any protection. To fix the issue, move the mutex to
the Logger class, and use it to protect all accesses to the categories
list. This requires moving all the logic of LogCategory::create() to a
new Logger::findOrCreateCategory() function that combines both
Logger::findCategory() and Logger::registerCategory() in order to make
the two operations exacute atomically.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-02-27 11:30:23 +01:00
Barnabás Pőcze
aca8457d34 libcamera: base: log: Pass dynamic prefix through
Use move construction to essentially pass through the string
returned by `Loggable::logPrefix()` to avoid an unnecessary copy.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 11:30:23 +01:00
Barnabás Pőcze
24c2caa1c1 libcamera: base: log: Use std::string_view to avoid some copies
Use `std::string_view` to avoid some largely unnecessary copies, and
to make string comparisong potentially faster by eliminating repeated
`strlen()` calls.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 11:30:23 +01:00
Barnabás Pőcze
16bcc5a3e4 libcamera: base: log: Make LogCategory::severity_ atomic
The severity of a log category may be changed from a different thread,
so it is important to ensure that the reads and writes happen atomically.
Using `std::memory_order_relaxed` should not introduce any synchronization
overhead, it should only guarantee that the operation itself is atomic.

Secondly, inline `LogCategory::setSeverity()`, as it is merely an
assignment, so going through a DSO call is a big pessimization.
`LogCategory` is not part of the public API, so this change has
no external effects.

Thirdly, assert that the atomic variable is lock free so as to ensure
it won't silently fall back to libatomic (or similar) on any platform.
If this assertion fails, this needs to be revisited.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 11:30:23 +01:00
Barnabás Pőcze
5d0af9840b libcamera: base: log: Remove LogMessage::init()
It is a short function that can be merged into the constructor with
essentially no change in observable behaviour, so do that.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 11:30:23 +01:00
Barnabás Pőcze
8fa119e0b5 libcamera: base: log: Use std::from_chars()
Use the `std::from_chars()` function from `<charconv>` to
parse the integral log level instead of `strtoul` as it
provides an easier to use interface and better type safety.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2025-02-27 11:30:23 +01:00
Barnabás Pőcze
d40250e03b libcamera: base: log: Remove move constructor
C++17 guarantees move and copy elision in certain cases,
such as when returning a prvalue of the same type as the
return type of the function.

This is what the `_log()` functions do, thus there is no need
for the move constructor, so remove it. Furthermore, do not
just remove the implementation, but instead delete it as well.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-02-27 11:30:23 +01:00
Paul Elder
f72c76eb6e rkisp1: Honor the FrameDurationLimits control
Add support to rkisp1 for controlling the framerate via the
FrameDurationLimits control.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-02-26 15:07:44 +09:00
Kieran Bingham
2abfcac1c3 ipa: rkisp1: Alias lineDuration
The configured line duration of the sensor is used frequently throughout
the AGC implementation.

It's available in the IPA context through the rather long:
  context.configuration.sensor.lineDuration

Take a copy of the lineDuration early in the call and replace the two
current usages of the reference with the shorter copy to manage line
length and ease readibility.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-26 15:07:44 +09:00
Kieran Bingham
443ac36e12 ipa: rkisp1: Initialise AGC from FrameDurationLimits controls
The IPA calculates and reports the FrameDurationLimits to applications
by configuring the ControlInfo accordingly during
IPARkISP1::updateControls()

We later need to know these limits during Agc::configure() for
initialising the ActiveState of the AGC implementation with the limits.

Store the FrameDurationLimits ControlInfo in the ControlInfoMap which is
now present in the IPAContext so that it is commonly available for the
AGC algorithm, removing the 'todo' accordingly.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-26 15:07:43 +09:00
Milan Zamazal
33ce463a46 libcamera: formatting: Avoid spaces in for loops without expression
The clang formatter removes spaces in places of empty expressions in for
loops.  For example, it changes

  for (init; condition; )

to

  for (init; condition;)

libcamera currently uses both the styles and we should use only one of
them for consistency.  Since there is apparently no option to override
the formatter behavior (see
https://clang.llvm.org/docs/ClangFormatStyleOptions.html), let's remove
the extra spaces to make the formatter happy.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-02-26 02:27:18 +02:00
Laurent Pinchart
4849a84a9b meson: Enable the -Wnon-virtual-dtor compiler option
A base class with virtual functions and a non-virtual public destructor
is prone to undefined behaviourif deleted from a pointer to the base.
Enable the -Wnon-virtual-dtor warning to report those issues.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-26 02:27:16 +02:00
Laurent Pinchart
005d19a73f libipa: awb: Fix non-virtual destructor warning in AwbStats
The AwbStats structure has virtual functions but a publicly accessible
non-virtual destructors. This can cause undefined behaviour if deleting
a derived class instance through a pointer to the base AwbStats class.

The problem is theoretical only as no code in libcamera is expected to
perform such deletion, but compilers can't know that and will emit a
warning if the -Wnon-virtual-dtor option is enabled.

Fixing this can be done by declaring a virtual public destructor in the
AwbStats class. A more efficient alternative is to declare a protected
non-virtual destructor, ensuring that instances can't be deleted through
a pointer to the base class. Do so, and mark the derived RkISP1AwbStats
as final to avoid the same warning.

Fixes: 6f663990a0 ("libipa: Add AWB algorithm base class")
Reported-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Tested-by: Milan Zamazal <mzamazal@redhat.com>
2025-02-26 02:27:09 +02:00
Stefan Klug
d4545edb38 ipa: rkisp1: algorithms: awb: Fix AWB means vector order in RGB mode
Fix the order of the rgbMeans vector that got broken accidentally
during refactoring.  As there is currently no way to enable rgb mode at
runtime it went unnoticed.

Fixes: 29892f1c56 ("ipa: libipa: colour: Use the RGB class to model RGB values")
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-02-24 23:26:08 +02:00
Laurent Pinchart
25dcdf2998 libcamera: Drop spurious colon after doxygen \todo directive
The doxygen \todo directive doesn't need to be followed by a colon. Drop
it. While at it, turn one 'todo:' into '\todo'.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:36:21 +02:00
Laurent Pinchart
7222171340 ipa: rkisp1: awb: Capitalize AWB
AWB is an abbreviation, capitalize it in comments and log messages for
consistency.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:36:19 +02:00
Laurent Pinchart
1bb4d3712d ipa: rkisp1: awb: Don't calculate RGB means if stats are missing
When statistics are missing we can't meaningfully calculate the RGB
means. Move their calculation after checking if stats are available.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:36:15 +02:00
Laurent Pinchart
127bc20965 libipa: awb_grey: Minor comment fixes
Fix the top-level file description to mention the file contains an AWB
grey world implementation, not a base class, and fix a grammar mistake
in a documentation block.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:22:41 +02:00
Laurent Pinchart
643af7f2e7 libipa: awb: Tidy up includes
Drop unneeded headers and add missing ones.

The yaml_parser.h header is dropped from awb_grey.h as the classes it
provides are only used in virtual functions defined by the base class,
so any required definitions are guaranteed to be available already.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:22:39 +02:00
Laurent Pinchart
afd87c342c libipa: awb: Rename AwbStats::getRGBMeans() to rgbMeans()
The convention in libcamera is not to prefix getters with a 'get'
prefix. Rename the AwbStats::getRGBMeans() function accordingly.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:22:38 +02:00
Laurent Pinchart
080cb47e9f ipa: rkisp1: awb: Fix wrong indentation in comment
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:22:36 +02:00
Laurent Pinchart
6981a5169b libipa: awb: Pass lux value to calculateAwb() as unsigned int
The lux value can never be negative. Pass it as an unsigned int.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:22:30 +02:00
Laurent Pinchart
1a948d5457 libipa: awb: Replace reference to pipeline handle with IPA module
The AwbStats documentation incorrectly references pipeline handlers when
it means IPA modules. Fix it.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:22:29 +02:00
Laurent Pinchart
ef5a162b34 libipa: awb: Standardize spelling on 'grey' world
All locations but one spell 'grey' instead of 'gray'. Fix the outlier.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:22:27 +02:00
Laurent Pinchart
7115b81310 libipa: awb: Follow function names with '()' in doxygen documentation
Function names are followed by parentheses in doxygen documentation
blocks as convention in libcamera. Add missing parentheses in the
AwbAlgorithm documentation.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:22:25 +02:00
Laurent Pinchart
704a3aa5d0 libipa: awb: Capitalize AWB
AWB is an abbreviation, capitalize it in comments and log messages for
consistency.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:22:22 +02:00
Laurent Pinchart
7199a0c39d libipa: awb: Sort class member documentation according to header order
Sort the documentation of the class members in the same order as the
member declaration in the class definition, as is customary in
libcamera.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-24 18:22:18 +02:00
Stefan Klug
d476f8358b libipa: awb_bayes: Change the probabilities from log space to linear space
The original code used to specify the probabilities in log space and
scaled for the RaspberryPi hardware with 192 AWB measurement points.
This is reasonable as the whole algorithm makes use of unitless numbers
to prefer some colour temperatures based on a lux level. These numbers
are then hand tuned with the specific device in mind.

This has two shortcomings:

1. The linear interpolation of PWLs in log space is mathematically
   incorrect. The outcome might still be ok, as both spaces (log and
linear) are monotonic, but it is still not "right".

2. Having unitless numbers gets more error prone when we try to
   harmonize the behavior over multiple platforms.

Change the algorithm to interpret the numbers as being in linear space.
This makes the interpolation mathematically correct at the expense of a
few log operations.

To account for that change, update the numbers in the tuning example
file with the linear counterparts scaled to one AWB zone measurement.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:51:10 +01:00
Stefan Klug
d98f3512ec libipa: awb_bayes: Remove overly verbose log messages
Logging every search step is too verbose even with debug messages
enabled and it hides the more important messages (min max values of
errors and likelihoods). Remove the debug messages in a separate commit,
so that it can easily be reverted if needed.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:51:10 +01:00
Stefan Klug
c2059585f3 libipa: awb_bayes: Add logging of value limits
When tuning the AWB algorithm it is more helpful to get a feeling for
the value ranges than to get verbose output of every single step. Add a
small utility class to track the limits and log them.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:51:10 +01:00
Stefan Klug
ce9d547aff libipa: lux: Normalize referenceY to 1
By normalizing the referenceY value to 1 (which is the usual range for
Y) in the tuning file, the bins_ value is no longer needed. Remove it.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:51:10 +01:00
Stefan Klug
92bb16b68e utils: tuning: rkisp1: Add lux module
Now that the lux module is available, add it to the rkisp1 tuner.

While at it, sort the imports correctly.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:51:10 +01:00
Stefan Klug
a783a90dec libtuning: Add module for lux calibration
For the lux algorithm, reference values get calculated based on a tuning
image taken at a known lux level. The reference data contains the mean Y
of the image, lux level, exposure time, gain and aperture. This module
calculates these values for insertion into the tuning file.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:51:09 +01:00
Stefan Klug
bab4db2d6d ipa: rkisp1: awb: Apply gains based on default colour temperature on start
The colour gains are initialized with a default value of 1. Improve that
by querying the auto white balance algorithm for the gains for a default
colour temperature. This is still not based on measurements, but it is
still better than the current implementation. If the algorithm doesn't
implement mapping from colour temperature to gains, it will internally
fallback to 1.0.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:51:09 +01:00
Stefan Klug
7a4012ec79 ipa: rkisp1: Add support for bayes AWB algorithm from libipa
Now that libipa contains a bayes AWB algorithm, add it as supported
algorithm to the rkisp1 ipa.

The decision between the grey world algorithm and the bayesian is done
based on the "algorithm" property of the "Awb" algorithm in the tuning
file. If the lux value in the frameContext is set by the Lux algorithm
it is taken into account. If the lux value is 0 the prior likelihood
estimation gets ignored in the AWB calculations.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:51:09 +01:00
Stefan Klug
f45eb6bd9d libipa: Add bayesian AWB algorithm
The bayesian AWB algorithm is an AWB algorithm that takes prior
probabilities for a given light source dependent on the current lux
level into account.

The biggest improvement compared to the grey world model comes from the
search of the ideal white point on the CT curve. The algorithm walks the
CT curve to minimize the colour error for a given statistics. After the
minimium is found it additionally tries to search the area around that
spot and also off the curve. So even without defined prior probabilities
this algorithm provides much better results than the grey world
algorithm.

The logic for this code was taken from the RaspberryPi implementation.
The logic was only minimally adjusted for usage with the rkisp1 and a
few things were left out (see doxygen doc for the AwbBayes class). The
code is refactored to better fit the libcamera code style and to make
use of the syntactic sugar provided by the Interpolator and Vector
classes.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:51:09 +01:00
Stefan Klug
60d60c1367 libtuning: module: awb: Add bayes AWB support
To support the bayesian AWB algorithm in libtuning, the necessary data
needs to be collected and written to the tuning file.

Extend libtuning to calculate and output that additional data.

Prior probabilities and AwbModes are manually specified and not
calculated in the tuning process. Add sample values from the RaspberryPi
tuning files to the example config file.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-02-21 17:35:03 +01:00
Stefan Klug
deb3f05137 ipa: rkisp1: Use grey world algorithm from libipa
Now that libipa contains a grey world algorithm, use that.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:35:03 +01:00
Stefan Klug
b60bd37b1a ipa: rkisp1: Move calculation of RGB means into own function
Move the calculation of the RGB means into an own function for better
code clarity. This commit doesn't contain any functional changes.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:35:03 +01:00
Stefan Klug
7ea83d5f7b libipa: Add grey world AWB algorithm
Add the grey world algorithm that is currently used in rkisp1 to libipa.
No changes in functionality were made.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-21 17:35:03 +01:00
Stefan Klug
d19ae2a228 libipa: awb: Add helper functions for AWB mode support
The AWB modes are specified in the libcamera core controls. It is
therefore quite likely that every AWB algorithm will implement them. Add
helper functions for parsing and storing the configured modes in the
AwbAlgorithm base class.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:35:03 +01:00
Stefan Klug
6f663990a0 libipa: Add AWB algorithm base class
Add a class to provide a generic interface for auto white balance
algorithms. Concrete AWB algorithms are expected to subclass the
AwbAlgorithm class to implement their functionality.

IPAs are expected to subclass the AwbStats class and implement the
necessary functions to give the algorithm access to the hardware
specific statistics data.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-21 17:35:03 +01:00
Stefan Klug
3acacb089d libipa: pwl: Add clear() function
Sometimes it is necessary to clear a pwl. Add a function for that.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:35:03 +01:00
Stefan Klug
3c653f3f65 libipa: interpolator: Add accessor to internal data
The only way to access the internal data of an Interpolator is through
the getInterpolated() method. Sometimes it is necessary to to access the
internal data directly to iterate over it. Add an accessor for that.

While at it, remove a line break from the doxygen documentation for
interpolate() so that doxygen is able to correctly match the function.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2025-02-21 17:35:02 +01:00
Stefan Klug
a7d7bc03e5 libcamera: pipeline: Fix LIBCAMERA_<NAME>_TUNING_FILE handling
In f5da05ed03 ("libcamera: pipeline: Move tuning file override
handling to IPAProxy") a incorrect comparison slipped through. That
broke the handling of LIBCAMERA_<NAME>_TUNING_FILE. Fix that.

Fixes: f5da05ed03 ("libcamera: pipeline: Move tuning file override handling to IPAProxy")
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-02-21 17:34:12 +01:00
Barnabás Pőcze
32cc6717d2 libcamera: request: addBuffer(): Do fence check earlier
Check if the buffer has a fence before making any modifications because
otherwise it is possible for `Request::addBuffer()` to return an error code
while at the same time the buffer - for all intents and purposes - is added
to the request.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
2025-02-17 18:16:41 +01:00
Nerijus Bendžiūnas
892e85e4e3 gstreamer: Fix scaler-crop property get
Fix a copy/paste/replace typo. Without this fix, the last element (4th)
is always zero.

Signed-off-by: Nerijus Bendžiūnas <nerijus.bendziunas@gmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-02-17 18:59:22 +02:00
Laurent Pinchart
9834402f81 ipa: rkisp1: algorithms: agc: Fix whitespace
Drop trailing whitespace introduced by mistake.

Fixes: 0e0e32b189 ("ipa: rkisp1: algorithms: agc: Check for correct stats type")
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-02-13 21:39:41 +02:00
Naushir Patuck
2e8b6fd217 ipa: rpi: Fix incorrect cast for ExposureTime ControlInfo
controls::ExposureTime is of type ControlTypeInteger32, but the
default ControlInfoMap casts a value to int64_t causing incorrect
initialisation of the associated ControlInfo.

Fix this by casting correctly to int32_t.

Fixes: bea2db5e61 ("ipa: rpi: Apply default ControlInfo values for sensor controls")
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-02-13 10:41:45 +00:00
Stefan Klug
fa93d40035 libipa: Drop Vector class
The Vector class from libipa is not used anymore. Drop it.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-02-12 14:26:27 +01:00
Stefan Klug
82cf918b5b ipa: Use Vector class from libcamera
Now that there is a Vector class in libcamera, use that one.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
2025-02-12 14:26:27 +01:00
Stefan Klug
cfd94e5f85 libcamera: Adapt Vector class to new location
Change the namespace of the Vector class from libipa to libcamera and
add it to the build.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-02-12 14:26:27 +01:00
Stefan Klug
e506b45822 libcamera: Copy Vector class files from libipa
Prepare the move of the Vector class from libipa to libcamera by copying
the relevant files into the corresponding libcamera directories. The
files are copied without modification.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-02-12 14:26:27 +01:00
Naushir Patuck
bea2db5e61 ipa: rpi: Apply default ControlInfo values for sensor controls
The existing IPA initialisation code did not set default values for
some sensor related controls. This caused a crash using libcamerify
when the it was trying to access the default value for
controls::FrameDurationLimits as part of a recent change.

Ensure controls::FrameDurationLimits, controls::AnalogueGain and
controls::ExposureTime advertise default values along with the existing
min/max values. The default is set to the defaults defined in the IPA
set during initialisation.

Bug: https://bugs.libcamera.org/show_bug.cgi?id=253
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-02-12 10:34:28 +00:00
Stefan Klug
f63f4d71d4 ipa: rkisp1: agc: Fix build on debian 11 (gcc-9)
In the CI on debian 11 (gcc 9.3.0 and gcc 10.2.1) compilation fails
because the compiler incorrectly selects the

explicit ControlInfo(std::set<bool> values, bool def)

version of the ControlInfo constructor. This behavior was not
reproducible using gcc 9.5.0 and 10.5.0. So it seems newer versions of
gcc already contain a fix. Fix the CI build by explicitly passing a
ControlValue as second argument to the constructor.

Fixes: ee918b370a ("ipa: rkisp1: agc: Initialize enum controls with a list of values")
Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-02-11 23:08:38 +01:00
Stefan Klug
ee918b370a ipa: rkisp1: agc: Initialize enum controls with a list of values
The controls ExposureTimeMode and AnalogueGainMode are shown in camshark
as normal int entries instead of enum popups. The reason is that
ControlInfos for these controls are constructed using min/max instead of
a list of valid ControlValues. Camshark (and cam) uses the values()
vector to deduce if the control is an enum or not. It might be debatable
if this is the correct check, but all other ControlInfos for enum
controls in libcamera are initialized using a list.

Modify the construction of the ControlInfos to use the Span based
constructor to fix that issue.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-02-11 13:20:28 +01:00
Stefan Klug
9b1f609e5b libcamera: log: Match whole category in LIBCAMERA_LOG_LEVELS
A LIBCAMERA_LOG_LEVELS value of "RkISP1:0" also applies to RkISP1Ccm and
RkISP1Awb. This behavior is unexpected as it automatically enables all
algorithm log categories when the intent is only to increase the log
level of the upper category. Fix that replacing the manual matching code
with fnmatch. This has the side effect that more wildcards ("?" and
"[...]") are supported which is acceptable but won't be advertised.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-02-06 15:25:52 +01:00
Laurent Pinchart
7e5d811842 libcamera: matrix: Add read-only accessor to internal data
Add a data() function to the Matrix class to access the internal data.
This is useful for code that needs to use the matrix contents as a
linear array, as shown by the RkISP1::Ccm::process() function that needs
to copy the matrix data to a local variable. Simplify that function by
using the new accessor.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
2025-02-04 21:27:54 +02:00
Barnabás Pőcze
7fdfe648a4 libcamera: pipeline: virtual: Set FrameError on error
Do not cancel, simply set the buffer's status to `FrameError`
to notify the user about the error condition.

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
Barnabás Pőcze
98cf9eb533 libcamera: pipeline: virtual: Fill buffer's metadata
Fill the `FrameMetadata` object of the `FrameBuffer`s because it
should not be left uninitialized as users expect to be able to
access it and find reasonable data there.

Bug: https://bugs.libcamera.org/show_bug.cgi?id=245
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
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
Barnabás Pőcze
9a7fce1b51 libcamera: base: object,thread: Disable copy/move
Objects of type `Object` and `Thread` have address identities, so they
should not be just moved/copied. And the special member functions
generated by the compiler do not do the right thing. So delete them.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-02-04 18:18:50 +01:00
Barnabás Pőcze
2ae569dad2 Documentation: guides: application-developer: Fix variable shadowing
The mentioned commit mistakenly introduced a new variable for storing
the camera instead of just assigining to the global variable defined
earlier in the tutorial. Fix that by making it an assignment.

Bug: https://bugs.libcamera.org/show_bug.cgi?id=252
Fixes: e77a275110 ("treewide: Query list of cameras just once")
Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-02-04 17:53:50 +01:00
Milan Zamazal
9bc8b6a573 libcamera: software_isp: Handle signals in the proper thread
inputBufferReady ready signal in the simple pipeline is handled in the
pipeline handler thread.  outputBufferReady and ispStatsReady signals
should be handled there too.

Rather than relying on the user of the SoftwareIsp instance, let
SoftwareIsp inherits Object.  SoftwareIsp serves as a signal proxy, the
signals above are emitted from signal handlers.  This means that if
SoftwareIsp inherits Object then the slots are invoked in SoftwareIsp
thread.  Which is the camera manager thread because the SoftwareIsp
instance is created there.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-02-01 10:09:46 +00:00
Milan Zamazal
8aef7b4dfb apps: ppm_writer: Return EIO on I/O errors
EINVAL should be returned only when the requested format is unsupported.
On errors when writing the data, let's return EIO, which is the closest
description of the situation when we don't inspect it more.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-27 00:06:11 +02:00
Milan Zamazal
8072518ca9 apps: ppm_writer: Add a missing include
<errno.h> should be included in the ppm writer, as the source of the
error code constants used there.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-27 00:05:56 +02:00
Milan Zamazal
818b737146 libcamera: software_isp: Move a non-loop condition out of the loop
The check for the number of outputs is done in a loop over the outputs.
It should be moved out of the loop as it's not loop specific and is just
repeated there.

This is a cosmetic change not changing any functionality.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-27 00:05:32 +02:00
Laurent Pinchart
f5da05ed03 libcamera: pipeline: Move tuning file override handling to IPAProxy
The rkisp1 and rpi pipeline handlers duplicate code to handle the
LIBCAMERA_RKISP1_TUNING_FILE and LIBCAMERA_RPI_TUNING_FILE environment
variables that override tuning file selection. Move the common code to
IPAProxy::configurationFile() to avoid the duplication, and make the
feature available to all pipeline handlers with the same behaviour.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-01-24 22:41:36 +02:00
Laurent Pinchart
3e753e2273 ipa: libipa: lux: Fix indentation
Indentation in a doxygen comment is wrong, fix it.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2025-01-24 22:07:17 +02:00
Paul Elder
fdc01dc3e0 ipa: raspberry: Report new AeEnable control as available
Even though the new AeEnable control internally switches on and off the
sub-controls (ExposureTimeMode and AnalogueGainMode), it still needs to
be declared as available. Report this control as available in the
rpi IPA.

Support for the control does not need to be added as it is handled by
the Camera class. It does not need to be handled in metadata either as
the new version of AeEnable is not returned in metadata.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 21:36:41 +02:00
Paul Elder
338ba00e7a ipa: rkisp1: agc: Report new AeEnable control as available
Even though the new AeEnable control internally switches on and off the
sub-controls (ExposureTimeMode and AnalogueGainMode), it still needs to
be declared as available. Report this control as available in the
rkisp1 IPA.

Support for the control does not need to be added as it is handled by
the Camera class. It does not need to be handled in metadata either as
the new version of AeEnable is not returned in metadata.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 21:36:40 +02:00
Paul Elder
7abd413905 libcamera: camera: Pre-process AeEnable control
Handle the AeEnable under the hood in the Camera class, such that
AeEnable activates ExposureTimeMode and AnalogueGain together. This
allows applications the convenience of setting auto/manual mode of all
of the AE-related controls, as well as protecting applications against a
nasty behavior change if an aperture control is added in the future.
This also moves common handling code out of the IPA.

While we also want to inject AeEnable in Camera::controls() so that IPAs
don't have to report it, it is technically difficult at the moment as
ControlInfoMaps are not easily modifiable.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 21:36:40 +02:00
Paul Elder
85cb179f28 controls: Redefine AeEnable
In the redesign of the AE-related controls, the AeEnable control was
intended to be removed in favor of more specific sub-controls for
analogue gain and exposure time. However this will cause problems if
aperture sub-controls are introduced, and an application from a
pre-aperture era uses a camera that supports aperture.

If there is no AeEnable control, then a pre-aperture era application
might set analogue gain and exposure time to manual, while aperture
silently stays auto since that's the default mode. Thus aperture would
be uncontrollable by the application.

With an AeEnable control, then a pre-aperture era application can set
AeEnable to manual, and under the hood all three of analogue gain and
exposure time and aperture will be set to manual. The application won't
be able to set the manual aperture, however.

Although the above scenario is expected to be rare, the scenario with an
AeEnable control seems less detrimental. With an AeEnable control at
least the aperture would be static at a reasonably usable value, whereas
without an AeEnable the aperture would be more-or-less uncontrolable and
could go to extreme values as the AEGC algorithm tries to compensate for
the manual analogue gain and exposure time values.

Thus we redefine the AeEnable control, available only as a control and
not in metadata. It will be preprocessed by the Camera class so that the
relevant sub-controls are set. No pipline handler nor IPA shall act on
the AeEnable control. The IPA still has to report the control as
available, however.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 21:36:40 +02:00
Jacopo Mondi
a533bd004b controls: Remove AeLocked
Now that the codebase has been ported to use the new AEGC controls
remove the definition of AeLocked. AeEnable is not be removed as it will
be redefined.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 21:36:40 +02:00
Paul Elder
187f2d537b gstreamer: Generate the new AEGC controls
Since AeEnable will be replaced with ExposureTimeMode and
AnalogueGainMode so that the two can be set between auto/manual
independently, update the gstreamer control ids generation to conform
with this.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Nicolas Nicolas <nicolas.dufresne@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 21:36:40 +02:00
Paul Elder
3d23f325fd ipa: rkisp1: Port to the new AEGC controls
The newly introduced controls to drive the AEGC algorithm allow
controlling the computation of the exposure time and analogue gain
separately.

Augument the RkISP1 AEGC implementation to handle the exposure and gain
controls separately using the new controls.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 21:36:40 +02:00
Jacopo Mondi
c45a2682c3 ipa: raspberry: Port to the new AEGC controls
The newly introduced controls to drive the AEGC algorithm allow to
control the computation of the exposure time and analogue gain
separately.

The RPi AEGC implementation already computes the shutter and gain values
separately but does not expose separate functions to control them.

Augment the AgcAlgorithm interface to allow pausing/resuming the shutter
and gain automatic computations separately and plumb them to the newly
introduced controls.

Add safety checks to ignore ExposureTime and AnalogueGain values if the
algorithms are not paused, and report the correct AeState value by
checking if both algorithms have been paused or if they have converged.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Acked-by: David Plowman <david.plowman@raspberrypi.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 20:59:42 +02:00
Jacopo Mondi
72b2c9dddf test: ipa_data_serialization: Use DebugMetadataEnable
Replace the deprecated AeEnable control with DebugMetadataEnable
in ipa_data_serialization test. We use DebugMetadataEnable instead of
one of the controls replacing AeEnable as they are not boolean controls.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 19:38:21 +02:00
Jacopo Mondi
bad8d591f8 libcamera: uvcvideo: Register ExposureTimeMode control
Port the UVC pipeline handler to use the new ExposureTimeMode control
when processing Camera controls in place of the AeEnable control.

The V4L2_CID_EXPOSURE_AUTO control allows 4 possible values, which
map to ExposureTimeModeAuto and ExposureTimeModeManual.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 19:38:20 +02:00
Paul Elder
128220a139 Documentation: design: ae: Document the design for AE controls
Document the design and rationale for the AE-related controls.
Also add documentation for the controls.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 19:38:14 +02:00
Paul Elder
3becdbcbe8 controls: Introduce AEGC-related controls
Introduce the AeState, ExposureTimeMode and AnalogueGainMode controls
to model the AEGC algorithm block.

The three controls allow applications to select the exposure time and
analogue gain computation calculation mode (auto vs manual)
independently from one another, while the AeState control reports the
global state for the AEGC algorithm.

The new controls are meant to replace the existing AeEnable and AeLocked
controls, which are momentarily kept not to break compilation of
platforms making use of them.

Bug: https://bugs.libcamera.org/show_bug.cgi?id=42
Bug: https://bugs.libcamera.org/show_bug.cgi?id=43
Bug: https://bugs.libcamera.org/show_bug.cgi?id=47
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-20 19:38:10 +02:00
Laurent Pinchart
b01918978c libcamera: v4l2_subdevice: Work around false positive warning
gcc 13.3.0, as provided by buildroot 2024.11.1, chokes when compiling
v4l2_subdevice.cpp with ASan enabled, due to the usage of the C++
standard library regex header:

In file included from /host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/functional:59,
                 from ../../include/libcamera/base/utils.h:12,
                 from ../../include/libcamera/base/log.h:15,
                 from ../../include/libcamera/internal/v4l2_subdevice.h:20,
                 from ../../src/libcamera/v4l2_subdevice.cpp:8:
In constructor ‘std::function<_Res(_ArgTypes ...)>::function(std::function<_Res(_ArgTypes ...)>&&) [with _Res = bool; _ArgTypes = {char}]’,
    inlined from ‘std::__detail::_State<_Char_type>::_State(std::__detail::_State<_Char_type>&&) [with _Char_type = char]’ at /host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/regex_automaton.h:149:4,
    inlined from ‘std::__detail::_StateIdT std::__detail::_NFA<_TraitsT>::_M_insert_subexpr_begin() [with _TraitsT = std::__cxx11::regex_traits<char>]’ at /host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/regex_automaton.h:281:24:
/host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/std_function.h:405:42: error: ‘*(std::function<bool(char)>*)((char*)&__tmp + offsetof(std::__detail::_StateT, std::__detail::_State<char>::<unnamed>.std::__detail::_State_base::<unnamed>)).std::function<bool(char)>::_M_invoker’ may be used uninitialized [-Werror=maybe-uninitialized]
  405 |       : _Function_base(), _M_invoker(__x._M_invoker)
      |                                      ~~~~^~~~~~~~~~
In file included from /host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/regex:65,
                 from ../../src/libcamera/v4l2_subdevice.cpp:11:
/host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/regex_automaton.h: In member function ‘std::__detail::_StateIdT std::__detail::_NFA<_TraitsT>::_M_insert_subexpr_begin() [with _TraitsT = std::__cxx11::regex_traits<char>]’:
/host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/regex_automaton.h:279:17: note: ‘__tmp’ declared here
  279 |         _StateT __tmp(_S_opcode_subexpr_begin);
      |                 ^~~~~
In member function ‘bool std::_Function_base::_M_empty() const’,
    inlined from ‘std::function<_Res(_ArgTypes ...)>::operator bool() const [with _Res = bool; _ArgTypes = {char}]’ at /host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/std_function.h:574:25,
    inlined from ‘std::function<_Res(_ArgTypes ...)>::function(std::function<_Res(_ArgTypes ...)>&&) [with _Res = bool; _ArgTypes = {char}]’ at /host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/std_function.h:407:6,
    inlined from ‘std::__detail::_State<_Char_type>::_State(std::__detail::_State<_Char_type>&&) [with _Char_type = char]’ at /host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/regex_automaton.h:149:4,
    inlined from ‘std::__detail::_StateIdT std::__detail::_NFA<_TraitsT>::_M_insert_subexpr_begin() [with _TraitsT = std::__cxx11::regex_traits<char>]’ at /host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/regex_automaton.h:281:24:
/host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/std_function.h:247:37: error: ‘*(const std::_Function_base*)((char*)&__tmp + offsetof(std::__detail::_StateT, std::__detail::_State<char>::<unnamed>.std::__detail::_State_base::<unnamed>)).std::_Function_base::_M_manager’ may be used uninitialized [-Werror=maybe-uninitialized]
  247 |     bool _M_empty() const { return !_M_manager; }
      |                                     ^~~~~~~~~~
/host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/regex_automaton.h: In member function ‘std::__detail::_StateIdT std::__detail::_NFA<_TraitsT>::_M_insert_subexpr_begin() [with _TraitsT = std::__cxx11:
/host/aarch64-buildroot-linux-gnu/include/c++/13.3.0/bits/regex_automaton.h:279:17: note: ‘__tmp’ declared here
  279 |         _StateT __tmp(_S_opcode_subexpr_begin);
      |                 ^~~~~

This is a false positive that previously occurred with gcc 12.1.0 and
was fixed in 12.2 (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562),
and it seems to have now reappeared. The bug report indicates that the
probability of such false positives increase when using sanitizers. As
this isn't caught by CI, which compiles libcamera with gcc 13.3.0, the
chance that such compilation failures will appear in environments
without a clear pattern is relatively high. Work around the problem by
disabling the warning around the inclusiong of the regex header.

If the regex header needs to be included in other source files, creating
a wrapped in libcamera-base may be a cleaner alternative.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2025-01-20 19:38:05 +02:00
Harvey Yang
4dad8ece72 DmaBufAllocator: Avoid syncing with an invalid file descriptor
As DmaSyncer disables the copy c'tor, the move c'tor will be used
instead. This leaves some DmaSyncers with invalid SharedFDs. They should
avoid syncing with invalid file descriptors in the d'tor.

Fixes: 545046a41e ("DmaBufAllocator: Make DmaSyncer non-copyable")
Signed-off-by: Harvey Yang <chenghaoyang@chromium.org>
Tested-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-14 22:17:33 +00:00
Barnabás Pőcze
8b35ce4753 libcamera: virtual: Install configuration file
Install the example configuration file of the virtual pipeline
handler as it serves documentation purposes, and to make the
virtual pipeline handler easily usable in CI.

Nonetheless, the file is installed with the ".example" suffix
so that it will not be used by default, to avoid cluttering
the camera lists of users whose distributions decide to
enable the virtual pipeline handler.

The file is installed in the proper location for convenience:
  (1) is is easier to use it in the CI;
  (2) users need not browse documentation to determine where
      they should place the file.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Harvey Yang <chenghaoyang@chromium.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-14 16:50:55 +02:00
Laurent Pinchart
6841ea8d36 libcamera: pipeline: virtual: Demote config file error message to debug
The virtual pipeline handler prints an error message when its
configuration file can't be opened. Not providing a configuration file
is the default method to disable virtual cameras, so this error is
confusing for users. Replace it with a debug message when the
configuration file is not found, and keep an error message when it
exists but can't be opened.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Julien Vuillaumier <julien.vuillaumier@nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-14 16:38:09 +02:00
Laurent Pinchart
31a7378c87 libcamera: pipeline_handler: Enable silent configuration file lookup
The PipelineHandler::configurationFile() function prints an error
message when no configuration file is found. It can be useful for
pipeline handlers to silence the lookup operation and handle errors
themselves. Add a silent parameter to the function to enable this.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Julien Vuillaumier <julien.vuillaumier@nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-14 16:38:04 +02:00
Barnabás Pőcze
2ae7b2ff74 meson: Don't override pipeline list when auto is selected
Consider `pipelines=auto,virtual`. Previously that would select
everything that `auto` would, but not actually enable the `virtual`
pipeline handler because the `pipelines` list was reset.

Bug: https://bugs.libcamera.org/show_bug.cgi?id=247
Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-10 12:53:18 +01:00
Barnabás Pőcze
a945532314 meson: Convert v4l2 into a feature option
The v4l2 compatibility layer does not have external dependencies,
the usual benefits of a feature option do not apply. The main
motivation behind this change is making use of the `auto_features`
meson option that can change all feature options set to "auto"
by default to the desired value.

This can be useful for two reasons: (1) using auto_features=disabled
and then building up the list of features that are desired in the
particular build; (2) using auto_features=enabled to achieve
(usually) more testing and compilation coverage.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-10 12:35:10 +01:00
Barnabás Pőcze
ac611e80d2 gstreamer: allocator: gst_libcamera_allocator_new(): Fix memory leak
If `FrameBufferAllocator::allocate()` causes the construction to be
aborted, the allocated `GstLibcameraAllocator` will not be
deallocated properly. Use `g_autoptr()` to address this.

`g_steal_pointer()` could only be used in glib 2.68 or later because
earlier it evaluates to a pointer-to-void in C++, which would necessitate
a `static_cast`.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2025-01-10 12:14:27 +01:00
Barnabás Pőcze
fd6e41b9fb gstreamer: allocator: gst_libcamera_allocator_new(): Recognize errors
`FrameBufferAllocator::allocate()` might return a negative error code,
but currently this is handled the same way as success. So instead
of continuing, abort the construction of the allocator object.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2025-01-10 12:14:27 +01:00
Harvey Yang
d49a84a4f3 Thread: Fix setThreadAffinity race condition in start
Previously we call Thread::setThreadAffinityInternal in
Thread::startThread. The purpose was to avoid the main workload being
run on incorrect CPUs. This leads to a race condition of setting
`Thread::thread_` in `Thread::start()` and accessing
`Thread::setThreadAffinityInternal` though.

This patch moves the call after the construction of std::thread to avoid
the race condition. The downside is that the first tasks, if any, upon
starting a thread might be run on incorrect CPUs.

Fixes: 4d9db06d66 ("libcamera: add method to set thread affinity")
Signed-off-by: Harvey Yang <chenghaoyang@chromium.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-09 23:18:56 +02:00
Paul Elder
8d50577c0f utils: codegen: controls.py: Fix missing direction error message
The error message for missing direction field prints the direction value
(usually 'None') instead of the name of the field 'direction'. Fix this.

Fixes: 39fe4ad968 ("utils: codegen: controls.py: Parse direction information")
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-09 14:12:50 -06:00
Laurent Pinchart
de44514b25 libcamera: include: Include missing stdint.h header
Many libcamera headers that use standard C integer types do not include
stdint.h. Fix the omission.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-09 15:11:17 +02:00
Sergei Trofimovich
91de550243 libcamera: Add missing <stdint.h> include to dma_buf_allocator.h
Without the change the build fails on upcoming `gcc-15` as:

    In file included from ../src/libcamera/dma_buf_allocator.cpp:9:
    ../include/libcamera/internal/dma_buf_allocator.h:66:19: error: 'uint64_t' has not been declared
       66 |         void sync(uint64_t step);
          |                   ^~~~~~~~

Signed-off-by: Sergei Trofimovich <slyich@gmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2025-01-09 03:34:13 +02:00
Mattijs Korpershoek
5d444bbd51 libcamera: base: Remove custom __nodiscard attribute
__nodiscard was introduced for compatibility with C++14.
In C++17, there is an official attribute: [[nodiscard]].
Moreover, some libc implementations (like bionic) already define the
__nodiscard macro [1].

Since:
- libcamera builds with cpp_std=c++17
- [[nodiscard]] is already used in the android HAL (exif)

We should replace all usage __nodiscard of by [[nodiscard]] for
consistency.

Do the replacement and remove the no longer used compiler.h.

[1] 3254860

Signed-off-by: Mattijs Korpershoek <mkorpershoek@baylibre.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 15:26:30 +00:00
Laurent Pinchart
3feb4df755 libcamera: camera_sensor: Add support for embedded data
Some sensors support producing and transmitting embedded data over a
stream separate from the image stream. Add support for this feature in
the CameraSensor interface, and implement it for the CameraSensorRaw
class. The CameraSensorLegacy uses the default stub implementation, as
the corresponding kernel drivers don't support embedded data.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 14:30:33 +01:00
Laurent Pinchart
a09c7f212c libcamera: Add CameraSensor implementation for raw V4L2 sensors
Add a new CameraSensorRaw implementation of the CameraSensor interface
tailored to devices that implement the new V4L2 raw camera sensors API.

This new class duplicates code from the CameraSensorLegacy class. The
two classes will be refactored to share code.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 14:30:25 +01:00
Laurent Pinchart
99b54c2c55 libcamera: v4l2_videodevice: Update to the new kernel metadata API
With support for metadata in the streams API, the v4l2_meta_format
structure has been extended with width, height and bytesperline fields.

Support them in the V4L2VideoDevice getFormat() and setFormat()
functions is the video device is meta capture device and if the
pixel format is one of the generic line-based metadata formats.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 14:28:35 +01:00
Laurent Pinchart
accee0fe1e libcamera: v4l2_subdevice: Add new metadata formats
Support the newly introduced V4L2 media bus formats for metadata. This
includes generic metadata formats, and two sensor-specific embedded data
formats.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 14:28:35 +01:00
Jacopo Mondi
57d13c2c63 include: linux: videodev2: Add generic line based pixel formats
Add the definition for the generic line based pixelformats.

The formats has been added in upstream Linux by commit
1d9215233958 ("media: uapi: v4l: Add generic 8-bit metadata format
definitions") which got merged in Linux v6.10.

The formats however are not yet available to userspace, as they
have been made only available to the kernel by commit
d69c8429ea80 ("media: uapi: v4l: Don't expose generic metadata formats
to userspace") to let the line-based metadata support stabilize before
allowing applications to use it.

With the forthcoming completion of the line-based metadata upstreaming
manually add the generic line based pixel format to prepare libcamera
to support them.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 14:28:35 +01:00
Jacopo Mondi
052a9ff245 include: linux: Update headers for line-based embedded data support
Update the kernel headers with the definition of two device-specific
line-based metadata formats, and with the definition of the
MEDIA_PAD_FL_INTERNAL and V4L2_SUBDEV_ROUTE_FL_IMMUTABLE flags.

The new definitions will allow to support handling line-based
metadata streams exposed by the sensor driver through an
internal sink pad.

While the changes have not yet been collected in the official
linux-media tree, they're available in the 'metadata' branch of
https://git.linuxtv.org/sailus/media_tree.git, at revision:
f9bbbd9a696d ("media: Documentation: Add binning and sub-sampling
controls")

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 14:28:35 +01:00
Naushir Patuck
18c9ff46ef ipa: rpi: Provide the camera helper with the hardware configuration
Add a CamHelper::setHwConfig() helper used by the IPA to set the
hardware configuration in use by the pipeline. This will be needed by
the IMX500 camera helper in a future commit to determine if the
metadata buffer is strided.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 12:30:58 +00:00
Naushir Patuck
cd3dfa1f03 ipa: rpi: Add a HW property to determine if the data buffer is strided
This property (dataBufferStrided) indicates if the CSI-2 hardware writes
to the embedded/metadata buffer directly, or if it treats the buffer
like an image buffer and strides the metadata lines.

Unicam writes this buffer strided, while the PiSP Frontend writes to it
directly. This information will be relevant to data parsers in the
helpers where the data is structured in lines.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 12:30:58 +00:00
Naushir Patuck
a45de8e81b ipa: rpi: Add erase()/eraseLocked() to RPiController::Metadata
These functions erase a key/value pair from the metadata object.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 12:30:58 +00:00
Naushir Patuck
031a57bcd2 ipa: rpi: Use r-value references in the set()/setLocked() functions
Use an r-value reference in set() and setLocked(), allowing more
efficient metadata handling with std::forward and std::move if needed.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 12:30:58 +00:00
Dave Stevenson
2d4660b51a ipa: rpi: Add vc4 tuning files for imx415
Basic tuning done by David Plowman using a Waveshare SKU 28524
"IMX415-98 IR-CUT Camera" module.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 10:00:35 +00:00
Dave Stevenson
07be807dc7 ipa: rpi: Add cam_helper for imx415
As another Starvis sensor, it is near identical to imx290/327.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 10:00:28 +00:00
Dave Stevenson
549916fead libcamera: camera_sensor_properties: Add delays for imx415
Believed correct based on imx290.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-08 10:00:08 +00:00
Harvey Yang
545046a41e DmaBufAllocator: Make DmaSyncer non-copyable
As DmaSyncer does sync start/end in the c'tor/d'tor, copying a DmaSyncer
instance would trigger sync end earlier than expected. This patch makes
it non-copyable to avoid the issue.

Fixes: 39482d59fe ("DmaBufAllocator: Add Dma Buffer synchronization function & helper class")
Signed-off-by: Harvey Yang <chenghaoyang@chromium.org>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-07 09:46:34 +00:00
Barnabás Pőcze
4f9d8a6301 libcamera: virtual: Speed up test pattern animation
After the initial generation, when a frame is requested,
the test pattern generator rotates the image left by 1 column.

The current approach has two shortcomings:
  (1) it allocates a temporary buffer to hold one column;
  (2) it swaps two columns at a time.

The test patterns are simple ARGB images, in row-major order,
so doing (2) works against memory prefetching. This can be
addressed by doing the rotation one row at a time as that way
the image is addressed in a purely linear fashion. Doing so also
eliminates the need for a dynamically allocated temporary buffer,
as the required buffer now only needs to hold one sample,
which is 4 bytes in this case.

In an optimized build, this results in about a 2x increase in the
number of frames per second as reported by `cam`. In an unoptimized,
ASAN and UBSAN intrumented build, the difference is even bigger,
which is useful for running lc-compliance in CI in a reasonable time.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-06 10:18:44 +01:00
Barnabás Pőcze
fe33e727f2 libcamera: virtual: Query number of planes correctly
`PixelFormatInfo::planes.size()` always returns 3 since `planes` is
an array, but that is not the number of planes of the pixel format.
Use the `numPlanes()` getter instead.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-06 10:18:44 +01:00
Barnabás Pőcze
277a28691c libcamera: virtual: Avoid some copies
There is no reason make copies, these functions return
const lvalue references, access the data through those.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-01-06 10:18:44 +01:00
309 changed files with 38759 additions and 2017 deletions

View file

@ -57,7 +57,8 @@ GENERATE_LATEX = NO
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
INCLUDE_PATH = "@TOP_SRCDIR@/include/libcamera"
INCLUDE_PATH = "@TOP_BUILDDIR@/include" \
"@TOP_SRCDIR@/include"
INCLUDE_FILE_PATTERNS = *.h
IMAGE_PATH = "@TOP_SRCDIR@/Documentation/images"

View file

@ -26,6 +26,7 @@ EXCLUDE = @TOP_SRCDIR@/include/libcamera/base/span.h \
@TOP_SRCDIR@/src/libcamera/ipc_pipe_unixsocket.cpp \
@TOP_SRCDIR@/src/libcamera/pipeline/ \
@TOP_SRCDIR@/src/libcamera/sensor/camera_sensor_legacy.cpp \
@TOP_SRCDIR@/src/libcamera/sensor/camera_sensor_raw.cpp \
@TOP_SRCDIR@/src/libcamera/tracepoints.cpp \
@TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \
@TOP_BUILDDIR@/include/libcamera/ipa/soft_ipa_interface.h \

331
Documentation/design/ae.rst Normal file
View file

@ -0,0 +1,331 @@
.. SPDX-License-Identifier: CC-BY-SA-4.0
Design of Exposure and Gain controls
====================================
This document explains the design and rationale of the controls related to
exposure and gain. This includes the all-encompassing auto-exposure (AE), the
manual exposure control, and the manual gain control.
Description of the problem
--------------------------
Sub controls
^^^^^^^^^^^^
There are more than one control that make up total exposure: exposure time,
gain, and aperture (though for now we will not consider aperture). We already
had individual controls for setting the values of manual exposure and manual
gain, but for switching between auto mode and manual mode we only had a
high-level boolean AeEnable control that would set *both* exposure and gain to
auto mode or manual mode; we had no way to set one to auto and the other to
manual.
So, we need to introduce two new controls to act as "levers" to indicate
individually for exposure and gain if the value would come from AEGC or if it
would come from the manual control value.
Aperture priority
^^^^^^^^^^^^^^^^^
We eventually may need to support aperture, and so whatever our solution is for
having only some controls on auto and the others on manual needs to be
extensible.
Flickering when going from auto to manual
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When a manual exposure or gain value is requested by the application, it costs
a few frames worth of time for them to take effect. This means that during a
transition from auto to manual, there would be flickering in the control values
and the transition won't be smooth.
Take for instance the following flow, where we start on auto exposure (which
for the purposes of the example increments by 1 each frame) and we want to
switch seamlessly to manual exposure, which involves copying the exposure value
computed by the auto exposure algorithm:
::
+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+
| N | | N+1 | | N+2 | | N+3 | | N+4 | | N+5 | | N+6 |
+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+
Mode requested: Auto Auto Auto Manual Manual Manual Manual
Exp requested: N/A N/A N/A 2 2 2 2
Set in Frame: N+2 N+3 N+4 N+5 N+6 N+7 N+8
Mode used: Auto Auto Auto Auto Auto Manual Manual
Exp used: 0 1 2 3 4 2 2
As we can see, after frame N+2 completes, we copy the exposure value that was
used for frame N+2 (which was computed by AE algorithm), and queue that value
into request N+3 with manual mode on. However, as it takes two frames for the
exposure to be set, the exposure still changes since it is set by AE, and we
get a flicker in the exposure during the switch from auto to manual.
A solution is to *not submit* any exposure value when manual mode is enabled,
and wait until the manual mode as been "applied" before copying the exposure
value:
::
+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+
| N | | N+1 | | N+2 | | N+3 | | N+4 | | N+5 | | N+6 |
+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+
Mode requested: Auto Auto Auto Manual Manual Manual Manual
Exp requested: N/A N/A N/A None None None 5
Set in Frame: N+2 N+3 N+4 N+5 N+6 N+7 N+8
Mode used: Auto Auto Auto Auto Auto Manual Manual
Exp used: 0 1 2 3 4 5 5
In practice, this works. However, libcamera has a policy where once a control
is submitted, its value is saved and does not need to be resubmitted. If the
manual exposure value was set while auto mode was on, in theory the value would
be saved, so when manual mode is enabled, the exposure value that was
previously set would immediately be used. Clearly this solution isn't correct,
but it can serve as the basis for a proper solution, with some more rigorous
rules.
Existing solutions
------------------
Raspberry Pi
^^^^^^^^^^^^
The Raspberry Pi IPA gets around the lack of individual AeEnable controls for
exposure and gain by using magic values. When AeEnable is false, if one of the
manual control values was set to 0 then the value computed by AEGC would be
used for just that control. This solution isn't desirable, as it prevents
that magic value from being used as a valid value.
To get around the flickering issue, when AeEnable is false, the Raspberry Pi
AEGC simply stops updating the values to be set, without restoring the
previously set manual exposure time and gain. This works, but is not a proper
solution.
Android
^^^^^^^
The Android HAL specification requires that exposure and gain (sensitivity)
must both be manual or both be auto. It cannot be that one is manual while the
other is auto, so they simply don't support sub controls.
For the flickering issue, the Android HAL has an AeLock control. To transition
from auto to manual, the application would keep AE on auto, and turn on the
lock. Once the lock has propagated through, then the value can be copied from
the result into the request and the lock disabled and the mode set to manual.
The problem with this solution is, besides the extra complexity, that it is
ambiguous what happens if there is a state transition from manual to locked
(even though it's a state transition that doesn't make sense). If locked is
defined to "use the last automatically computed values" then it could use the
values from the last time it AE was set to auto, or it would be undefined if AE
was never auto (eg. it started out as manual), or if AE is implemented to run
in the background it could just use the current values that are computed. If
locked is defined to "use the last value that was set" there would be less
ambiguity. Still, it's better if we can make it impossible to execute this
nonsensical state transition, and if we can reduce the complexity of having
this extra control or extra setting on a lever.
Summary of goals
----------------
- We need a lock of some sort, to instruct the AEGC to not update output
results
- We need manual modes, to override the values computed by the AEGC
- We need to support seamless transitions from auto to manual, and do so
without flickering
- We need custom minimum values for the manual controls; that is, no magic
values for enabling/disabling auto
- All of these need to be done with AE sub-controls (exposure time, analogue
gain) and be extensible to aperture in the future
Our solution
------------
A diagram of our solution:
::
+----------------------------+-------------+------------------+-----------------+
| INPUT | ALGORITHM | RESULT | OUTPUT |
+----------------------------+-------------+------------------+-----------------+
ExposureTimeMode ExposureTimeMode
---------------------+----------------------------------------+----------------->
0: Auto | |
1: Manual | V
| |\
| | \
| /----------------------------------> | 1| ExposureTime
| | +-------------+ exposure time | | -------------->
\--)--> | | --------------> | 0|
ExposureTime | | | | /
------------------------+--> | | |/
| | AeState
| AEGC | ----------------------------------->
AnalogueGain | |
------------------------+--> | | |\
| | | | \
/--)--> | | --------------> | 0| AnalogueGain
| | +-------------+ analogue gain | | -------------->
| \----------------------------------> | 1|
| | /
| |/
| ^
AnalogueGainMode | | AnalogueGainMode
---------------------+----------------------------------------+----------------->
0: Auto
1: Manual
AeEnable
- True -> ExposureTimeMode:Auto + AnalogueGainMode:Auto
- False -> ExposureTimeMode:Manual + AnalogueGainMode:Manual
The diagram is divided in four sections horizontally:
- Input: The values received from the request controls
- Algorithm: The algorithm itself
- Result: The values calculated by the algorithm
- Output: The values reported in result metadata and applied to the device
The four input controls are divided between manual values (ExposureTime and
AnalogueGain), and operation modes (ExposureTimeMode and AnalogueGainMode). The
former are the manual values, the latter control how they're applied. The two
modes are independent from each other, and each can take one of two values:
- Auto (0): The AGC computes the value normally. The AGC result is applied
to the output. The manual value is ignored *and is not retained*.
- Manual (1): The AGC uses the manual value internally. The corresponding
manual control from the request is applied to the output. The AGC result
is ignored.
The AeState control reports the state of the unified AEGC block. If both
ExposureTimeMode and AnalogueGainMode are set to manual then it will report
Idle. If at least one of the two is set to auto, then AeState will report
if the AEGC has Converged or not (Searching). This control replaces the old
AeLocked control, as it was insufficient for reporting the AE state.
There is a caveat to manual mode: the manual control value is not retained if
it is set during auto mode. This means that if manual mode is entered without
also setting the manual value, then it will enter a state similar to "locked",
where the last automatically computed value while the mode was auto will be
used. Once the manual value is set, then that will be used and retained as
usual.
This simulates an auto -> locked -> manual or auto -> manual state transition,
and makes it impossible to do the nonsensical manual -> locked state
transition.
AeEnable still exists to allow applications to set the mode of all the
sub-controls at once. Besides being for convenience, this will also be useful
when we eventually implement an aperture control. This is because applications
that will be made before aperture will have been available would still be able
to set aperture mode to auto or manual, as opposed to having the aperture stuck
at auto while the application really wanted manual. Although the aperture would
still be stuck at an uncontrollable value, at least it would be at a static
usable value as opposed to varying via the AEGC algorithm.
With this solution, the earlier example would become:
::
+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+
| N+2 | | N+3 | | N+4 | | N+5 | | N+6 | | N+7 | | N+8 | | N+9 | | N+10|
+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+
Mode requested: Auto Manual Manual Manual Manual Manual Manual Manual Manual
Exp requested: N/A None None None None 10 None 10 10
Set in Frame: N+4 N+5 N+6 N+7 N+8 N+9 N+10 N+11 N+12
Mode used: Auto Auto Auto Manual Manual Manual Manual Manual Manual
Exp used: 2 3 4 5 5 5 5 10 10
This example is extended by a few frames to exhibit the simulated "locked"
state. At frame N+5 the application has confirmed that the manual mode has been
entered, but does not provide a manual value until request N+7. Thus, the value
that is used in requests N+5 and N+6 (where the mode is disabled), comes from
the last value that was used when the mode was auto, which comes from frame
N+4.
Then, in N+7, a manual value of 10 is supplied. It takes until frame N+9 for
the exposure to be applied. N+8 does not supply a manual value, but the last
supplied value is retained, so a manual value of 10 is still used and set in
frame N+10.
Although this behavior is the same as what we had with waiting for the manual
mode to propagate (in the section "Description of the problem"), this time it
is correct as we have defined specifically that if a manual value was specified
while the mode was auto, it will not be retained.
Description of the controls
---------------------------
As described above, libcamera offers the following controls related to exposure
and gain:
- AnalogueGain
- AnalogueGainMode
- ExposureTime
- ExposureTimeMode
- AeState
- AeEnable
Auto-exposure and auto-gain can be enabled and disabled separately using the
ExposureTimeMode and AnalogueGainMode controls respectively. The AeEnable
control can also be used, as it sets both of the modes simultaneously. The
AeEnable control is not returned in metadata.
When the respective mode is set to auto, the respective value that is computed
by the AEGC algorithm is applied to the image sensor. Any value that is
supplied in the manual ExposureTime/AnalogueGain control is ignored and not
retained. Another way to understand this is that when the mode transitions from
auto to manual, the internally stored control value is overwritten with the
last value computed by the auto algorithm.
This means that when we transition from auto to manual without supplying a
manual control value, the last value that was set by the AEGC algorithm will
keep be used. This can be used to do a flickerless transition from auto to
manual as described earlier. If the camera started out in manual mode and no
corresponding value has been supplied yet, then a best-effort default value
shall be set.
The manual control value can be set in the same request as setting the mode to
auto if the desired manual control value is already known.
Transitioning from manual to auto shall be implicitly flickerless, as the AEGC
algorithms are expected to start running from the last manual value.
The AeState metadata reports the state of the AE algorithm. As AE cannot
compute exposure and gain separately, the state of the AE component is
unified. There are three states: Idle, Searching, and Converged.
The state shall be Idle if both ExposureTimeMode and AnalogueGainMode
are set to Manual. If the camera only supports one of the two controls,
then the state shall be Idle if that one control is set to Manual. If
the camera does not support Manual for at least one of the two controls,
then the state will never be Idle, as AE will always be running.
The state shall be Searching if at least one of exposure or gain calculated
by the AE algorithm is used (that is, at least one of the two modes is Auto),
*and* the value(s) have not converged yet.
The state shall be Converged if at least one of exposure or gain calculated
by the AE algorithm is used (that is, at least one of the two modes is Auto),
*and* the value(s) have converged.

View file

@ -57,8 +57,8 @@ LIBCAMERA_RPI_CONFIG_FILE
Example value: ``/usr/local/share/libcamera/pipeline/rpi/vc4/minimal_mem.yaml``
LIBCAMERA_RPI_TUNING_FILE
Define a custom JSON tuning file to use in the Raspberry Pi.
LIBCAMERA_<NAME>_TUNING_FILE
Define a custom IPA tuning file to use with the pipeline handler `NAME`.
Example value: ``/usr/local/share/libcamera/ipa/rpi/vc4/custom_sensor.json``

View file

@ -128,7 +128,7 @@ available.
std::string cameraId = cameras[0]->id();
auto camera = cm->get(cameraId);
camera = cm->get(cameraId);
/*
* Note that `camera` may not compare equal to `cameras[0]`.
* In fact, it might simply be a `nullptr`, as the particular
@ -618,7 +618,7 @@ accordingly. In this example, the application file has been named
simple_cam = executable('simple-cam',
'simple-cam.cpp',
dependencies: dependency('libcamera', required : true))
dependencies: dependency('libcamera'))
The ``dependencies`` line instructs meson to ask ``pkgconfig`` (or ``cmake``) to
locate the ``libcamera`` library, which the test application will be

View file

@ -186,7 +186,7 @@ to the libcamera build options in the top level ``meson_options.txt``.
option('pipelines',
type : 'array',
choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'uvcvideo', 'vimc', 'vivid'],
choices : ['ipu3', 'rkisp1', 'rpi/pisp', 'rpi/vc4', 'simple', 'uvcvideo', 'vimc', 'vivid'],
description : 'Select which pipeline handlers to include')
@ -213,7 +213,7 @@ implementations for the overridden class members.
std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
int start(Camera *camera, const ControlList *controls) override;
void stop(Camera *camera) override;
void stopDevice(Camera *camera) override;
int queueRequestDevice(Camera *camera, Request *request) override;
@ -247,7 +247,7 @@ implementations for the overridden class members.
return -1;
}
void PipelineHandlerVivid::stop(Camera *camera)
void PipelineHandlerVivid::stopDevice(Camera *camera)
{
}
@ -521,14 +521,14 @@ handler and camera manager using `registerCamera`_.
Finally with a successful construction, we return 'true' indicating that the
PipelineHandler successfully matched and constructed a device.
.. _Camera::create: https://libcamera.org/api-html/classlibcamera_1_1Camera.html#a453740e0d2a2f495048ae307a85a2574
.. _Camera::create: https://libcamera.org/internal-api-html/classlibcamera_1_1Camera.html#adf5e6c22411f953bfaa1ae21155d6c31
.. _registerCamera: https://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html#adf02a7f1bbd87aca73c0e8d8e0e6c98b
.. code-block:: cpp
std::set<Stream *> streams{ &data->stream_ };
std::shared_ptr<Camera> camera = Camera::create(this, data->video_->deviceName(), streams);
registerCamera(std::move(camera), std::move(data));
std::shared_ptr<Camera> camera = Camera::create(std::move(data), data->video_->deviceName(), streams);
registerCamera(std::move(camera));
return true;
@ -554,8 +554,7 @@ Our match function should now look like the following:
/* Create and register the camera. */
std::set<Stream *> streams{ &data->stream_ };
const std::string &id = data->video_->deviceName();
std::shared_ptr<Camera> camera = Camera::create(data.release(), id, streams);
std::shared_ptr<Camera> camera = Camera::create(std::move(data), data->video_->deviceName(), streams);
registerCamera(std::move(camera));
return true;
@ -593,11 +592,11 @@ immutable properties of the ``Camera`` device.
The libcamera controls and properties are defined in YAML form which is
processed to automatically generate documentation and interfaces. Controls are
defined by the src/libcamera/`control_ids_core.yaml`_ file and camera properties
are defined by src/libcamera/`properties_ids_core.yaml`_.
are defined by src/libcamera/`property_ids_core.yaml`_.
.. _controls framework: https://libcamera.org/api-html/controls_8h.html
.. _control_ids_core.yaml: https://libcamera.org/api-html/control__ids_8h.html
.. _properties_ids_core.yaml: https://libcamera.org/api-html/property__ids_8h.html
.. _property_ids_core.yaml: https://libcamera.org/api-html/property__ids_8h.html
Pipeline handlers can optionally register the list of controls an application
can set as well as a list of immutable camera properties. Being both
@ -800,8 +799,7 @@ derived class, and assign it to a base class pointer.
.. code-block:: cpp
VividCameraData *data = cameraData(camera);
CameraConfiguration *config = new VividCameraConfiguration();
auto config = std::make_unique<VividCameraConfiguration>();
A ``CameraConfiguration`` is specific to each pipeline, so you can only create
it from the pipeline handler code path. Applications can also generate an empty
@ -829,9 +827,7 @@ To generate a ``StreamConfiguration``, you need a list of pixel formats and
frame sizes which are supported as outputs of the stream. You can fetch a map of
the ``V4LPixelFormat`` and ``SizeRange`` supported by the underlying output
device, but the pipeline handler needs to convert this to a
``libcamera::PixelFormat`` type to pass to applications. We do this here using
``std::transform`` to convert the formats and populate a new ``PixelFormat`` map
as shown below.
``libcamera::PixelFormat`` type to pass to applications.
Continue adding the following code example to our ``generateConfiguration``
implementation.
@ -841,14 +837,12 @@ implementation.
std::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =
data->video_->formats();
std::map<PixelFormat, std::vector<SizeRange>> deviceFormats;
std::transform(v4l2Formats.begin(), v4l2Formats.end(),
std::inserter(deviceFormats, deviceFormats.begin()),
[&](const decltype(v4l2Formats)::value_type &format) {
return decltype(deviceFormats)::value_type{
format.first.toPixelFormat(),
format.second
};
});
for (auto &[v4l2PixelFormat, sizes] : v4l2Formats) {
PixelFormat pixelFormat = v4l2PixelFormat.toPixelFormat();
if (pixelFormat.isValid())
deviceFormats.try_emplace(pixelFormat, std::move(sizes));
}
The `StreamFormats`_ class holds information about the pixel formats and frame
sizes that a stream can support. The class groups size information by the pixel
@ -938,9 +932,9 @@ Add the following function implementation to your file:
StreamConfiguration &cfg = config_[0];
const std::vector<libcamera::PixelFormat> formats = cfg.formats().pixelformats();
const std::vector<libcamera::PixelFormat> &formats = cfg.formats().pixelformats();
if (std::find(formats.begin(), formats.end(), cfg.pixelFormat) == formats.end()) {
cfg.pixelFormat = cfg.formats().pixelformats()[0];
cfg.pixelFormat = formats[0];
LOG(VIVID, Debug) << "Adjusting format to " << cfg.pixelFormat.toString();
status = Adjusted;
}
@ -1158,7 +1152,7 @@ available to the devices which have to be started and ready to produce
images. At the end of a capture session the ``Camera`` device needs to be
stopped, to gracefully clean up any allocated memory and stop the hardware
devices. Pipeline handlers implement two functions for these purposes, the
``start()`` and ``stop()`` functions.
``start()`` and ``stopDevice()`` functions.
The memory initialization phase that happens at ``start()`` time serves to
configure video devices to be able to use memory buffers exported as dma-buf
@ -1261,8 +1255,8 @@ algorithms, or other devices you should also stop them.
.. _releaseBuffers: https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#a191619c152f764e03bc461611f3fcd35
Of course we also need to handle the corresponding actions to stop streaming on
a device, Add the following to the ``stop`` function, to stop the stream with
the `streamOff`_ function and release all buffers.
a device, Add the following to the ``stopDevice()`` function, to stop the
stream with the `streamOff`_ function and release all buffers.
.. _streamOff: https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#a61998710615bdf7aa25a046c8565ed66

View file

@ -23,7 +23,9 @@
SoftwareISP Benchmarking <software-isp-benchmarking>
Tracing guide <guides/tracing>
Design document: AE <design/ae>
.. toctree::
:hidden:
introduction
introduction

View file

@ -116,10 +116,8 @@ endif
# Sphinx
#
sphinx = find_program('sphinx-build-3', required : false)
if not sphinx.found()
sphinx = find_program('sphinx-build', required : get_option('documentation'))
endif
sphinx = find_program('sphinx-build-3', 'sphinx-build',
required : get_option('documentation'))
if sphinx.found()
docs_sources = [
@ -128,6 +126,7 @@ if sphinx.found()
'coding-style.rst',
'conf.py',
'contributing.rst',
'design/ae.rst',
'documentation-contents.rst',
'environment_variables.rst',
'feature_requirements.rst',

View file

@ -44,7 +44,7 @@ A C++ toolchain: [required]
Either {g++, clang}
Meson Build system: [required]
meson (>= 0.60) ninja-build pkg-config
meson (>= 0.63) ninja-build pkg-config
for the libcamera core: [required]
libyaml-dev python3-yaml python3-ply python3-jinja2
@ -83,9 +83,10 @@ for cam: [optional]
- libdrm-dev: Enables the KMS sink
- libjpeg-dev: Enables MJPEG on the SDL sink
- libsdl2-dev: Enables the SDL sink
- libtiff-dev: Enables writing DNG
for qcam: [optional]
libtiff-dev qt6-base-dev qt6-tools-dev-tools
libtiff-dev qt6-base-dev
for tracing with lttng: [optional]
liblttng-ust-dev python3-jinja2 lttng-tools
@ -93,9 +94,6 @@ for tracing with lttng: [optional]
for android: [optional]
libexif-dev libjpeg-dev
for Python bindings: [optional]
pybind11-dev
for lc-compliance: [optional]
libevent-dev libgtest-dev

View file

@ -98,21 +98,15 @@ public:
using PackType = BoundMethodPack<R, Args...>;
private:
template<std::size_t... I, typename T = R>
std::enable_if_t<!std::is_void<T>::value, void>
invokePack(BoundMethodPackBase *pack, std::index_sequence<I...>)
template<std::size_t... I>
void invokePack(BoundMethodPackBase *pack, std::index_sequence<I...>)
{
PackType *args = static_cast<PackType *>(pack);
args->ret_ = invoke(std::get<I>(args->args_)...);
}
[[maybe_unused]] auto *args = static_cast<PackType *>(pack);
template<std::size_t... I, typename T = R>
std::enable_if_t<std::is_void<T>::value, void>
invokePack(BoundMethodPackBase *pack, std::index_sequence<I...>)
{
/* args is effectively unused when the sequence I is empty. */
PackType *args [[gnu::unused]] = static_cast<PackType *>(pack);
invoke(std::get<I>(args->args_)...);
if constexpr (!std::is_void_v<R>)
args->ret_ = invoke(std::get<I>(args->args_)...);
else
invoke(std::get<I>(args->args_)...);
}
public:

View file

@ -1,14 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2021, Google Inc.
*
* Compiler support
*/
#pragma once
#if __cplusplus >= 201703L
#define __nodiscard [[nodiscard]]
#else
#define __nodiscard
#endif

View file

@ -7,7 +7,9 @@
#pragma once
#include <atomic>
#include <sstream>
#include <string_view>
#include <libcamera/base/private.h>
@ -28,19 +30,22 @@ enum LogSeverity {
class LogCategory
{
public:
static LogCategory *create(const char *name);
static LogCategory *create(std::string_view name);
const std::string &name() const { return name_; }
LogSeverity severity() const { return severity_; }
void setSeverity(LogSeverity severity);
LogSeverity severity() const { return severity_.load(std::memory_order_relaxed); }
void setSeverity(LogSeverity severity) { severity_.store(severity, std::memory_order_relaxed); }
static const LogCategory &defaultCategory();
private:
explicit LogCategory(const char *name);
friend class Logger;
explicit LogCategory(std::string_view name);
const std::string name_;
LogSeverity severity_;
std::atomic<LogSeverity> severity_;
static_assert(decltype(severity_)::is_always_lock_free);
};
#define LOG_DECLARE_CATEGORY(name) \
@ -60,9 +65,7 @@ class LogMessage
public:
LogMessage(const char *fileName, unsigned int line,
const LogCategory &category, LogSeverity severity,
const std::string &prefix = std::string());
LogMessage(LogMessage &&);
std::string prefix = {});
~LogMessage();
std::ostream &stream() { return msgStream_; }
@ -75,9 +78,7 @@ public:
const std::string msg() const { return msgStream_.str(); }
private:
LIBCAMERA_DISABLE_COPY(LogMessage)
void init(const char *fileName, unsigned int line);
LIBCAMERA_DISABLE_COPY_AND_MOVE(LogMessage)
std::ostringstream msgStream_;
const LogCategory &category_;

View file

@ -5,7 +5,6 @@ libcamera_base_include_dir = libcamera_include_dir / 'base'
libcamera_base_public_headers = files([
'bound_method.h',
'class.h',
'compiler.h',
'flags.h',
'object.h',
'shared_fd.h',

View file

@ -23,10 +23,6 @@ namespace libcamera {
class LIBCAMERA_TSA_CAPABILITY("mutex") Mutex final
{
public:
constexpr Mutex()
{
}
void lock() LIBCAMERA_TSA_ACQUIRE()
{
mutex_.lock();
@ -84,10 +80,6 @@ private:
class ConditionVariable final
{
public:
ConditionVariable()
{
}
void notify_one() noexcept
{
cv_.notify_one();

View file

@ -9,9 +9,11 @@
#include <list>
#include <memory>
#include <utility>
#include <vector>
#include <libcamera/base/bound_method.h>
#include <libcamera/base/class.h>
namespace libcamera {
@ -38,7 +40,7 @@ public:
{
T *obj = static_cast<T *>(this);
auto *method = new BoundMethodMember<T, R, FuncArgs...>(obj, this, func, type);
return method->activate(args..., true);
return method->activate(std::forward<Args>(args)..., true);
}
Thread *thread() const { return thread_; }
@ -52,6 +54,8 @@ protected:
bool assertThreadBound(const char *message);
private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(Object)
friend class SignalBase;
friend class Thread;

View file

@ -63,11 +63,8 @@ public:
#ifndef __DOXYGEN__
template<typename T, typename Func,
std::enable_if_t<std::is_base_of<Object, T>::value
#if __cplusplus >= 201703L
&& std::is_invocable_v<Func, Args...>
#endif
> * = nullptr>
std::enable_if_t<std::is_base_of<Object, T>::value &&
std::is_invocable_v<Func, Args...>> * = nullptr>
void connect(T *obj, Func func, ConnectionType type = ConnectionTypeAuto)
{
Object *object = static_cast<Object *>(obj);
@ -75,11 +72,8 @@ public:
}
template<typename T, typename Func,
std::enable_if_t<!std::is_base_of<Object, T>::value
#if __cplusplus >= 201703L
&& std::is_invocable_v<Func, Args...>
#endif
> * = nullptr>
std::enable_if_t<!std::is_base_of<Object, T>::value &&
std::is_invocable_v<Func, Args...>> * = nullptr>
#else
template<typename T, typename Func>
#endif

View file

@ -346,13 +346,7 @@ public:
}
constexpr Span(const Span &other) noexcept = default;
constexpr Span &operator=(const Span &other) noexcept
{
data_ = other.data_;
size_ = other.size_;
return *this;
}
constexpr Span &operator=(const Span &other) noexcept = default;
constexpr iterator begin() const { return data(); }
constexpr const_iterator cbegin() const { return begin(); }

View file

@ -13,6 +13,7 @@
#include <libcamera/base/private.h>
#include <libcamera/base/class.h>
#include <libcamera/base/message.h>
#include <libcamera/base/signal.h>
#include <libcamera/base/span.h>
@ -47,13 +48,16 @@ public:
EventDispatcher *eventDispatcher();
void dispatchMessages(Message::Type type = Message::Type::None);
void dispatchMessages(Message::Type type = Message::Type::None,
Object *receiver = nullptr);
protected:
int exec();
virtual void run();
private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(Thread)
void startThread();
void finishThread();

View file

@ -10,7 +10,6 @@
#include <utility>
#include <libcamera/base/class.h>
#include <libcamera/base/compiler.h>
namespace libcamera {
@ -43,7 +42,7 @@ public:
return *this;
}
__nodiscard int release()
[[nodiscard]] int release()
{
int fd = fd_;
fd_ = -1;

View file

@ -13,6 +13,7 @@
#include <iterator>
#include <ostream>
#include <sstream>
#include <stdint.h>
#include <string.h>
#include <string>
#include <sys/time.h>

View file

@ -9,6 +9,7 @@
#include <memory>
#include <string>
#include <string_view>
#include <sys/types.h>
#include <vector>
@ -31,7 +32,7 @@ public:
void stop();
std::vector<std::shared_ptr<Camera>> cameras() const;
std::shared_ptr<Camera> get(const std::string &id);
std::shared_ptr<Camera> get(std::string_view id);
static const std::string &version() { return version_; }

View file

@ -120,12 +120,12 @@ struct control_type<Point> {
};
template<typename T, std::size_t N>
struct control_type<Span<T, N>> : public control_type<std::remove_cv_t<T>> {
struct control_type<Span<T, N>, std::enable_if_t<control_type<std::remove_cv_t<T>>::size == 0>> : public control_type<std::remove_cv_t<T>> {
static constexpr std::size_t size = N;
};
template<typename T>
struct control_type<T, std::enable_if_t<std::is_enum_v<T>>> : public control_type<int32_t> {
struct control_type<T, std::enable_if_t<std::is_enum_v<T> && sizeof(T) == sizeof(int32_t)>> : public control_type<int32_t> {
};
} /* namespace details */

View file

@ -26,6 +26,7 @@ struct FrameMetadata {
FrameSuccess,
FrameError,
FrameCancelled,
FrameStartup,
};
struct Plane {

View file

@ -11,8 +11,6 @@
#include <ostream>
#include <string>
#include <libcamera/base/compiler.h>
namespace libcamera {
class Rectangle;
@ -110,8 +108,8 @@ public:
return *this;
}
__nodiscard constexpr Size alignedDownTo(unsigned int hAlignment,
unsigned int vAlignment) const
[[nodiscard]] constexpr Size alignedDownTo(unsigned int hAlignment,
unsigned int vAlignment) const
{
return {
width / hAlignment * hAlignment,
@ -119,8 +117,8 @@ public:
};
}
__nodiscard constexpr Size alignedUpTo(unsigned int hAlignment,
unsigned int vAlignment) const
[[nodiscard]] constexpr Size alignedUpTo(unsigned int hAlignment,
unsigned int vAlignment) const
{
return {
(width + hAlignment - 1) / hAlignment * hAlignment,
@ -128,7 +126,7 @@ public:
};
}
__nodiscard constexpr Size boundedTo(const Size &bound) const
[[nodiscard]] constexpr Size boundedTo(const Size &bound) const
{
return {
std::min(width, bound.width),
@ -136,7 +134,7 @@ public:
};
}
__nodiscard constexpr Size expandedTo(const Size &expand) const
[[nodiscard]] constexpr Size expandedTo(const Size &expand) const
{
return {
std::max(width, expand.width),
@ -144,7 +142,7 @@ public:
};
}
__nodiscard constexpr Size grownBy(const Size &margins) const
[[nodiscard]] constexpr Size grownBy(const Size &margins) const
{
return {
width + margins.width,
@ -152,7 +150,7 @@ public:
};
}
__nodiscard constexpr Size shrunkBy(const Size &margins) const
[[nodiscard]] constexpr Size shrunkBy(const Size &margins) const
{
return {
width > margins.width ? width - margins.width : 0,
@ -160,10 +158,10 @@ public:
};
}
__nodiscard Size boundedToAspectRatio(const Size &ratio) const;
__nodiscard Size expandedToAspectRatio(const Size &ratio) const;
[[nodiscard]] Size boundedToAspectRatio(const Size &ratio) const;
[[nodiscard]] Size expandedToAspectRatio(const Size &ratio) const;
__nodiscard Rectangle centeredTo(const Point &center) const;
[[nodiscard]] Rectangle centeredTo(const Point &center) const;
Size operator*(float factor) const;
Size operator/(float factor) const;
@ -294,11 +292,11 @@ public:
Rectangle &scaleBy(const Size &numerator, const Size &denominator);
Rectangle &translateBy(const Point &point);
__nodiscard Rectangle boundedTo(const Rectangle &bound) const;
__nodiscard Rectangle enclosedIn(const Rectangle &boundary) const;
__nodiscard Rectangle scaledBy(const Size &numerator,
const Size &denominator) const;
__nodiscard Rectangle translatedBy(const Point &point) const;
[[nodiscard]] Rectangle boundedTo(const Rectangle &bound) const;
[[nodiscard]] Rectangle enclosedIn(const Rectangle &boundary) const;
[[nodiscard]] Rectangle scaledBy(const Size &numerator,
const Size &denominator) const;
[[nodiscard]] Rectangle translatedBy(const Point &point) const;
Rectangle transformedBetween(const Rectangle &source,
const Rectangle &target) const;

View file

@ -11,6 +11,7 @@
#include <list>
#include <memory>
#include <set>
#include <stdint.h>
#include <string>
#include <libcamera/base/class.h>

View file

@ -7,6 +7,7 @@
#pragma once
#include <memory>
#include <stdint.h>
#include <string>
#include <libcamera/base/class.h>

View file

@ -8,6 +8,7 @@
#pragma once
#include <memory>
#include <stdint.h>
#include <string>
#include <variant>
#include <vector>
@ -62,6 +63,11 @@ public:
Transform transform = Transform::Identity,
V4L2SubdeviceFormat *sensorFormat = nullptr) = 0;
virtual V4L2Subdevice::Stream imageStream() const;
virtual std::optional<V4L2Subdevice::Stream> embeddedDataStream() const;
virtual V4L2SubdeviceFormat embeddedDataFormat() const;
virtual int setEmbeddedDataEnabled(bool enable);
virtual const ControlList &properties() const = 0;
virtual int sensorInfo(IPACameraSensorInfo *info) const = 0;
virtual Transform computeTransform(Orientation *orientation) const = 0;

View file

@ -0,0 +1,68 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2024, Raspberry Pi Ltd
*
* Camera recovery algorithm
*/
#pragma once
#include <stdint.h>
namespace libcamera {
class ClockRecovery
{
public:
ClockRecovery();
void configure(unsigned int numSamples = 100, unsigned int maxJitter = 2000,
unsigned int minSamples = 10, unsigned int errorThreshold = 50000);
void reset();
void addSample();
void addSample(uint64_t input, uint64_t output);
uint64_t getOutput(uint64_t input);
private:
/* Approximate number of samples over which the model state persists. */
unsigned int numSamples_;
/* Remove any output jitter larger than this immediately. */
unsigned int maxJitter_;
/* Number of samples required before we start to use model estimates. */
unsigned int minSamples_;
/* Threshold above which we assume the wallclock has been reset. */
unsigned int errorThreshold_;
/* How many samples seen (up to numSamples_). */
unsigned int count_;
/* This gets subtracted from all input values, just to make the numbers easier. */
uint64_t inputBase_;
/* As above, for the output. */
uint64_t outputBase_;
/* The previous input sample. */
uint64_t lastInput_;
/* The previous output sample. */
uint64_t lastOutput_;
/* Average x value seen so far. */
double xAve_;
/* Average y value seen so far */
double yAve_;
/* Average x^2 value seen so far. */
double x2Ave_;
/* Average x*y value seen so far. */
double xyAve_;
/*
* The latest estimate of linear parameters to derive the output clock
* from the input.
*/
double slope_;
double offset_;
/* Use this cumulative error to monitor for spontaneous clock updates. */
double error_;
};
} /* namespace libcamera */

View file

@ -10,13 +10,15 @@
#include <stdint.h>
#include <unordered_map>
#include <libcamera/base/object.h>
#include <libcamera/controls.h>
namespace libcamera {
class V4L2Device;
class DelayedControls
class DelayedControls : public Object
{
public:
struct ControlParams {

View file

@ -8,6 +8,7 @@
#pragma once
#include <memory>
#include <stdint.h>
#include <string>
#include <vector>
@ -60,9 +61,14 @@ public:
explicit DmaSyncer(SharedFD fd, SyncType type = SyncType::ReadWrite);
DmaSyncer(DmaSyncer &&other) = default;
DmaSyncer &operator=(DmaSyncer &&other) = default;
~DmaSyncer();
private:
LIBCAMERA_DISABLE_COPY(DmaSyncer)
void sync(uint64_t step);
SharedFD fd_;

View file

@ -8,6 +8,7 @@
#pragma once
#include <memory>
#include <stdint.h>
#include <utility>
#include <libcamera/base/class.h>

View file

@ -7,6 +7,7 @@
#pragma once
#include <stdint.h>
#include <string.h>
#include <tuple>
#include <type_traits>
@ -308,7 +309,6 @@ public:
serialize(const Flags<E> &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
{
std::vector<uint8_t> dataVec;
dataVec.reserve(sizeof(Flags<E>));
appendPOD<uint32_t>(dataVec, static_cast<typename Flags<E>::Type>(data));
return { dataVec, {} };

View file

@ -7,6 +7,7 @@
#pragma once
#include <memory>
#include <stdint.h>
#include <vector>
@ -67,7 +68,7 @@ private:
bool isSignatureValid(IPAModule *ipa) const;
std::vector<IPAModule *> modules_;
std::vector<std::unique_ptr<IPAModule>> modules_;
#if HAVE_IPA_PUBKEY
static const uint8_t publicKeyData_[];

View file

@ -29,7 +29,7 @@ public:
bool isValid() const;
const struct IPAModuleInfo &info() const;
const std::vector<uint8_t> signature() const;
const std::vector<uint8_t> &signature() const;
const std::string &path() const;
bool load();

View file

@ -7,6 +7,7 @@
#pragma once
#include <stdint.h>
#include <vector>
#include <libcamera/base/shared_fd.h>

View file

@ -9,6 +9,7 @@
#include <map>
#include <memory>
#include <stdint.h>
#include "libcamera/internal/ipc_pipe.h"
#include "libcamera/internal/ipc_unixsocket.h"

View file

@ -8,6 +8,7 @@
#include <algorithm>
#include <sstream>
#include <type_traits>
#include <vector>
#include <libcamera/base/log.h>
@ -20,17 +21,19 @@ namespace libcamera {
LOG_DECLARE_CATEGORY(Matrix)
#ifndef __DOXYGEN__
template<typename T, unsigned int Rows, unsigned int Cols,
std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
#else
template<typename T, unsigned int Rows, unsigned int Cols>
template<typename T>
bool matrixInvert(Span<const T> dataIn, Span<T> dataOut, unsigned int dim,
Span<T> scratchBuffer, Span<unsigned int> swapBuffer);
#endif /* __DOXYGEN__ */
template<typename T, unsigned int Rows, unsigned int Cols>
class Matrix
{
static_assert(std::is_arithmetic_v<T>, "Matrix type must be arithmetic");
public:
Matrix()
constexpr Matrix()
{
data_.fill(static_cast<T>(0));
}
Matrix(const std::array<T, Rows * Cols> &data)
@ -38,7 +41,12 @@ public:
std::copy(data.begin(), data.end(), data_.begin());
}
static Matrix identity()
Matrix(const Span<const T, Rows * Cols> data)
{
std::copy(data.begin(), data.end(), data_.begin());
}
static constexpr Matrix identity()
{
Matrix ret;
for (size_t i = 0; i < std::min(Rows, Cols); i++)
@ -66,12 +74,14 @@ public:
return out.str();
}
Span<const T, Cols> operator[](size_t i) const
constexpr Span<const T, Rows * Cols> data() const { return data_; }
constexpr Span<const T, Cols> operator[](size_t i) const
{
return Span<const T, Cols>{ &data_.data()[i * Cols], Cols };
}
Span<T, Cols> operator[](size_t i)
constexpr Span<T, Cols> operator[](size_t i)
{
return Span<T, Cols>{ &data_.data()[i * Cols], Cols };
}
@ -88,8 +98,30 @@ public:
return *this;
}
Matrix<T, Rows, Cols> inverse(bool *ok = nullptr) const
{
static_assert(Rows == Cols, "Matrix must be square");
Matrix<T, Rows, Cols> inverse;
std::array<T, Rows * Cols * 2> scratchBuffer;
std::array<unsigned int, Rows> swapBuffer;
bool res = matrixInvert(Span<const T>(data_),
Span<T>(inverse.data_),
Rows,
Span<T>(scratchBuffer),
Span<unsigned int>(swapBuffer));
if (ok)
*ok = res;
return inverse;
}
private:
std::array<T, Rows * Cols> data_;
/*
* \todo The initializer is only necessary for the constructor to be
* constexpr in C++17. Remove the initializer as soon as we are on
* C++20.
*/
std::array<T, Rows * Cols> data_ = {};
};
#ifndef __DOXYGEN__
@ -121,21 +153,16 @@ Matrix<U, Rows, Cols> operator*(const Matrix<U, Rows, Cols> &m, T d)
return d * m;
}
#ifndef __DOXYGEN__
template<typename T,
unsigned int R1, unsigned int C1,
unsigned int R2, unsigned int C2,
std::enable_if_t<C1 == R2> * = nullptr>
#else
template<typename T, unsigned int R1, unsigned int C1, unsigned int R2, unsigned in C2>
#endif /* __DOXYGEN__ */
Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1> &m1, const Matrix<T, R2, C2> &m2)
template<typename T1, unsigned int R1, unsigned int C1, typename T2, unsigned int R2, unsigned int C2>
constexpr Matrix<std::common_type_t<T1, T2>, R1, C2> operator*(const Matrix<T1, R1, C1> &m1,
const Matrix<T2, R2, C2> &m2)
{
Matrix<T, R1, C2> result;
static_assert(C1 == R2, "Matrix dimensions must match for multiplication");
Matrix<std::common_type_t<T1, T2>, R1, C2> result;
for (unsigned int i = 0; i < R1; i++) {
for (unsigned int j = 0; j < C2; j++) {
T sum = 0;
std::common_type_t<T1, T2> sum = 0;
for (unsigned int k = 0; k < C1; k++)
sum += m1[i][k] * m2[k][j];
@ -148,7 +175,7 @@ Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1> &m1, const Matrix<T, R2, C2>
}
template<typename T, unsigned int Rows, unsigned int Cols>
Matrix<T, Rows, Cols> operator+(const Matrix<T, Rows, Cols> &m1, const Matrix<T, Rows, Cols> &m2)
constexpr Matrix<T, Rows, Cols> operator+(const Matrix<T, Rows, Cols> &m1, const Matrix<T, Rows, Cols> &m2)
{
Matrix<T, Rows, Cols> result;

View file

@ -55,6 +55,8 @@ public:
Signal<> disconnected;
std::vector<MediaEntity *> locateEntities(unsigned int function);
protected:
std::string logPrefix() const override;

View file

@ -112,7 +112,7 @@ public:
unsigned int deviceMinor() const { return minor_; }
const std::vector<MediaPad *> &pads() const { return pads_; }
const std::vector<MediaEntity *> ancillaryEntities() const { return ancillaryEntities_; }
const std::vector<MediaEntity *> &ancillaryEntities() const { return ancillaryEntities_; }
const MediaPad *getPadByIndex(unsigned int index) const;
const MediaPad *getPadById(unsigned int id) const;

View file

@ -0,0 +1,59 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2024, Ideas on Board Oy
*
* Media pipeline support
*/
#pragma once
#include <list>
#include <string>
#include <libcamera/base/log.h>
namespace libcamera {
class CameraSensor;
class MediaEntity;
class MediaLink;
class MediaPad;
struct V4L2SubdeviceFormat;
class MediaPipeline
{
public:
int init(MediaEntity *source, std::string_view sink);
int initLinks();
int configure(CameraSensor *sensor, V4L2SubdeviceFormat *);
private:
struct Entity {
/* The media entity, always valid. */
MediaEntity *entity;
/*
* Whether or not the entity is a subdev that supports the
* routing API.
*/
bool supportsRouting;
/*
* The local sink pad connected to the upstream entity, null for
* the camera sensor at the beginning of the pipeline.
*/
const MediaPad *sink;
/*
* The local source pad connected to the downstream entity, null
* for the video node at the end of the pipeline.
*/
const MediaPad *source;
/*
* The link on the source pad, to the downstream entity, null
* for the video node at the end of the pipeline.
*/
MediaLink *sourceLink;
};
std::list<Entity> entities_;
};
} /* namespace libcamera */

View file

@ -11,6 +11,7 @@ libcamera_internal_headers = files([
'camera_manager.h',
'camera_sensor.h',
'camera_sensor_properties.h',
'clock_recovery.h',
'control_serializer.h',
'control_validator.h',
'converter.h',
@ -32,6 +33,7 @@ libcamera_internal_headers = files([
'matrix.h',
'media_device.h',
'media_object.h',
'media_pipeline.h',
'pipeline_handler.h',
'process.h',
'pub_key.h',
@ -43,6 +45,7 @@ libcamera_internal_headers = files([
'v4l2_pixelformat.h',
'v4l2_subdevice.h',
'v4l2_videodevice.h',
'vector.h',
'yaml_parser.h',
])

View file

@ -63,7 +63,8 @@ public:
void cancelRequest(Request *request);
std::string configurationFile(const std::string &subdir,
const std::string &name) const;
const std::string &name,
bool silent = false) const;
const char *name() const { return name_; }

View file

@ -11,6 +11,7 @@
#include <string>
#include <vector>
#include <libcamera/base/class.h>
#include <libcamera/base/signal.h>
#include <libcamera/base/unique_fd.h>
@ -42,6 +43,8 @@ public:
Signal<enum ExitStatus, int> finished;
private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(Process)
void closeAllFdsExcept(const std::vector<int> &fds);
int isolate();
void died(int wstatus);

View file

@ -10,6 +10,7 @@
#include <chrono>
#include <map>
#include <memory>
#include <stdint.h>
#include <unordered_set>
#include <libcamera/base/event_notifier.h>

View file

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2023, 2024 Red Hat Inc.
* Copyright (C) 2023-2025 Red Hat Inc.
*
* Authors:
* Hans de Goede <hdegoede@redhat.com>
@ -18,11 +18,37 @@ namespace libcamera {
struct DebayerParams {
static constexpr unsigned int kRGBLookupSize = 256;
using ColorLookupTable = std::array<uint8_t, kRGBLookupSize>;
struct CcmColumn {
int16_t r;
int16_t g;
int16_t b;
};
ColorLookupTable red;
ColorLookupTable green;
ColorLookupTable blue;
using LookupTable = std::array<uint8_t, kRGBLookupSize>;
using CcmLookupTable = std::array<CcmColumn, kRGBLookupSize>;
/*
* Color lookup tables when CCM is not used.
*
* Each color of a debayered pixel is amended by the corresponding
* value in the given table.
*/
LookupTable red;
LookupTable green;
LookupTable blue;
/*
* Color and gamma lookup tables when CCM is used.
*
* Each of the CcmLookupTable's corresponds to a CCM column; together they
* make a complete 3x3 CCM lookup table. The CCM is applied on debayered
* pixels and then the gamma lookup table is used to set the resulting
* values of all the three colors.
*/
CcmLookupTable redCcm;
CcmLookupTable greenCcm;
CcmLookupTable blueCcm;
LookupTable gammaLut;
};
} /* namespace libcamera */

View file

@ -7,6 +7,7 @@
#pragma once
#include <deque>
#include <functional>
#include <initializer_list>
#include <map>
@ -18,6 +19,7 @@
#include <libcamera/base/class.h>
#include <libcamera/base/log.h>
#include <libcamera/base/object.h>
#include <libcamera/base/signal.h>
#include <libcamera/base/thread.h>
@ -43,7 +45,7 @@ struct StreamConfiguration;
LOG_DECLARE_CATEGORY(SoftwareIsp)
class SoftwareIsp
class SoftwareIsp : public Object
{
public:
SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
@ -83,6 +85,7 @@ public:
Signal<FrameBuffer *> inputBufferReady;
Signal<FrameBuffer *> outputBufferReady;
Signal<uint32_t, uint32_t> ispStatsReady;
Signal<uint32_t, const ControlList &> metadataReady;
Signal<const ControlList &> setSensorControls;
private:
@ -97,8 +100,11 @@ private:
SharedMemObject<DebayerParams> sharedParams_;
DebayerParams debayerParams_;
DmaBufAllocator dmaHeap_;
bool ccmEnabled_;
std::unique_ptr<ipa::soft::IPAProxySoft> ipa_;
std::deque<FrameBuffer *> queuedInputBuffers_;
std::deque<FrameBuffer *> queuedOutputBuffers_;
};
} /* namespace libcamera */

View file

@ -5,6 +5,8 @@
* request.tp - Tracepoints for the request object
*/
#include <stdint.h>
#include <libcamera/framebuffer.h>
#include "libcamera/internal/request.h"

View file

@ -10,6 +10,7 @@
#include <map>
#include <memory>
#include <optional>
#include <stdint.h>
#include <vector>
#include <linux/videodev2.h>
@ -44,6 +45,7 @@ public:
const std::string &deviceNode() const { return deviceNode_; }
std::string devicePath() const;
bool supportsFrameStartEvent();
int setFrameStartEnabled(bool enable);
Signal<uint32_t> frameStart;

View file

@ -49,6 +49,8 @@ public:
static const std::vector<V4L2PixelFormat> &
fromPixelFormat(const PixelFormat &pixelFormat);
bool isGenericLineBasedMetadata() const;
private:
uint32_t fourcc_;
};

View file

@ -10,6 +10,7 @@
#include <memory>
#include <optional>
#include <ostream>
#include <stdint.h>
#include <string>
#include <vector>

View file

@ -8,7 +8,6 @@
#pragma once
#include <array>
#include <atomic>
#include <memory>
#include <optional>
#include <ostream>
@ -158,7 +157,7 @@ private:
std::vector<Plane> planes_;
};
std::atomic<uint64_t> lastUsedCounter_;
uint64_t lastUsedCounter_;
std::vector<Entry> cache_;
/* \todo Expose the miss counter through an instrumentation API. */
unsigned int missCounter_;

View file

@ -13,6 +13,7 @@
#include <numeric>
#include <optional>
#include <ostream>
#include <type_traits>
#include <libcamera/base/log.h>
#include <libcamera/base/span.h>
@ -24,8 +25,6 @@ namespace libcamera {
LOG_DECLARE_CATEGORY(Vector)
namespace ipa {
#ifndef __DOXYGEN__
template<typename T, unsigned int Rows,
std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
@ -44,8 +43,12 @@ public:
constexpr Vector(const std::array<T, Rows> &data)
{
for (unsigned int i = 0; i < Rows; i++)
data_[i] = data[i];
std::copy(data.begin(), data.end(), data_.begin());
}
constexpr Vector(const Span<const T, Rows> data)
{
std::copy(data.begin(), data.end(), data_.begin());
}
const T &operator[](size_t i) const
@ -293,13 +296,13 @@ private:
template<typename T>
using RGB = Vector<T, 3>;
template<typename T, unsigned int Rows, unsigned int Cols>
Vector<T, Rows> operator*(const Matrix<T, Rows, Cols> &m, const Vector<T, Cols> &v)
template<typename T, typename U, unsigned int Rows, unsigned int Cols>
Vector<std::common_type_t<T, U>, Rows> operator*(const Matrix<T, Rows, Cols> &m, const Vector<U, Cols> &v)
{
Vector<T, Rows> result;
Vector<std::common_type_t<T, U>, Rows> result;
for (unsigned int i = 0; i < Rows; i++) {
T sum = 0;
std::common_type_t<T, U> sum = 0;
for (unsigned int j = 0; j < Cols; j++)
sum += m[i][j] * v[j];
result[i] = sum;
@ -329,11 +332,9 @@ bool operator!=(const Vector<T, Rows> &lhs, const Vector<T, Rows> &rhs)
bool vectorValidateYaml(const YamlObject &obj, unsigned int size);
#endif /* __DOXYGEN__ */
} /* namespace ipa */
#ifndef __DOXYGEN__
template<typename T, unsigned int Rows>
std::ostream &operator<<(std::ostream &out, const ipa::Vector<T, Rows> &v)
std::ostream &operator<<(std::ostream &out, const Vector<T, Rows> &v)
{
out << "Vector { ";
for (unsigned int i = 0; i < Rows; i++) {
@ -346,13 +347,13 @@ std::ostream &operator<<(std::ostream &out, const ipa::Vector<T, Rows> &v)
}
template<typename T, unsigned int Rows>
struct YamlObject::Getter<ipa::Vector<T, Rows>> {
std::optional<ipa::Vector<T, Rows>> get(const YamlObject &obj) const
struct YamlObject::Getter<Vector<T, Rows>> {
std::optional<Vector<T, Rows>> get(const YamlObject &obj) const
{
if (!ipa::vectorValidateYaml(obj, Rows))
if (!vectorValidateYaml(obj, Rows))
return std::nullopt;
ipa::Vector<T, Rows> vector;
Vector<T, Rows> vector;
unsigned int i = 0;
for (const YamlObject &entry : obj.asList()) {

View file

@ -66,6 +66,7 @@ pipeline_ipa_mojom_mapping = {
'ipu3': 'ipu3.mojom',
'mali-c55': 'mali-c55.mojom',
'rkisp1': 'rkisp1.mojom',
'rpi/pisp': 'raspberrypi.mojom',
'rpi/vc4': 'raspberrypi.mojom',
'simple': 'soft.mojom',
'vimc': 'vimc.mojom',

View file

@ -52,7 +52,8 @@ struct ConfigResult {
struct StartResult {
libcamera.ControlList controls;
int32 dropFrameCount;
int32 startupFrameCount;
int32 invalidFrameCount;
};
struct PrepareParams {

View file

@ -16,8 +16,9 @@ interface IPASoftInterface {
init(libcamera.IPASettings settings,
libcamera.SharedFD fdStats,
libcamera.SharedFD fdParams,
libcamera.ControlInfoMap sensorCtrlInfoMap)
=> (int32 ret, libcamera.ControlInfoMap ipaControls);
libcamera.IPACameraSensorInfo sensorInfo,
libcamera.ControlInfoMap sensorControls)
=> (int32 ret, libcamera.ControlInfoMap ipaControls, bool ccmEnabled);
start() => (int32 ret);
stop();
configure(IPAConfigInfo configInfo)
@ -33,4 +34,5 @@ interface IPASoftInterface {
interface IPASoftEventInterface {
setSensorControls(libcamera.ControlList sensorControls);
setIspParams();
metadataReady(uint32 frame, libcamera.ControlList metadata);
};

View file

@ -37,6 +37,7 @@ controls_map = {
'core': 'control_ids_core.yaml',
'debug': 'control_ids_debug.yaml',
'draft': 'control_ids_draft.yaml',
'rpi/pisp': 'control_ids_rpi.yaml',
'rpi/vc4': 'control_ids_rpi.yaml',
},
@ -89,6 +90,7 @@ foreach mode, entry : controls_map
command : [gen_controls, '-o', '@OUTPUT@',
'--mode', mode, '-t', template_file,
'-r', ranges_file, '@INPUT@'],
depend_files : [py_mod_controls],
env : py_build_env,
install : true,
install_dir : libcamera_headers_install_dir)

View file

@ -1,4 +1,4 @@
# SPDX-License-Identifier: CC0-1.0
Files in this directory are imported from next-media-rkisp1-20240814-14-ga043ea54bbb9 of the Linux kernel. Do not
Files in this directory are imported from v6.13-rc1-68-gf9bbbd9a696d of the Linux kernel. Do not
modify them manually.

View file

@ -188,4 +188,8 @@
#define MEDIA_BUS_FMT_META_20 0x8006
#define MEDIA_BUS_FMT_META_24 0x8007
/* Specific metadata formats. Next is 0x9003. */
#define MEDIA_BUS_FMT_CCS_EMBEDDED 0x9001
#define MEDIA_BUS_FMT_OV2740_EMBEDDED 0x9002
#endif /* __LINUX_MEDIA_BUS_FORMAT_H */

View file

@ -206,6 +206,7 @@ struct media_entity_desc {
#define MEDIA_PAD_FL_SINK (1U << 0)
#define MEDIA_PAD_FL_SOURCE (1U << 1)
#define MEDIA_PAD_FL_MUST_CONNECT (1U << 2)
#define MEDIA_PAD_FL_INTERNAL (1U << 3)
struct media_pad_desc {
__u32 entity; /* entity ID */

View file

@ -204,6 +204,11 @@ struct v4l2_subdev_capability {
* on a video node.
*/
#define V4L2_SUBDEV_ROUTE_FL_ACTIVE (1U << 0)
/*
* Is the route immutable? The ACTIVE flag of an immutable route may not be
* unset.
*/
#define V4L2_SUBDEV_ROUTE_FL_IMMUTABLE (1U << 1)
/**
* struct v4l2_subdev_route - A route inside a subdev

View file

@ -843,6 +843,18 @@ struct v4l2_pix_format {
#define V4L2_META_FMT_MALI_C55_PARAMS v4l2_fourcc('C', '5', '5', 'P') /* ARM Mali-C55 Parameters */
#define V4L2_META_FMT_MALI_C55_3A_STATS v4l2_fourcc('C', '5', '5', 'S') /* ARM Mali-C55 3A Statistics */
/*
* Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when
* adding new ones!
*/
#define V4L2_META_FMT_GENERIC_8 v4l2_fourcc('M', 'E', 'T', '8') /* Generic 8-bit metadata */
#define V4L2_META_FMT_GENERIC_CSI2_10 v4l2_fourcc('M', 'C', '1', 'A') /* 10-bit CSI-2 packed 8-bit metadata */
#define V4L2_META_FMT_GENERIC_CSI2_12 v4l2_fourcc('M', 'C', '1', 'C') /* 12-bit CSI-2 packed 8-bit metadata */
#define V4L2_META_FMT_GENERIC_CSI2_14 v4l2_fourcc('M', 'C', '1', 'E') /* 14-bit CSI-2 packed 8-bit metadata */
#define V4L2_META_FMT_GENERIC_CSI2_16 v4l2_fourcc('M', 'C', '1', 'G') /* 16-bit CSI-2 packed 8-bit metadata */
#define V4L2_META_FMT_GENERIC_CSI2_20 v4l2_fourcc('M', 'C', '1', 'K') /* 20-bit CSI-2 packed 8-bit metadata */
#define V4L2_META_FMT_GENERIC_CSI2_24 v4l2_fourcc('M', 'C', '1', 'O') /* 24-bit CSI-2 packed 8-bit metadata */
/* priv field value to indicates that subsequent fields are valid. */
#define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe

View file

@ -2,7 +2,7 @@
project('libcamera', 'c', 'cpp',
meson_version : '>= 0.63',
version : '0.4.0',
version : '0.5.1',
default_options : [
'werror=true',
'warning_level=2',
@ -110,7 +110,9 @@ common_arguments = [
]
c_arguments = []
cpp_arguments = []
cpp_arguments = [
'-Wnon-virtual-dtor',
]
cxx_stdlib = 'libstdc++'
@ -204,7 +206,7 @@ liblttng = dependency('lttng-ust', required : get_option('tracing'))
# Pipeline handlers
#
pipelines = get_option('pipelines')
wanted_pipelines = get_option('pipelines')
arch_arm = ['arm', 'aarch64']
arch_x86 = ['x86', 'x86_64']
@ -213,6 +215,7 @@ pipelines_support = {
'ipu3': arch_x86,
'mali-c55': arch_arm,
'rkisp1': arch_arm,
'rpi/pisp': arch_arm,
'rpi/vc4': arch_arm,
'simple': ['any'],
'uvcvideo': ['any'],
@ -220,16 +223,18 @@ pipelines_support = {
'virtual': ['test'],
}
if pipelines.contains('all')
if wanted_pipelines.contains('all')
pipelines = pipelines_support.keys()
elif pipelines.contains('auto')
elif wanted_pipelines.contains('auto')
host_cpu = host_machine.cpu_family()
pipelines = []
foreach pipeline, archs : pipelines_support
if host_cpu in archs or 'any' in archs
if pipeline in wanted_pipelines or host_cpu in archs or 'any' in archs
pipelines += pipeline
endif
endforeach
else
pipelines = wanted_pipelines
endif
# Tests require the vimc pipeline handler, include it automatically when tests

View file

@ -18,6 +18,7 @@ option('cam',
option('documentation',
type : 'feature',
value : 'auto',
description : 'Generate the project documentation')
option('doc_werror',
@ -32,7 +33,8 @@ option('gstreamer',
option('ipas',
type : 'array',
choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],
choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/pisp', 'rpi/vc4', 'simple',
'vimc'],
description : 'Select which IPA modules to build')
option('lc-compliance',
@ -50,6 +52,7 @@ option('pipelines',
'ipu3',
'mali-c55',
'rkisp1',
'rpi/pisp',
'rpi/vc4',
'simple',
'uvcvideo',
@ -84,6 +87,7 @@ option('udev',
description : 'Enable udev support for hotplug')
option('v4l2',
type : 'boolean',
value : false,
description : 'Compile the V4L2 compatibility layer')
type : 'feature',
value : 'auto',
description : 'Compile the V4L2 compatibility layer',
deprecated : {'true': 'enabled', 'false': 'disabled'})

View file

@ -1079,7 +1079,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
buffer.internalBuffer = frameBuffer;
descriptor->request_->addBuffer(sourceStream->stream(),
frameBuffer, nullptr);
frameBuffer);
requestedStreams.insert(sourceStream);
}

View file

@ -5,9 +5,12 @@
* Camera capture session
*/
#include "camera_session.h"
#include <iomanip>
#include <iostream>
#include <limits.h>
#include <optional>
#include <sstream>
#include <libcamera/control_ids.h>
@ -16,7 +19,6 @@
#include "../common/event_loop.h"
#include "../common/stream_options.h"
#include "camera_session.h"
#include "capture_script.h"
#include "file_sink.h"
#ifdef HAVE_KMS
@ -60,11 +62,32 @@ CameraSession::CameraSession(CameraManager *cm,
return;
}
std::vector<StreamRole> roles = StreamKeyValueParser::roles(options_[OptStream]);
std::vector<StreamRole> roles =
StreamKeyValueParser::roles(options_[OptStream]);
std::vector<std::vector<StreamRole>> tryRoles;
if (!roles.empty()) {
/*
* If the roles are explicitly specified then there's no need
* to try other roles
*/
tryRoles.push_back(roles);
} else {
tryRoles.push_back({ StreamRole::Viewfinder });
tryRoles.push_back({ StreamRole::Raw });
}
std::unique_ptr<CameraConfiguration> config =
camera_->generateConfiguration(roles);
if (!config || config->size() != roles.size()) {
std::unique_ptr<CameraConfiguration> config;
bool valid = false;
for (std::vector<StreamRole> &rolesIt : tryRoles) {
config = camera_->generateConfiguration(rolesIt);
if (config && config->size() == rolesIt.size()) {
roles = rolesIt;
valid = true;
break;
}
}
if (!valid) {
std::cerr << "Failed to get default stream configuration"
<< std::endl;
return;
@ -173,6 +196,11 @@ void CameraSession::listControls() const
std::cout << "Control: " << io.str()
<< id->vendor() << "::" << id->name() << ":"
<< std::endl;
std::optional<int32_t> def;
if (!info.def().isNone())
def = info.def().get<int32_t>();
for (const auto &value : info.values()) {
int32_t val = value.get<int32_t>();
const auto &it = id->enumerators().find(val);
@ -182,7 +210,10 @@ void CameraSession::listControls() const
std::cout << "UNKNOWN";
else
std::cout << it->second;
std::cout << " (" << val << ")" << std::endl;
std::cout << " (" << val << ")"
<< (val == def ? " [default]" : "")
<< std::endl;
}
}

View file

@ -8,6 +8,7 @@
#include "capture_script.h"
#include <iostream>
#include <memory>
#include <stdio.h>
#include <stdlib.h>
@ -521,45 +522,22 @@ ControlValue CaptureScript::parseArrayControl(const ControlId *id,
case ControlTypeNone:
break;
case ControlTypeBool: {
/*
* This is unpleasant, but we cannot use an std::vector<> as its
* boolean type overload does not allow to access the raw data,
* as boolean values are stored in a bitmask for efficiency.
*
* As we need a contiguous memory region to wrap in a Span<>,
* use an array instead but be strict about not overflowing it
* by limiting the number of controls we can store.
*
* Be loud but do not fail, as the issue would present at
* runtime and it's not fatal.
*/
static constexpr unsigned int kMaxNumBooleanControls = 1024;
std::array<bool, kMaxNumBooleanControls> values;
unsigned int idx = 0;
auto values = std::make_unique<bool[]>(repr.size());
for (const std::string &s : repr) {
bool val;
for (std::size_t i = 0; i < repr.size(); i++) {
const auto &s = repr[i];
if (s == "true") {
val = true;
values[i] = true;
} else if (s == "false") {
val = false;
values[i] = false;
} else {
unpackFailure(id, s);
return value;
}
if (idx == kMaxNumBooleanControls) {
std::cerr << "Cannot parse more than "
<< kMaxNumBooleanControls
<< " boolean controls" << std::endl;
break;
}
values[idx++] = val;
}
value = Span<bool>(values.data(), idx);
value = Span<bool>(values.get(), repr.size());
break;
}
case ControlTypeByte: {
@ -600,10 +578,6 @@ ControlValue CaptureScript::parseArrayControl(const ControlId *id,
value = Span<const float>(values.data(), values.size());
break;
}
case ControlTypeString: {
value = Span<const std::string>(repr.data(), repr.size());
break;
}
default:
std::cerr << "Unsupported control type" << std::endl;
break;

View file

@ -450,8 +450,6 @@ int Device::openCard()
}
for (struct dirent *res; (res = readdir(folder));) {
uint64_t cap;
if (strncmp(res->d_name, "card", 4))
continue;
@ -465,15 +463,22 @@ int Device::openCard()
}
/*
* Skip devices that don't support the modeset API, to avoid
* selecting a DRM device corresponding to a GPU. There is no
* modeset capability, but the kernel returns an error for most
* caps if mode setting isn't support by the driver. The
* DRM_CAP_DUMB_BUFFER capability is one of those, other would
* do as well. The capability value itself isn't relevant.
* Skip non-display devices. While this could in theory be done
* by checking for support of the mode setting API, some
* out-of-tree render-only GPU drivers (namely powervr)
* incorrectly set the DRIVER_MODESET driver feature. Check for
* the presence of at least one CRTC, encoder and connector
* instead.
*/
ret = drmGetCap(fd_, DRM_CAP_DUMB_BUFFER, &cap);
if (ret < 0) {
std::unique_ptr<drmModeRes, decltype(&drmModeFreeResources)> resources{
drmModeGetResources(fd_),
&drmModeFreeResources
};
if (!resources ||
resources->count_connectors <= 0 ||
resources->count_crtcs <= 0 ||
resources->count_encoders <= 0) {
resources.reset();
drmClose(fd_);
fd_ = -1;
continue;

View file

@ -5,6 +5,8 @@
* File Sink
*/
#include "file_sink.h"
#include <array>
#include <assert.h>
#include <fcntl.h>
@ -21,8 +23,6 @@
#include "../common/image.h"
#include "../common/ppm_writer.h"
#include "file_sink.h"
using namespace libcamera;
FileSink::FileSink([[maybe_unused]] const libcamera::Camera *camera,

View file

@ -11,6 +11,7 @@
#include <memory>
#include <string>
#include <libcamera/controls.h>
#include <libcamera/stream.h>
#include "frame_sink.h"

View file

@ -5,6 +5,8 @@
* cam - The libcamera swiss army knife
*/
#include "main.h"
#include <atomic>
#include <iomanip>
#include <iostream>
@ -19,7 +21,6 @@
#include "../common/stream_options.h"
#include "camera_session.h"
#include "main.h"
using namespace libcamera;

View file

@ -34,6 +34,7 @@ if libsdl2.found()
cam_sources += files([
'sdl_sink.cpp',
'sdl_texture.cpp',
'sdl_texture_1plane.cpp',
'sdl_texture_yuv.cpp',
])

View file

@ -11,6 +11,7 @@
#include <fcntl.h>
#include <iomanip>
#include <iostream>
#include <optional>
#include <signal.h>
#include <sstream>
#include <string.h>
@ -22,6 +23,7 @@
#include "../common/event_loop.h"
#include "../common/image.h"
#include "sdl_texture_1plane.h"
#ifdef HAVE_LIBJPEG
#include "sdl_texture_mjpg.h"
#endif
@ -31,6 +33,46 @@ using namespace libcamera;
using namespace std::chrono_literals;
namespace {
std::optional<SDL_PixelFormatEnum> singlePlaneFormatToSDL(const libcamera::PixelFormat &f)
{
switch (f) {
case libcamera::formats::RGB888:
return SDL_PIXELFORMAT_BGR24;
case libcamera::formats::BGR888:
return SDL_PIXELFORMAT_RGB24;
case libcamera::formats::RGBA8888:
return SDL_PIXELFORMAT_ABGR32;
case libcamera::formats::ARGB8888:
return SDL_PIXELFORMAT_BGRA32;
case libcamera::formats::BGRA8888:
return SDL_PIXELFORMAT_ARGB32;
case libcamera::formats::ABGR8888:
return SDL_PIXELFORMAT_RGBA32;
#if SDL_VERSION_ATLEAST(2, 29, 1)
case libcamera::formats::RGBX8888:
return SDL_PIXELFORMAT_XBGR32;
case libcamera::formats::XRGB8888:
return SDL_PIXELFORMAT_BGRX32;
case libcamera::formats::BGRX8888:
return SDL_PIXELFORMAT_XRGB32;
case libcamera::formats::XBGR8888:
return SDL_PIXELFORMAT_RGBX32;
#endif
case libcamera::formats::YUYV:
return SDL_PIXELFORMAT_YUY2;
case libcamera::formats::UYVY:
return SDL_PIXELFORMAT_UYVY;
case libcamera::formats::YVYU:
return SDL_PIXELFORMAT_YVYU;
}
return {};
}
} /* namespace */
SDLSink::SDLSink()
: window_(nullptr), renderer_(nullptr), rect_({}),
init_(false)
@ -62,25 +104,20 @@ int SDLSink::configure(const libcamera::CameraConfiguration &config)
rect_.w = cfg.size.width;
rect_.h = cfg.size.height;
switch (cfg.pixelFormat) {
if (auto sdlFormat = singlePlaneFormatToSDL(cfg.pixelFormat))
texture_ = std::make_unique<SDLTexture1Plane>(rect_, *sdlFormat, cfg.stride);
#ifdef HAVE_LIBJPEG
case libcamera::formats::MJPEG:
else if (cfg.pixelFormat == libcamera::formats::MJPEG)
texture_ = std::make_unique<SDLTextureMJPG>(rect_);
break;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 16)
case libcamera::formats::NV12:
else if (cfg.pixelFormat == libcamera::formats::NV12)
texture_ = std::make_unique<SDLTextureNV12>(rect_, cfg.stride);
break;
#endif
case libcamera::formats::YUYV:
texture_ = std::make_unique<SDLTextureYUYV>(rect_, cfg.stride);
break;
default:
std::cerr << "Unsupported pixel format "
<< cfg.pixelFormat.toString() << std::endl;
else {
std::cerr << "Unsupported pixel format " << cfg.pixelFormat << std::endl;
return -EINVAL;
};
}
return 0;
}

View file

@ -7,7 +7,7 @@
#pragma once
#include <vector>
#include <libcamera/base/span.h>
#include <SDL2/SDL.h>
@ -19,7 +19,7 @@ public:
SDLTexture(const SDL_Rect &rect, uint32_t pixelFormat, const int stride);
virtual ~SDLTexture();
int create(SDL_Renderer *renderer);
virtual void update(const std::vector<libcamera::Span<const uint8_t>> &data) = 0;
virtual void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) = 0;
SDL_Texture *get() const { return ptr_; }
protected:

View file

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025, Ideas on Board Oy
*
* SDL single plane textures
*/
#include "sdl_texture_1plane.h"
#include <assert.h>
void SDLTexture1Plane::update(libcamera::Span<const libcamera::Span<const uint8_t>> data)
{
assert(data.size() == 1);
assert(data[0].size_bytes() == std::size_t(rect_.h) * std::size_t(stride_));
SDL_UpdateTexture(ptr_, nullptr, data[0].data(), stride_);
}

View file

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025, Ideas on Board Oy
*
* SDL single plane textures
*/
#pragma once
#include "sdl_texture.h"
class SDLTexture1Plane final : public SDLTexture
{
public:
using SDLTexture::SDLTexture;
void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) override;
};

View file

@ -76,7 +76,7 @@ int SDLTextureMJPG::decompress(Span<const uint8_t> data)
return 0;
}
void SDLTextureMJPG::update(const std::vector<libcamera::Span<const uint8_t>> &data)
void SDLTextureMJPG::update(libcamera::Span<const libcamera::Span<const uint8_t>> data)
{
decompress(data[0]);
SDL_UpdateTexture(ptr_, nullptr, rgb_.get(), stride_);

View file

@ -14,7 +14,7 @@ class SDLTextureMJPG : public SDLTexture
public:
SDLTextureMJPG(const SDL_Rect &rect);
void update(const std::vector<libcamera::Span<const uint8_t>> &data) override;
void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) override;
private:
int decompress(libcamera::Span<const uint8_t> data);

View file

@ -15,19 +15,9 @@ SDLTextureNV12::SDLTextureNV12(const SDL_Rect &rect, unsigned int stride)
{
}
void SDLTextureNV12::update(const std::vector<libcamera::Span<const uint8_t>> &data)
void SDLTextureNV12::update(libcamera::Span<const libcamera::Span<const uint8_t>> data)
{
SDL_UpdateNVTexture(ptr_, &rect_, data[0].data(), stride_,
SDL_UpdateNVTexture(ptr_, nullptr, data[0].data(), stride_,
data[1].data(), stride_);
}
#endif
SDLTextureYUYV::SDLTextureYUYV(const SDL_Rect &rect, unsigned int stride)
: SDLTexture(rect, SDL_PIXELFORMAT_YUY2, stride)
{
}
void SDLTextureYUYV::update(const std::vector<libcamera::Span<const uint8_t>> &data)
{
SDL_UpdateTexture(ptr_, &rect_, data[0].data(), stride_);
}

View file

@ -14,13 +14,6 @@ class SDLTextureNV12 : public SDLTexture
{
public:
SDLTextureNV12(const SDL_Rect &rect, unsigned int stride);
void update(const std::vector<libcamera::Span<const uint8_t>> &data) override;
void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) override;
};
#endif
class SDLTextureYUYV : public SDLTexture
{
public:
SDLTextureYUYV(const SDL_Rect &rect, unsigned int stride);
void update(const std::vector<libcamera::Span<const uint8_t>> &data) override;
};

View file

@ -21,12 +21,35 @@ EventLoop::EventLoop()
evthread_use_pthreads();
base_ = event_base_new();
instance_ = this;
callsTrigger_ = event_new(base_, -1, EV_PERSIST, [](evutil_socket_t, short, void *closure) {
auto *self = static_cast<EventLoop *>(closure);
for (;;) {
std::function<void()> call;
{
std::lock_guard locker(self->lock_);
if (self->calls_.empty())
break;
call = std::move(self->calls_.front());
self->calls_.pop_front();
}
call();
}
}, this);
assert(callsTrigger_);
event_add(callsTrigger_, nullptr);
}
EventLoop::~EventLoop()
{
instance_ = nullptr;
event_free(callsTrigger_);
events_.clear();
event_base_free(base_);
libevent_global_shutdown();
@ -50,20 +73,20 @@ void EventLoop::exit(int code)
event_base_loopbreak(base_);
}
void EventLoop::callLater(const std::function<void()> &func)
void EventLoop::callLater(std::function<void()> &&func)
{
{
std::unique_lock<std::mutex> locker(lock_);
calls_.push_back(func);
calls_.push_back(std::move(func));
}
event_base_once(base_, -1, EV_TIMEOUT, dispatchCallback, this, nullptr);
event_active(callsTrigger_, 0, 0);
}
void EventLoop::addFdEvent(int fd, EventType type,
const std::function<void()> &callback)
std::function<void()> &&callback)
{
std::unique_ptr<Event> event = std::make_unique<Event>(callback);
std::unique_ptr<Event> event = std::make_unique<Event>(std::move(callback));
short events = (type & Read ? EV_READ : 0)
| (type & Write ? EV_WRITE : 0)
| EV_PERSIST;
@ -85,9 +108,9 @@ void EventLoop::addFdEvent(int fd, EventType type,
}
void EventLoop::addTimerEvent(const std::chrono::microseconds period,
const std::function<void()> &callback)
std::function<void()> &&callback)
{
std::unique_ptr<Event> event = std::make_unique<Event>(callback);
std::unique_ptr<Event> event = std::make_unique<Event>(std::move(callback));
event->event_ = event_new(base_, -1, EV_PERSIST, &EventLoop::Event::dispatch,
event.get());
if (!event->event_) {
@ -108,31 +131,8 @@ void EventLoop::addTimerEvent(const std::chrono::microseconds period,
events_.push_back(std::move(event));
}
void EventLoop::dispatchCallback([[maybe_unused]] evutil_socket_t fd,
[[maybe_unused]] short flags, void *param)
{
EventLoop *loop = static_cast<EventLoop *>(param);
loop->dispatchCall();
}
void EventLoop::dispatchCall()
{
std::function<void()> call;
{
std::unique_lock<std::mutex> locker(lock_);
if (calls_.empty())
return;
call = calls_.front();
calls_.pop_front();
}
call();
}
EventLoop::Event::Event(const std::function<void()> &callback)
: callback_(callback), event_(nullptr)
EventLoop::Event::Event(std::function<void()> &&callback)
: callback_(std::move(callback)), event_(nullptr)
{
}

View file

@ -8,11 +8,14 @@
#pragma once
#include <chrono>
#include <deque>
#include <functional>
#include <list>
#include <memory>
#include <mutex>
#include <libcamera/base/class.h>
#include <event2/util.h>
struct event_base;
@ -33,18 +36,20 @@ public:
int exec();
void exit(int code = 0);
void callLater(const std::function<void()> &func);
void callLater(std::function<void()> &&func);
void addFdEvent(int fd, EventType type,
const std::function<void()> &handler);
std::function<void()> &&handler);
using duration = std::chrono::steady_clock::duration;
void addTimerEvent(const std::chrono::microseconds period,
const std::function<void()> &handler);
std::function<void()> &&handler);
private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(EventLoop)
struct Event {
Event(const std::function<void()> &callback);
Event(std::function<void()> &&callback);
LIBCAMERA_DISABLE_COPY_AND_MOVE(Event)
~Event();
static void dispatch(int fd, short events, void *arg);
@ -58,11 +63,9 @@ private:
struct event_base *base_;
int exitCode_;
std::list<std::function<void()>> calls_;
std::deque<std::function<void()>> calls_;
struct event *callsTrigger_ = nullptr;
std::list<std::unique_ptr<Event>> events_;
std::mutex lock_;
static void dispatchCallback(evutil_socket_t fd, short flags,
void *param);
void dispatchCall();
};

View file

@ -98,12 +98,12 @@ unsigned int Image::numPlanes() const
Span<uint8_t> Image::data(unsigned int plane)
{
assert(plane <= planes_.size());
assert(plane < planes_.size());
return planes_[plane];
}
Span<const uint8_t> Image::data(unsigned int plane) const
{
assert(plane <= planes_.size());
assert(plane < planes_.size());
return planes_[plane];
}

View file

@ -1040,7 +1040,7 @@ void OptionsParser::usageOptions(const std::list<Option> &options,
std::cerr << std::setw(indent) << argument;
for (const char *help = option.help, *end = help; end; ) {
for (const char *help = option.help, *end = help; end;) {
end = strchr(help, '\n');
if (end) {
std::cerr << std::string(help, end - help + 1);

View file

@ -7,6 +7,7 @@
#include "ppm_writer.h"
#include <errno.h>
#include <fstream>
#include <iostream>
@ -28,7 +29,7 @@ int PPMWriter::write(const char *filename,
std::ofstream output(filename, std::ios::binary);
if (!output) {
std::cerr << "Failed to open ppm file: " << filename << std::endl;
return -EINVAL;
return -EIO;
}
output << "P6" << std::endl
@ -36,7 +37,7 @@ int PPMWriter::write(const char *filename,
<< "255" << std::endl;
if (!output) {
std::cerr << "Failed to write the file header" << std::endl;
return -EINVAL;
return -EIO;
}
const unsigned int rowLength = config.size.width * 3;
@ -45,7 +46,7 @@ int PPMWriter::write(const char *filename,
output.write(row, rowLength);
if (!output) {
std::cerr << "Failed to write image data at row " << y << std::endl;
return -EINVAL;
return -EIO;
}
}

View file

@ -42,9 +42,8 @@ KeyValueParser::Options StreamKeyValueParser::parse(const char *arguments)
std::vector<StreamRole> StreamKeyValueParser::roles(const OptionValue &values)
{
/* If no configuration values to examine default to viewfinder. */
if (values.empty())
return { StreamRole::Viewfinder };
return {};
const std::vector<OptionValue> &streamParameters = values.toArray();

View file

@ -23,5 +23,5 @@ private:
Environment() = default;
std::string cameraId_;
libcamera::CameraManager *cm_;
libcamera::CameraManager *cm_ = nullptr;
};

View file

@ -7,13 +7,14 @@
#include "capture.h"
#include <assert.h>
#include <gtest/gtest.h>
using namespace libcamera;
Capture::Capture(std::shared_ptr<Camera> camera)
: loop_(nullptr), camera_(camera),
allocator_(std::make_unique<FrameBufferAllocator>(camera))
: camera_(std::move(camera)), allocator_(camera_)
{
}
@ -22,14 +23,29 @@ Capture::~Capture()
stop();
}
void Capture::configure(StreamRole role)
void Capture::configure(libcamera::Span<const libcamera::StreamRole> roles)
{
config_ = camera_->generateConfiguration({ role });
assert(!roles.empty());
if (!config_) {
std::cout << "Role not supported by camera" << std::endl;
GTEST_SKIP();
}
config_ = camera_->generateConfiguration(roles);
if (!config_)
GTEST_SKIP() << "Roles not supported by camera";
ASSERT_EQ(config_->size(), roles.size()) << "Unexpected number of streams in configuration";
/*
* Set the buffers count to the largest value across all streams.
* \todo: Should all streams from a Camera have the same buffer count ?
*/
auto largest =
std::max_element(config_->begin(), config_->end(),
[](const StreamConfiguration &l, const StreamConfiguration &r)
{ return l.bufferCount < r.bufferCount; });
assert(largest != config_->end());
for (auto &cfg : *config_)
cfg.bufferCount = largest->bufferCount;
if (config_->validate() != CameraConfiguration::Valid) {
config_.reset();
@ -42,13 +58,98 @@ void Capture::configure(StreamRole role)
}
}
void Capture::run(unsigned int captureLimit, std::optional<unsigned int> queueLimit)
{
assert(!queueLimit || captureLimit <= *queueLimit);
captureLimit_ = captureLimit;
queueLimit_ = queueLimit;
captureCount_ = queueCount_ = 0;
EventLoop loop;
loop_ = &loop;
start();
for (const auto &request : requests_)
queueRequest(request.get());
EXPECT_EQ(loop_->exec(), 0);
stop();
EXPECT_LE(captureLimit_, captureCount_);
EXPECT_LE(captureCount_, queueCount_);
EXPECT_TRUE(!queueLimit_ || queueCount_ <= *queueLimit_);
}
int Capture::queueRequest(libcamera::Request *request)
{
if (queueLimit_ && queueCount_ >= *queueLimit_)
return 0;
int ret = camera_->queueRequest(request);
if (ret < 0)
return ret;
queueCount_ += 1;
return 0;
}
void Capture::requestComplete(Request *request)
{
captureCount_++;
if (captureCount_ >= captureLimit_) {
loop_->exit(0);
return;
}
EXPECT_EQ(request->status(), Request::Status::RequestComplete)
<< "Request didn't complete successfully";
request->reuse(Request::ReuseBuffers);
if (queueRequest(request))
loop_->exit(-EINVAL);
}
void Capture::start()
{
Stream *stream = config_->at(0).stream();
int count = allocator_->allocate(stream);
assert(config_);
assert(!config_->empty());
assert(!allocator_.allocated());
assert(requests_.empty());
ASSERT_GE(count, 0) << "Failed to allocate buffers";
EXPECT_EQ(count, config_->at(0).bufferCount) << "Allocated less buffers than expected";
const auto bufferCount = config_->at(0).bufferCount;
/* No point in testing less requests then the camera depth. */
if (queueLimit_ && *queueLimit_ < bufferCount) {
GTEST_SKIP() << "Camera needs " << bufferCount
<< " requests, can't test only " << *queueLimit_;
}
for (std::size_t i = 0; i < bufferCount; i++) {
std::unique_ptr<Request> request = camera_->createRequest();
ASSERT_TRUE(request) << "Can't create request";
requests_.push_back(std::move(request));
}
for (const auto &cfg : *config_) {
Stream *stream = cfg.stream();
int count = allocator_.allocate(stream);
ASSERT_GE(count, 0) << "Failed to allocate buffers";
const auto &buffers = allocator_.buffers(stream);
ASSERT_EQ(buffers.size(), bufferCount) << "Mismatching buffer count";
for (std::size_t i = 0; i < bufferCount; i++) {
ASSERT_EQ(requests_[i]->addBuffer(stream, buffers[i].get()), 0)
<< "Failed to add buffer to request";
}
}
ASSERT_TRUE(allocator_.allocated());
camera_->requestCompleted.connect(this, &Capture::requestComplete);
@ -57,140 +158,19 @@ void Capture::start()
void Capture::stop()
{
if (!config_ || !allocator_->allocated())
if (!config_ || !allocator_.allocated())
return;
camera_->stop();
camera_->requestCompleted.disconnect(this);
Stream *stream = config_->at(0).stream();
requests_.clear();
allocator_->free(stream);
}
/* CaptureBalanced */
CaptureBalanced::CaptureBalanced(std::shared_ptr<Camera> camera)
: Capture(camera)
{
}
void CaptureBalanced::capture(unsigned int numRequests)
{
start();
Stream *stream = config_->at(0).stream();
const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);
/* No point in testing less requests then the camera depth. */
if (buffers.size() > numRequests) {
std::cout << "Camera needs " + std::to_string(buffers.size())
+ " requests, can't test only "
+ std::to_string(numRequests) << std::endl;
GTEST_SKIP();
for (const auto &cfg : *config_) {
EXPECT_EQ(allocator_.free(cfg.stream()), 0)
<< "Failed to free buffers associated with stream";
}
queueCount_ = 0;
captureCount_ = 0;
captureLimit_ = numRequests;
/* Queue the recommended number of requests. */
for (const std::unique_ptr<FrameBuffer> &buffer : buffers) {
std::unique_ptr<Request> request = camera_->createRequest();
ASSERT_TRUE(request) << "Can't create request";
ASSERT_EQ(request->addBuffer(stream, buffer.get()), 0) << "Can't set buffer for request";
ASSERT_EQ(queueRequest(request.get()), 0) << "Failed to queue request";
requests_.push_back(std::move(request));
}
/* Run capture session. */
loop_ = new EventLoop();
loop_->exec();
stop();
delete loop_;
ASSERT_EQ(captureCount_, captureLimit_);
}
int CaptureBalanced::queueRequest(Request *request)
{
queueCount_++;
if (queueCount_ > captureLimit_)
return 0;
return camera_->queueRequest(request);
}
void CaptureBalanced::requestComplete(Request *request)
{
EXPECT_EQ(request->status(), Request::Status::RequestComplete)
<< "Request didn't complete successfully";
captureCount_++;
if (captureCount_ >= captureLimit_) {
loop_->exit(0);
return;
}
request->reuse(Request::ReuseBuffers);
if (queueRequest(request))
loop_->exit(-EINVAL);
}
/* CaptureUnbalanced */
CaptureUnbalanced::CaptureUnbalanced(std::shared_ptr<Camera> camera)
: Capture(camera)
{
}
void CaptureUnbalanced::capture(unsigned int numRequests)
{
start();
Stream *stream = config_->at(0).stream();
const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);
captureCount_ = 0;
captureLimit_ = numRequests;
/* Queue the recommended number of requests. */
for (const std::unique_ptr<FrameBuffer> &buffer : buffers) {
std::unique_ptr<Request> request = camera_->createRequest();
ASSERT_TRUE(request) << "Can't create request";
ASSERT_EQ(request->addBuffer(stream, buffer.get()), 0) << "Can't set buffer for request";
ASSERT_EQ(camera_->queueRequest(request.get()), 0) << "Failed to queue request";
requests_.push_back(std::move(request));
}
/* Run capture session. */
loop_ = new EventLoop();
int status = loop_->exec();
stop();
delete loop_;
ASSERT_EQ(status, 0);
}
void CaptureUnbalanced::requestComplete(Request *request)
{
captureCount_++;
if (captureCount_ >= captureLimit_) {
loop_->exit(0);
return;
}
EXPECT_EQ(request->status(), Request::Status::RequestComplete)
<< "Request didn't complete successfully";
request->reuse(Request::ReuseBuffers);
if (camera_->queueRequest(request))
loop_->exit(-EINVAL);
EXPECT_FALSE(allocator_.allocated());
}

View file

@ -8,6 +8,7 @@
#pragma once
#include <memory>
#include <optional>
#include <libcamera/libcamera.h>
@ -16,51 +17,29 @@
class Capture
{
public:
void configure(libcamera::StreamRole role);
protected:
Capture(std::shared_ptr<libcamera::Camera> camera);
virtual ~Capture();
~Capture();
void configure(libcamera::Span<const libcamera::StreamRole> roles);
void run(unsigned int captureLimit, std::optional<unsigned int> queueLimit = {});
private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(Capture)
void start();
void stop();
virtual void requestComplete(libcamera::Request *request) = 0;
EventLoop *loop_;
int queueRequest(libcamera::Request *request);
void requestComplete(libcamera::Request *request);
std::shared_ptr<libcamera::Camera> camera_;
std::unique_ptr<libcamera::FrameBufferAllocator> allocator_;
libcamera::FrameBufferAllocator allocator_;
std::unique_ptr<libcamera::CameraConfiguration> config_;
std::vector<std::unique_ptr<libcamera::Request>> requests_;
};
class CaptureBalanced : public Capture
{
public:
CaptureBalanced(std::shared_ptr<libcamera::Camera> camera);
void capture(unsigned int numRequests);
private:
int queueRequest(libcamera::Request *request);
void requestComplete(libcamera::Request *request) override;
unsigned int queueCount_;
unsigned int captureCount_;
unsigned int captureLimit_;
};
class CaptureUnbalanced : public Capture
{
public:
CaptureUnbalanced(std::shared_ptr<libcamera::Camera> camera);
void capture(unsigned int numRequests);
private:
void requestComplete(libcamera::Request *request) override;
unsigned int captureCount_;
unsigned int captureLimit_;
EventLoop *loop_ = nullptr;
unsigned int captureLimit_ = 0;
std::optional<unsigned int> queueLimit_;
unsigned int captureCount_ = 0;
unsigned int queueCount_ = 0;
};

View file

@ -45,13 +45,11 @@ class ThrowListener : public testing::EmptyTestEventListener
static void listCameras(CameraManager *cm)
{
for (const std::shared_ptr<Camera> &cam : cm->cameras())
std::cout << "- " << cam.get()->id() << std::endl;
std::cout << "- " << cam->id() << std::endl;
}
static int initCamera(CameraManager *cm, OptionsParser::Options options)
{
std::shared_ptr<Camera> camera;
int ret = cm->start();
if (ret) {
std::cout << "Failed to start camera manager: "
@ -66,7 +64,7 @@ static int initCamera(CameraManager *cm, OptionsParser::Options options)
}
const std::string &cameraId = options[OptCamera];
camera = cm->get(cameraId);
std::shared_ptr<Camera> camera = cm->get(cameraId);
if (!camera) {
std::cout << "Camera " << cameraId << " not found, available cameras:" << std::endl;
listCameras(cm);
@ -82,45 +80,27 @@ static int initCamera(CameraManager *cm, OptionsParser::Options options)
static int initGtestParameters(char *arg0, OptionsParser::Options options)
{
const std::map<std::string, std::string> gtestFlags = { { "list", "--gtest_list_tests" },
{ "filter", "--gtest_filter" } };
int argc = 0;
std::vector<const char *> argv;
std::string filterParam;
/*
* +2 to have space for both the 0th argument that is needed but not
* used and the null at the end.
*/
char **argv = new char *[(gtestFlags.size() + 2)];
if (!argv)
return -ENOMEM;
argv.push_back(arg0);
argv[0] = arg0;
argc++;
if (options.isSet(OptList)) {
argv[argc] = const_cast<char *>(gtestFlags.at("list").c_str());
argc++;
}
if (options.isSet(OptList))
argv.push_back("--gtest_list_tests");
if (options.isSet(OptFilter)) {
/*
* The filter flag needs to be passed as a single parameter, in
* the format --gtest_filter=filterStr
*/
filterParam = gtestFlags.at("filter") + "=" +
static_cast<const std::string &>(options[OptFilter]);
argv[argc] = const_cast<char *>(filterParam.c_str());
argc++;
filterParam = "--gtest_filter=" + options[OptFilter].toString();
argv.push_back(filterParam.c_str());
}
argv[argc] = nullptr;
argv.push_back(nullptr);
::testing::InitGoogleTest(&argc, argv);
delete[] argv;
int argc = argv.size();
::testing::InitGoogleTest(&argc, const_cast<char **>(argv.data()));
return 0;
}

View file

@ -15,6 +15,7 @@ lc_compliance_sources = files([
'environment.cpp',
'helpers/capture.cpp',
'main.cpp',
'test_base.cpp',
'tests/capture_test.cpp',
])

View file

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2021, Collabora Ltd.
*
* test_base.cpp - Base definitions for tests
*/
#include "test_base.h"
#include "environment.h"
void CameraHolder::acquireCamera()
{
Environment *env = Environment::get();
camera_ = env->cm()->get(env->cameraId());
ASSERT_EQ(camera_->acquire(), 0);
}
void CameraHolder::releaseCamera()
{
if (!camera_)
return;
camera_->release();
camera_.reset();
}

View file

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2021, Collabora Ltd.
*
* test_base.h - Base definitions for tests
*/
#ifndef __LC_COMPLIANCE_TEST_BASE_H__
#define __LC_COMPLIANCE_TEST_BASE_H__
#include <libcamera/libcamera.h>
#include <gtest/gtest.h>
class CameraHolder
{
protected:
void acquireCamera();
void releaseCamera();
std::shared_ptr<libcamera::Camera> camera_;
};
#endif /* __LC_COMPLIANCE_TEST_BASE_H__ */

View file

@ -8,69 +8,54 @@
#include "capture.h"
#include <iostream>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>
#include <gtest/gtest.h>
#include "environment.h"
#include "test_base.h"
namespace {
using namespace libcamera;
const std::vector<int> NUMREQUESTS = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
const std::vector<StreamRole> ROLES = {
StreamRole::Raw,
StreamRole::StillCapture,
StreamRole::VideoRecording,
StreamRole::Viewfinder
};
class SingleStream : public testing::TestWithParam<std::tuple<StreamRole, int>>
class SimpleCapture : public testing::TestWithParam<std::tuple<std::vector<StreamRole>, int>>, public CameraHolder
{
public:
static std::string nameParameters(const testing::TestParamInfo<SingleStream::ParamType> &info);
static std::string nameParameters(const testing::TestParamInfo<SimpleCapture::ParamType> &info);
protected:
void SetUp() override;
void TearDown() override;
std::shared_ptr<Camera> camera_;
};
/*
* We use gtest's SetUp() and TearDown() instead of constructor and destructor
* in order to be able to assert on them.
*/
void SingleStream::SetUp()
void SimpleCapture::SetUp()
{
Environment *env = Environment::get();
camera_ = env->cm()->get(env->cameraId());
ASSERT_EQ(camera_->acquire(), 0);
acquireCamera();
}
void SingleStream::TearDown()
void SimpleCapture::TearDown()
{
if (!camera_)
return;
camera_->release();
camera_.reset();
releaseCamera();
}
std::string SingleStream::nameParameters(const testing::TestParamInfo<SingleStream::ParamType> &info)
std::string SimpleCapture::nameParameters(const testing::TestParamInfo<SimpleCapture::ParamType> &info)
{
std::map<StreamRole, std::string> rolesMap = {
{ StreamRole::Raw, "Raw" },
{ StreamRole::StillCapture, "StillCapture" },
{ StreamRole::VideoRecording, "VideoRecording" },
{ StreamRole::Viewfinder, "Viewfinder" }
};
const auto &[roles, numRequests] = info.param;
std::ostringstream ss;
std::string roleName = rolesMap[std::get<0>(info.param)];
std::string numRequestsName = std::to_string(std::get<1>(info.param));
for (StreamRole r : roles)
ss << r << '_';
return roleName + "_" + numRequestsName;
ss << '_' << numRequests;
return ss.str();
}
/*
@ -80,15 +65,15 @@ std::string SingleStream::nameParameters(const testing::TestParamInfo<SingleStre
* failure is a camera that completes less requests than the number of requests
* queued.
*/
TEST_P(SingleStream, Capture)
TEST_P(SimpleCapture, Capture)
{
auto [role, numRequests] = GetParam();
const auto &[roles, numRequests] = GetParam();
CaptureBalanced capture(camera_);
Capture capture(camera_);
capture.configure(role);
capture.configure(roles);
capture.capture(numRequests);
capture.run(numRequests, numRequests);
}
/*
@ -98,17 +83,17 @@ TEST_P(SingleStream, Capture)
* a camera that does not clean up correctly in its error path but is only
* tested by single-capture applications.
*/
TEST_P(SingleStream, CaptureStartStop)
TEST_P(SimpleCapture, CaptureStartStop)
{
auto [role, numRequests] = GetParam();
const auto &[roles, numRequests] = GetParam();
unsigned int numRepeats = 3;
CaptureBalanced capture(camera_);
Capture capture(camera_);
capture.configure(role);
capture.configure(roles);
for (unsigned int starts = 0; starts < numRepeats; starts++)
capture.capture(numRequests);
capture.run(numRequests, numRequests);
}
/*
@ -118,19 +103,43 @@ TEST_P(SingleStream, CaptureStartStop)
* is a camera that does not handle cancelation of buffers coming back from the
* video device while stopping.
*/
TEST_P(SingleStream, UnbalancedStop)
TEST_P(SimpleCapture, UnbalancedStop)
{
auto [role, numRequests] = GetParam();
const auto &[roles, numRequests] = GetParam();
CaptureUnbalanced capture(camera_);
Capture capture(camera_);
capture.configure(role);
capture.configure(roles);
capture.capture(numRequests);
capture.run(numRequests);
}
INSTANTIATE_TEST_SUITE_P(CaptureTests,
SingleStream,
testing::Combine(testing::ValuesIn(ROLES),
const int NUMREQUESTS[] = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
const std::vector<StreamRole> SINGLEROLES[] = {
{ StreamRole::Raw, },
{ StreamRole::StillCapture, },
{ StreamRole::VideoRecording, },
{ StreamRole::Viewfinder, },
};
const std::vector<StreamRole> MULTIROLES[] = {
{ StreamRole::Raw, StreamRole::StillCapture },
{ StreamRole::Raw, StreamRole::VideoRecording },
{ StreamRole::StillCapture, StreamRole::VideoRecording },
{ StreamRole::VideoRecording, StreamRole::VideoRecording },
};
INSTANTIATE_TEST_SUITE_P(SingleStream,
SimpleCapture,
testing::Combine(testing::ValuesIn(SINGLEROLES),
testing::ValuesIn(NUMREQUESTS)),
SingleStream::nameParameters);
SimpleCapture::nameParameters);
INSTANTIATE_TEST_SUITE_P(MultiStream,
SimpleCapture,
testing::Combine(testing::ValuesIn(MULTIROLES),
testing::ValuesIn(NUMREQUESTS)),
SimpleCapture::nameParameters);
} /* namespace */

View file

@ -249,7 +249,7 @@ void FormatConverter::convertYUVPacked(const Image *srcImage, unsigned char *dst
dst_stride = width_ * 4;
for (src_y = 0, dst_y = 0; dst_y < height_; src_y++, dst_y++) {
for (src_x = 0, dst_x = 0; dst_x < width_; ) {
for (src_x = 0, dst_x = 0; dst_x < width_;) {
cb = src[src_y * src_stride + src_x * 4 + cb_pos_];
cr = src[src_y * src_stride + src_x * 4 + cr_pos];

View file

@ -356,6 +356,9 @@ int MainWindow::startCapture()
/* Verify roles are supported. */
switch (roles.size()) {
case 0:
roles.push_back(StreamRole::Viewfinder);
break;
case 1:
if (roles[0] != StreamRole::Viewfinder) {
qWarning() << "Only viewfinder supported for single stream";
@ -386,10 +389,7 @@ int MainWindow::startCapture()
/* Use a format supported by the viewfinder if available. */
std::vector<PixelFormat> formats = vfConfig.formats().pixelformats();
for (const PixelFormat &format : viewfinder_->nativeFormats()) {
auto match = std::find_if(formats.begin(), formats.end(),
[&](const PixelFormat &f) {
return f == format;
});
auto match = std::find(formats.begin(), formats.end(), format);
if (match != formats.end()) {
vfConfig.pixelFormat = format;
break;

View file

@ -39,7 +39,7 @@ static void value_set_rectangle(GValue *value, const Rectangle &rect)
GValue height = G_VALUE_INIT;
g_value_init(&height, G_TYPE_INT);
g_value_set_int(&x, size.height);
g_value_set_int(&height, size.height);
gst_value_array_append_and_take_value(value, &height);
}
@ -68,7 +68,7 @@ static const GEnumValue {{ ctrl.name|snake_case }}_types[] = {
"{{ enum.gst_name }}"
},
{%- endfor %}
{0, NULL, NULL}
{0, nullptr, nullptr}
};
#define TYPE_{{ ctrl.name|snake_case|upper }} \
@ -223,7 +223,6 @@ bool GstCameraControls::setProperty(guint propId, const GValue *value,
{%- for ctrl in ctrls %}
case controls::{{ ctrl.namespace }}{{ ctrl.name|snake_case|upper }}: {
ControlValue control;
{%- if ctrl.is_array %}
size_t size = gst_value_array_get_size(value);
{%- if ctrl.size != 0 %}
@ -254,12 +253,9 @@ bool GstCameraControls::setProperty(guint propId, const GValue *value,
}
{%- if ctrl.size == 0 %}
control.set(Span<const {{ ctrl.element_type }}>(values.data(),
size));
Span<const {{ ctrl.element_type }}> val(values.data(), size);
{%- else %}
control.set(Span<const {{ ctrl.element_type }},
{{ ctrl.size }}>(values.data(),
{{ ctrl.size }}));
Span<const {{ ctrl.element_type }}, {{ ctrl.size }}> val(values.data(), size);
{%- endif %}
{%- else %}
{%- if ctrl.is_rectangle %}
@ -273,10 +269,9 @@ bool GstCameraControls::setProperty(guint propId, const GValue *value,
{%- else %}
auto val = g_value_get_{{ ctrl.gtype }}(value);
{%- endif %}
control.set(val);
{%- endif %}
controls_.set(propId, control);
controls_acc_.set(propId, control);
controls_.set(controls::{{ ctrl.namespace }}{{ ctrl.name }}, val);
controls_acc_.set(controls::{{ ctrl.namespace }}{{ ctrl.name }}, val);
return true;
}
{%- endfor %}

View file

@ -74,6 +74,7 @@ static struct {
{ GST_VIDEO_FORMAT_I420, formats::YUV420 },
{ GST_VIDEO_FORMAT_YV12, formats::YVU420 },
{ GST_VIDEO_FORMAT_Y42B, formats::YUV422 },
{ GST_VIDEO_FORMAT_Y444, formats::YUV444 },
/* YUV Packed */
{ GST_VIDEO_FORMAT_UYVY, formats::UYVY },
@ -493,9 +494,12 @@ void gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,
/* Configure colorimetry */
if (gst_structure_has_field(s, "colorimetry")) {
const gchar *colorimetry_str = gst_structure_get_string(s, "colorimetry");
const gchar *colorimetry_str;
GstVideoColorimetry colorimetry;
gst_structure_fixate_field(s, "colorimetry");
colorimetry_str = gst_structure_get_string(s, "colorimetry");
if (!gst_video_colorimetry_from_string(&colorimetry, colorimetry_str))
g_critical("Invalid colorimetry %s", colorimetry_str);
@ -595,6 +599,43 @@ gst_task_resume(GstTask *task)
}
#endif
#if !GST_CHECK_VERSION(1, 22, 0)
/*
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Library <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
* Copyright (C) <2007> David A. Schleef <ds@schleef.org>
*/
/*
* This function has been imported directly from the gstreamer project to
* support backwards compatibility and should be removed when the older version
* is no longer supported.
*/
gint gst_video_format_info_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride)
{
gint estride;
gint comp[GST_VIDEO_MAX_COMPONENTS];
gint i;
/* There is nothing to extrapolate on first plane. */
if (plane == 0)
return stride;
gst_video_format_info_component(finfo, plane, comp);
/*
* For now, all planar formats have a single component on first plane, but
* if there was a planar format with more, we'd have to make a ratio of the
* number of component on the first plane against the number of component on
* the current plane.
*/
estride = 0;
for (i = 0; i < GST_VIDEO_MAX_COMPONENTS && comp[i] >= 0; i++)
estride += GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(finfo, comp[i], stride);
return estride;
}
#endif
G_LOCK_DEFINE_STATIC(cm_singleton_lock);
static std::weak_ptr<CameraManager> cm_singleton_ptr;

View file

@ -36,6 +36,11 @@ static inline void gst_clear_event(GstEvent **event_ptr)
#if !GST_CHECK_VERSION(1, 17, 1)
gboolean gst_task_resume(GstTask *task);
#endif
#if !GST_CHECK_VERSION(1, 22, 0)
gint gst_video_format_info_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride);
#endif
std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);
/**

View file

@ -8,6 +8,8 @@
#include "gstlibcameraallocator.h"
#include <utility>
#include <libcamera/camera.h>
#include <libcamera/framebuffer_allocator.h>
#include <libcamera/stream.h>
@ -199,22 +201,20 @@ GstLibcameraAllocator *
gst_libcamera_allocator_new(std::shared_ptr<Camera> camera,
CameraConfiguration *config_)
{
auto *self = GST_LIBCAMERA_ALLOCATOR(g_object_new(GST_TYPE_LIBCAMERA_ALLOCATOR,
nullptr));
g_autoptr(GstLibcameraAllocator) self = GST_LIBCAMERA_ALLOCATOR(g_object_new(GST_TYPE_LIBCAMERA_ALLOCATOR,
nullptr));
gint ret;
self->cm_ptr = new std::shared_ptr<CameraManager>(gst_libcamera_get_camera_manager(ret));
if (ret) {
g_object_unref(self);
if (ret)
return nullptr;
}
self->fb_allocator = new FrameBufferAllocator(camera);
for (StreamConfiguration &streamCfg : *config_) {
Stream *stream = streamCfg.stream();
ret = self->fb_allocator->allocate(stream);
if (ret == 0)
if (ret <= 0)
return nullptr;
GQueue *pool = g_queue_new();
@ -228,7 +228,7 @@ gst_libcamera_allocator_new(std::shared_ptr<Camera> camera,
g_hash_table_insert(self->pools, stream, pool);
}
return self;
return std::exchange(self, nullptr);
}
bool

View file

@ -18,6 +18,8 @@ struct _GstLibcameraPad {
GstPad parent;
StreamRole role;
GstLibcameraPool *pool;
GstBufferPool *video_pool;
GstVideoInfo info;
GstClockTime latency;
};
@ -70,6 +72,10 @@ gst_libcamera_pad_query(GstPad *pad, GstObject *parent, GstQuery *query)
if (query->type != GST_QUERY_LATENCY)
return gst_pad_query_default(pad, parent, query);
GLibLocker lock(GST_OBJECT(self));
if (self->latency == GST_CLOCK_TIME_NONE)
return FALSE;
/* TRUE here means live, we assumes that max latency is the same as min
* as we have no idea that duration of frames. */
gst_query_set_latency(query, TRUE, self->latency, self->latency);
@ -79,6 +85,7 @@ gst_libcamera_pad_query(GstPad *pad, GstObject *parent, GstQuery *query)
static void
gst_libcamera_pad_init(GstLibcameraPad *self)
{
self->latency = GST_CLOCK_TIME_NONE;
GST_PAD_QUERYFUNC(self) = gst_libcamera_pad_query;
}
@ -100,7 +107,7 @@ gst_libcamera_stream_role_get_type()
"libcamera::Viewfinder",
"view-finder",
},
{ 0, NULL, NULL }
{ 0, nullptr, nullptr }
};
if (!type)
@ -153,6 +160,35 @@ gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *pool)
self->pool = pool;
}
GstBufferPool *
gst_libcamera_pad_get_video_pool(GstPad *pad)
{
auto *self = GST_LIBCAMERA_PAD(pad);
return self->video_pool;
}
void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool)
{
auto *self = GST_LIBCAMERA_PAD(pad);
if (self->video_pool)
g_object_unref(self->video_pool);
self->video_pool = video_pool;
}
GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad)
{
auto *self = GST_LIBCAMERA_PAD(pad);
return self->info;
}
void gst_libcamera_pad_set_video_info(GstPad *pad, const GstVideoInfo *info)
{
auto *self = GST_LIBCAMERA_PAD(pad);
self->info = *info;
}
Stream *
gst_libcamera_pad_get_stream(GstPad *pad)
{

Some files were not shown because too many files have changed in this diff Show more