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.
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.
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.
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.
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.
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.
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.
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()`.
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.
`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()`.
Instead of applying each control separately, collect them into a
`libcamera::ControlList`, and try to apply them at the end.
Furthermore, instead of doing a blocking loop invoke, use `spa_loop_locked()`
as it is sufficient to update `impl::ctrls`.
After this change control setting works in a more "atomic" way since every
control is parsed and checked before actually reaching the camera. It is
also detected if the given control is not supported by the camera.
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.
For enumerated controls of type `libcamera::ControlTypeInteger32`, libcamera
provides the names of the enumerators, so add them to `SPA_PROP_INFO_labels`.
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.
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.
`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.
The body code didn't use atomic loads, so it had undefined behavior if
the body was concurrently modified. Use atomic loads to fix this.
Since the memory order is __ATOMIC_RELAXED this has no runtime overhead.
Also add barriers around a strncpy call and cast to volatile before
checking for a NUL terminator, though NUL-terminated strings in shared
memory are unuseable. There are some places where bytewise atomic
memcpy(), which doesn't currently exist, is needed. Instead, try to
fake it by using two barriers around memcpy().
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);
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.
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.
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()`.
`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.
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.
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.
Use kernel BT_PKT_SEQNUM (likely in Linux v6.17) to provide the ISO
packets sequence numbers. Fall back to counting packets if kernel is too
old to support the feature.