Commit graph

4852 commits

Author SHA1 Message Date
Barnabás Pőcze
3bdc84aadb spa: libcamera: device: adapt to libcamera change
The interface of string typed controls has recently been changed in
libcamera[0], which affects `properties::Model`, so adapt to that change
in such a way that is compatible with both the new and old versions.

[0]: f84522d7cd
2025-10-08 17:55:14 +02:00
Peter Ujfalusi
ba7790123c alsa: Use the minimum period size as headroom for SOF cards
Configure the headroom to be equal of the minimum allowed period size for
the configuration.

This is desirable when the ALSA driver's hw_ptr is 'jumpy' due to
underplaying hardware architecture, like SOF.
In case of SOF the DSP firmware will burst read at stream start to fill
it's host facing buffer and later settles to a constant pace. The minimal
period size is constrained by the driver to cover the initial burst and
settling time of the hw_ptr.

Guard this mode of working with a new boolean flag, which is only enabled
for SOF cards, kept it disabled for other cards to avoid any unforeseen
side effects.

Even if the use-period-size-min-as-headroom is set to true, the manual
headroom configuration will take precedence to allow experimentation.

Link: https://github.com/thesofproject/linux/issues/5284
Link: https://github.com/thesofproject/sof/issues/9695#issuecomment-2569033847
Link: https://github.com/thesofproject/sof/issues/10172
Link: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/4489
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
2025-09-29 17:32:33 +02:00
Wim Taymans
78937ee0ec adapter: fix Start of adapter
Commit cbbf37c3b8 changed the logic of the
Start command. Before this commit, when there was no converter, the
follower would always get the Start command. After the commit, the
follower would only get Start when previously Paused.

This however breaks when we set a format or buffers on the follower
without a converter because those actions might change the state of the
follower to Paused implicitly.

We should simply remove the started check here and always call Start on
the converter and follower, the implementations themselves will keep track
if anything needs to be done.

Fixes #4911
2025-09-29 17:26:20 +02:00
Pauli Virtanen
dd3798487c alsa: don't fail if 3 periods_min fails
Some drivers (emu10k1) appear to not necessarily support more than 2
periods.

Don't fail start if snd_pcm_hw_params_set_periods_min() fails, then we
just set nearest possible periods and buffer sizes.
2025-09-29 17:25:41 +02:00
Barnabás Pőcze
989b2150dc spa: libcamera: source: fix typo in log message
';' -> ':'

(cherry picked from commit 7a98bcf735)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
b6c0f26fd1 spa: libcamera: source: query frame buffer planes just once
(cherry picked from commit 93941e5207)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
88aa30f0d2 spa: libcamera: source: simplify spa_libcamera_clear_buffers()
Remove the unused `impl` parameter and the unnecessary early return.

(cherry picked from commit bf327d3dfb)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
9be2e3d7e9 spa: libcamera: source: keep libcamera::FrameBufferAllocator
Instantiate it once and keep it instead of always dynamically
allocating it when the camera is acquired.

(cherry picked from commit 1f60cd291f)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
657e55a65b spa: libcamera: source: clear buffers when format is changed
pipewire assumes that the buffers are removed from a port when its format is
changed even without an explicit call to `port_use_buffer()`.

So if the format is not just tested, clear the buffers, as well as the libcamera
requests and buffers because they are also recreated when a new format is set.
This matches the behaviour of the v4l2 plugin. Furthermore, this change also
removes the call to `spa_libcamera_use_buffers()` because that function does
nothing. And finally this change necessitates that the current format is always
reset (when not testing), not just before reaching `spa_libcamera_set_format()`.

(cherry picked from commit c517e712ed)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
a6ec4521f7 spa: libcamera: source: handle try-only format unset
Do nothing if `format == nullptr` and `SPA_NODE_PARAM_FLAG_TEST_ONLY` is present.

(cherry picked from commit 31176120f5)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
e7eef5f7ec spa: libcamera: source: do not emit param change if try-only
If `SPA_NODE_PARAM_FLAG_TEST_ONLY`, then the format does not change
on the node, it is only tested. So do not emit the param change events.

(cherry picked from commit a8a60832cd)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
0c418d05c3 spa: libcamera: source: extract presence of SPA_NODE_PARAM_FLAG_TEST_ONLY
Use a boolean named `try_only` instead of checking `flags` each time.

(cherry picked from commit dac9e40be6)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
3b5a2c06f3 spa: libcamera: source: remove format config shortcut
Remove the code that is supposed to compare the current and the to-be-set
format for returning early if they match. This is removed:

  * v4l2 also does not have it either;
  * it needs more consideration (there are not properly handled fields);
  * it would make later changes somewhat more complicated.

