systemd, dbus-broker, and many glib applications heavily
utilize the GNU C attribute "cleanup" to achieve C++ RAII-like
semantics for local resource management. This commit introduces
essentialy same mechanism into pipewire.
At the moment, this is inteded to be a strictly private API.
free() and close() as cleanup targets are sufficiently common
to warrant their own special macros:
spa_autofree char *s = strdup(p);
// will call `free(s)` at the end of the lifetime of `s`
spa_autoclose int fd = openat(...);
// will call `close(fd)` if `fd >= 0` at the end of the lifetime of `fd`
However, with `spa_auto()` or `spa_autoptr()` it is possible to define
other variables that will be cleaned up properly. Currently four are supported:
spa_autoptr(FILE) f = fopen(...);
// `f` has type `FILE *`
// will call `fclose(f)` if `f != NULL`
spa_autoptr(DIR) d = opendir(...);
// `d` has type `DIR *`
// will call `closedir(d)` if `d != NULL`
spa_autoptr(pw_properties) p = pw_properties_new(NULL, NULL);
// `p` has type `struct pw_properties *`
// will call `pw_properties_free(p)`
spa_auto(pw_strv) v = pw_split_strv(...);
// `v` has type `char **`
// will call `pw_strv_free(v)`
It is possible to add support for other types, e.g.
SPA_DEFINE_AUTOPTR_CLEANUP(pw_main_loop, struct pw_main_loop, {
// the pointer can be accessed using `*thing`
// `thing` has type `struct pw_main_loop **`
spa_clear_ptr(*thing, pw_main_loop_destroy);
})
spa_autoptr(pw_main_loop) l = ...;
// `l` has type `struct pw_main_loop *`
// will call `pw_main_loop_destroy(l)`
or
SPA_DEFINE_AUTO_CLEANUP(spa_pod_dynamic_builder, struct spa_pod_dynamic_builder, {
// `thing` has type `struct spa_pod_dynamic_builder *`
spa_pod_dynamic_builder_clean(thing);
})
spa_auto(spa_pod_dynamic_builder) builder = ...
// `builder` has type `struct spa_pod_dynamic_builder`
// will call `spa_pod_dynamic_builder_clean(&builder)`
The first argument is always an arbitrary name. This name must be passed to
`spa_auto()` and `spa_autoptr()` as it is what determines the actual type
and destructor used. The second parameter is the concrete type. For
`SPA_DEFINE_AUTO_CLEANUP()` this is the concrete type to be used, while for
`SPA_DEFINE_AUTOPTR_CLEANUP()` it is the concrete type without the
outermost pointer. That is,
SPA_DEFINE_AUTOPTR_CLEANUP(A, foo, ...)
SPA_DEFINE_AUTO_CLEANUP(B, foo, ...)
spa_autoptr(A) x; // `x` has type `foo *`
spa_auto(B) y; // `y` has type `foo`
A couple other macros are also added:
spa_clear_ptr(ptr, destructor)
// calls `destructor(ptr)` if `ptr != NULL` and sets `ptr` to `NULL`
spa_clear_fd(fd)
// calls `close(fd)` if `fd >= 0` and sets `fd` to -1
spa_steal_ptr(ptr)
// sets `ptr` to `NULL` and returns the old value,
// useful for preventing the auto cleanup mechanism from kicking in
// when returning the pointer from a function
spa_steal_fd(fd)
// sets `fd` to -1 and returns the old value
Always use get_avail() and then only fetch the hires timestamp when
enabled to enhance the delay reporting. This way we also recover from
errors from snd_pcm_avail() instead of ignoring them.
This should make the recover after mmap_begin obsolete but we'll leave
that just to be safe.
Buffer sizes smaller than one cycle are possible, so don't assert that.
Instead, just provide as much samples as fits to the buffer.
If we are driver when this happens, emit a warning (once). Similarly to
ALSA, as driver we produce only one buffer at cycle start, and no new
buffers in process. If the whole cycle doesn't fit into the buffer,
recording probably will be broken and we want some debug when there will
be a bug report about that.
Since UCM doesn't perform the path lookups of alsa-mixer all UCM devices
fall back to the card's properties for e.g. icons and hence usually show
the `audio-card` icon for all ports giving a confusing UI to users as
all ports show the same icon.
As ucm names are pretty standard augment some common port names with
icons.
As far as I can tell, the code in question was not doing anything there
and was also breaking the new code elsewhere, so it was put on the
chopping block. Don't mind the squirming tentacles.
Signed-off-by: Niklāvs Koļesņikovs <89q1r14hd@relay.firefox.com>
There are currently several issues when multiple alsa devices are
involved.
For alsa devices that are followers, all data is written via
impl_node_process(). Currently spa_alsa_write() is only called if a new
buffer was queued.
It can happen that all buffers are in the ready list (queued by previous
calls but not yet written because there was no free space in the kernel
ring buffer). In this case writing stalls indefinitely.
To fix this, also call spa_alsa_write() if no new buffer is queued but
there are still buffers in the ready list.
If the ready list of the primary device is not empty then only this
device is handled because spa_alsa_write() is called directly. The other
devices make no progress during this interval.
The clock drift calculation works by comparing the alsa delay with the
expected delay since the last wakeup. This only work if the alsa
ringbuffer was filled completly. If the ready list contains a partial
buffer then the ringbuffer is not filled and the timing calculation
during the next wakeup is incorrect.
To fix all this, remove the special case for the non-empty ready list
and just call spa_node_call_ready() every time.
If we detect Playback/Capture Pitch 1000000, we can adjust those values
to update the feedback endpoint for the host. On the capture side, this
will instruct the host to adjust the rate at which data is being sent.
On the playback side, this will adjust the amount of data the USB gadget
driver sends out in each USB tick.
Use snd_pcm_htimestamp to get both the available space and the timestamp
when this was calculated. We can then use this to get a better estimate
of the delay in the device against the graph start and get a more
reliable delay between capture and playback.