(cherry picked from commit 05a9e52caf)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
11d224befa spa: libcamera: source: set chunk flags on error
Set `SPA_CHUNK_FLAG_CORRUPTED` if the frame buffer metadata
signals anything other than success.

(cherry picked from commit 25075bb3d7)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
8ed8f21100 spa: libcamera: source: process requests on data loop
Since `impl::requestComplete()` runs in an internal libcamera thread, extra care
would need to be taken to validate all accesses to common data structures. For
example, the function might call `spa_libcamera_buffer_recycle()`, which accesses
`impl::ctrls`, which would be unsafe because it could read or modified at the same
time on the data thread. So move the processing of requests to the data loop.

(cherry picked from commit 019a5c130f)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
3ca1e20b3d spa: libcamera: source: process all requests in the ring buffer
It is possible that multiple requests complete before the data loop can run
`libcamera_on_fd_events()`. However, previously only the earliest item was
processed from the ring buffer, meaning that in those cases request processing
could be lagging request completion by multiple requests.

Fix that by getting the number of available requests and processing them all.

(cherry picked from commit 22ddb88072)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
746805d30b spa: libcamera: source: reset ring buffer when stopping
Presently, the ring buffer of completed requests is only cleared
when the buffers are removed from the port. This is not entirely
correct since pause/start commands do not clear the buffers but
they stop the camera. As a consequence, it is possible that some
completed requests stay in the ring buffer, causing them to be
mistakenly processed when the camera is started next.

So reset the ring buffer after the camera is stopped, the same time
as the queue of free buffers is cleared.

(cherry picked from commit c01a2977a5)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
1d0a1cbf12 spa: libcamera: source: move request completion data to impl
A `libcamera::Request` is directly tied to the camera, not any ports ("streams")
of it. So move the request completion ring buffer into the `impl` struct.

This is a prerequisite for supporting multiple libcamera streams (~ exposing
multiple ports on the node) in the future.

(cherry picked from commit 72fd462090)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
ad1dbf1541 spa: libcamera: source: store the request pointer in ring buffer
The request will be needed later, so store that directly in the
completion ring buffer for easy access.

This is fine even though `impl::requestComplete()` calls `Request::reuse()`
because only the request cookie is used in `libcamera_on_fd_events()` and
that remains constant during the lifetime of a request object.

(cherry picked from commit 099292d63d)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
c80af0c2c0 spa: libcamera: source: remove impl::pendingRequests
The handling of `impl::pendingRequests` is a bit problematic because,
for example, during startup, if `spa_libcamera_buffer_recycle()` observes
that `impl::active == false`, then it will try to append to `impl::pendingRequests`,
which is being iterated in the main thread in `spa_libcamera_stream_on()`.
That is not allowed on an `std::deque`.

So instead remove it altogether, and simply queue all requests when starting.
After `libcamera::Camera::stop()` returns, every request is guaranteed not
to be used by libcamera, and they can be freely reused, so this is safe to do.

This also removes the need for calling `spa_libcamera_buffer_recycle()` when
the buffers are set/unset on a port since that function no longer changes
anything apart from updating `buffer::flags` but `spa_libcamera_alloc_buffers()`
is modified appropriately to take care of that. And in the case of
`spa_libcamera_clear_buffers()` clearing the flag is not required because the
next `spa_libcamera_alloc_buffers()` call will reset reset the flags.

(cherry picked from commit 68627c5563)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
ceb10964d3 spa: libcamera: source: persistent requests <-> buffer association
Currently only a single stream is used. This makes it easy to associate
each request with the appropriate frame buffer when it is created. In
turn, this makes reusing requests a bit simpler and a bit more efficient.

So do that: add buffers to requests when they are allocated in `allocBuffers()`.

(cherry picked from commit 475665d615)
2025-09-27 13:22:59 +02:00
Barnabás Pőcze
2dc1e2a5d3 spa: libcamera: source: allocBuffers(): more error checking
First, check if the request pool is empty, and signal an error if it
is not. Second, ensure that the number of allocated buffers matches.

(cherry picked from commit 29b0c87d71)
2025-09-27 13:22:58 +02:00
Barnabás Pőcze
98842d50cf spa: libcamera: source: allocBuffers(): restore on failure
Undo the allocation and clear the requests if a failure is encountered
in order to leave things in a known state.

(cherry picked from commit 348e703be0)
2025-09-27 13:22:58 +02:00
Barnabás Pőcze
b28eb20c1f spa: libcamera: source: freeBuffers(): call when format is unset
At the moment the libcamera buffer allocation is completely tied to format
negotiation. `freeBuffers()` undoes `allocBuffers()`. And `allocBuffers()`
is called as part of `spa_libcamera_set_format()`. Therefore `freeBuffers()`
should be called independent of the state of the buffers on any port.

Otherwise unsetting the format while there are no buffers on the port will
cause the libcamera requests and buffers not to be released correctly. Similarly,
removing the buffers from a port would clear the libcamera requests and buffers,
making the node unusable without setting a new format.

(cherry picked from commit b9b7c0ab05)
2025-09-27 13:22:58 +02:00
Barnabás Pőcze
a5cb888578 spa: libcamera: source: freeBuffers(): split pending request removal
`freeBuffers()` should undo exactly what `allocBuffers()` does. However,
it currently also clears `impl::pendingRequests`, but that is filled
by `spa_libcamera_buffer_recycle()` during `spa_libcamera_alloc_buffers()`.

So remove the clearing of `impl::pendingRequests` from `freeBuffers()` and
move it directly into `spa_libcamera_clear_buffers()`.

(cherry picked from commit 89545946fd)
2025-09-27 13:22:58 +02:00
Barnabás Pőcze
93426bca4b spa: libcamera: source: propagate error when setting format
If `spa_libcamera_set_format()` fails, propagate its return value
as is without rewriting it to `EINVAL`.

(cherry picked from commit 14e0a8f66f)
2025-09-27 13:22:58 +02:00
Barnabás Pőcze
ef09f436b3 spa: libcamera: source: port_set_format(): remove goto
Remove the `done` label by moving things into the `format != nullptr` branch.

(cherry picked from commit 47ee86938b)
2025-09-27 13:22:58 +02:00
Barnabás Pőcze
d76fee1353 spa: libcamera: source: use dynamic builder for controls
Use a dynamic spa pod builder when enumerating controls since previous
changes can report more information from any given control. Also increase
the stack buffer size to 4096 bytes.

(cherry picked from commit e6f767d41d)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
dbc376fc2f spa: libcamera: source: provide value labels if available
For enumerated controls of type `libcamera::ControlTypeInteger32`, libcamera
provides the names of the enumerators, so add them to `SPA_PROP_INFO_labels`.

(cherry picked from commit 8673f17c0a)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
9b638d37d1 spa: libcamera: source: handle enum controls better
If the `libcamera::ControlInfo` object explicitly lists the values of a
control, then use those values when adding `SPA_PROP_INFO_type` to
construct an enumerated choice object.

(cherry picked from commit e379267274)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
b8bbcfdb96 spa: libcamera: source: unify control range logic
If libcamera does not provide a default value, then the average of the
minimum and maximum values is taken. The same logic is duplicated for
`float` and `int32_t`, so move it into a function template.

(cherry picked from commit 8d9e469e09)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
337d3b3daf spa: libcamera: source: ignore array controls
Properly ignore array controls because they are not supported for now.

(cherry picked from commit e9367443ac)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
a1cc0f3d86 spa: libcamera: source: rework bool control type info
`SPA_POD_CHOICE_Bool()` assumes that both true and false are available,
which may not be the case for libcamera controls, so manualy construct
the enumerated choice object and only add the actually available options.

(cherry picked from commit 66cc01ee2d)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
f6d3db72a1 spa: libcamera: source: move control enumeration to loop
Remove the `goto`s and instead move everything into a loop.

(cherry picked from commit 44c05cfa7b)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
eb6b9a47fc spa: libcamera: source: separate type info generation
Add a new function `control_details_to_pod()` that builds a
`SPA_TYPE_OBJECT_PropInfo` object describing a libcamera control.

(cherry picked from commit cc187b035b)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
7b4713ec73 spa: libcamera: manager: keep libcamera::CameraManager
At the moment, the camera manager shared pointer is released when the last
listener is removed, and recreated when the first listener is added. This
is the same behaviour that the alsa and v4l2 monitors implement with their
respective udev, inotify monitors.

However, for `libcamera::CameraManager`, this is likely not the best way
for multiple reasons:

  (a) it is a complex object with significant construction and starting cost,
      which includes starting threads and usually loading shared libraries;
  (b) usually one listener is added right after creating, and it is removed
      right before destruction, in which there are real no advantages;
  (c) the camera manager, being a shared resource, might very well be kept
      alive by some other component, in which case there is again not much
      real benefit.

So simplify the code by getting a camera manager reference at the beginning
and keeping it until the libcamera monitor is destroyed.

This also fixes a race condition where a hot-plugged camera might not have
been detected if the libcamera event was emitted between these two:

  collect_existing_devices(impl);
  start_monitor(impl);

(cherry picked from commit 8614fc45f8)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
4847bb3faf spa: libcamera: manager: factor out hotplug event submission
The `impl::{add,remove}Camera` functions do the same thing except
for one value, the type of the hotplug event. Add a private method
to `impl` that implements the common parts.

(cherry picked from commit a36b8a273d)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
19d9bb7219 spa: libcamera: source: create eventfd before starting camera
An eventfd is used to signal the data loop from the libcamera request
completion event handler. Previously, this eventfd was created and
installed after the camera has been started and requests were queued.

This is problematic because it creates a small time frame where the
libcamera request completion handler will run in a state where the
eventfd is not fully set up.

Fix that by settup up the eventfd before the camera is started.

(cherry picked from commit e0e8bf083d)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
21a61b2194 spa: libcamera: source: generate camera config right away
Currently the plugin uses a single configuration during its entire lifetime.
So generate that configuration during initialization and hold on to it.

This makes the code a bit simpler, and also fixes issues stemming from missed
calls to `spa_libcamera_get_config()` as well as no error checking of
`libcamera::Camera::generateConfiguration()`.

(cherry picked from commit 49be2a1c52)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
1774249bdb spa: libcamera: source: remove SPA_PROP_device{,Name}
`SPA_PROP_deviceName` is an empty string and setting it does nothing.

`SPA_PROP_device` is always the libcamera identifier of the camera,
and setting it has no effect whatsoever in contrast to other plugins
such as v4l2.

So remove them both for now.

(cherry picked from commit 8c4f60af48)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
47aacb2a84 spa: libcamera: source: do not close fd
Currently the plugin does not support importing memory and uses
`libcamera::FrameBufferAllocator` to allocate memory. Every file
descriptor is managed by that object, so they must not be closed
manually.

(cherry picked from commit 429c0e03a3)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
192eab1fea spa: libcamera: source: remove unused enum_fmt member
This was introduced by b2c38a2b3b ("libcamera: work on rewrite"),
but it has never been used.

(cherry picked from commit c167b98ff2)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
a080a4a6f2 spa: libcamera: source: prop_id_to_control(): do range check first
If it is a custom spa property, it will not be found in the lookup
table, so we can return immediately.

(cherry picked from commit 30ce210c2a)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
b775acca60 spa: libcamera: source: fix mapping of libcamera::ColorSpace::TransferFunction::Linear
`SPA_VIDEO_TRANSFER_GAMMA10` should be used to represent a
linear transfer function.

Fixes: 7e202a3844 ("spa: libcamera: add colorimetry support")
(cherry picked from commit 07a4e593bb)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
db23d9fa04 spa: libcamera: source: simplify color space conversion
Instead of using an out parameter, just return the `spa_video_colorimetry`
object; and do the libcamera -> spa conversion in a single place, where
the data is actually added to the pod.

(cherry picked from commit 938195b19f)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
e770ed42c3 spa: libcamera: source: avoid iterator overrun when enumerating controls
Do not overrun the iterator when skipping the initial couple items.
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
c3fec9769c spa: libcamera: manager: fix id allocation
There is an issue in the id allocation mechanism which can result
in the different devices having the same id. Specifically, consider
the scenario where there are only two cameras, which have just been
added. In this case `impl::devices` looks like this:

   (0, camA) | (1, camB) | (?, nullptr) | ...

Now assume that `camA` is removed, after which the array appears
as follows:

  (1, camB) | (1, nullptr) | (?, nullptr) | ...

Then assume that a new camera appears. When `get_free_id()` runs,
when `i == 1`, it will observe that `devices[i].camera == nullptr`,
so it selects `1` as the id. Leading to the following:

  (1, camB) | (1, camC) | (?, nullptr) | ...

This is of course incorrect. The set of ids must be unique. When
wireplumber is faced with this situation it destroys the device
object for `camB` when `camC` is emitted.

Fix this by simply not moving elements in the `devices` array,
leaving everything where it is. In which case the array looks
like this:

  (nullptr) | (camB) | (nullptr) | ... // after `camA` removal
  (camC) | (camB) | (nullptr) | ... // after `camC` appearance

Note that `device::id` is removed, and the id is now derived from
the position in `impl::devices`.

(cherry picked from commit 2c2808fab1)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
0749851cfe spa: libcamera: use nullptr instead of NULL
(cherry picked from commit db3d91ebeb)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
a5ba04a8ba spa: libcamera: use C++ style casts
(cherry picked from commit 4fa11619a2)
2025-09-26 09:58:10 +02:00
Barnabás Pőcze
ea67bf1662 spa: libcamera: use anon ns instead of static
Move most things into anonymous namespaces for internal linkage
instead of using `static`. This shortes declarations and makes it
hard to forget.

(cherry picked from commit bb8223bff1)
2025-09-26 09:58:10 +02:00