Compare commits

...

1002 commits

Author SHA1 Message Date
Wim Taymans
08d4e319cf avb: fix stack overflow in MRP parsing
AVB_MRP_VECTOR_GET_NUM_VALUES can be 13 bits and is stored in a
unit16_t. event_len and param_len are however calculated from this and
then truncated to 8 bits (uint8_t) which causes the bounds check to
silently pass and cause an OOB read.

Change the type to uint16_t to avoid overflows.
2026-05-08 18:13:12 +02:00
Wim Taymans
6c0a9b31f6 alsa: write silence in smaller chunks
With large quantum and many channels, the silence buffer can become
quite large (many MB), which can cause a stack overflow.

Use a fixed size silence buffer instead and write in smaller chunks.
2026-05-08 17:43:23 +02:00
Wim Taymans
0ac3cf3c88 alsa: fix warning about sign 2026-05-08 17:39:09 +02:00
Barnabás Pőcze
b9bae7fdcf spa: alsa: pcm: spa_alsa_clear(): clear pointers
Use `spa_clear_ptr()` to ensure that all freed pointers are cleared.
2026-05-08 15:27:54 +00:00
Barnabás Pőcze
c8462edf4b spa: alsa: pcm: log_write(): return early if log level is disabled
If the "debug" log level is not enabled for the "spa.alsa" log topic,
then there is no point in going into the loop and splitting the data
into lines, so skip that.
2026-05-08 15:27:54 +00:00
Barnabás Pőcze
cfe9c7d6ca spa: alsa: pcm: log_write(): don't use strcspn()
Do not use `strcspn()` because it assumes a null terminated string,
but the `fopencookie()` write callback receives a (ptr, length) pair.

So use `memchr()` instead to find the `\n`.
2026-05-08 15:27:54 +00:00
Barnabás Pőcze
bba43d4433 spa: alsa: pcm: log_write(): fix return value
The `fopencookie()` write callback should return the number of consumed
bytes, but it currently only ever returns 0, which signals an error
condition according to the documentation.

Fix that by not overwriting `size`.

Fixes: 73073eb33f ("alsa: redirect alsa output to log file")
2026-05-08 15:27:54 +00:00
Barnabás Pőcze
ff7b996596 treewide: mark fopencookie() vtable const
`fopencookie()` takes the vtable by value, so it can be marked `const`, so do that.
2026-05-08 15:27:54 +00:00
Barnabás Pőcze
8caea521d7 doc: compile tutorial programs
The programs in `doc/examples` are not compiled currently, so let's
compile them if the `docs` and `examples` options don't disallow it.

`tutorial4.c` needs a small modification to avoid `-Wfloat-conversion`.

Additionally, install them if `installed_tests` is not disabled.
2026-05-08 15:20:41 +00:00
Wim Taymans
b5d294eab0 dfffile: handle invalid channels and rate
Missing or malformed headers could cause unspecified channels or rate
that can cause crashes.
2026-05-08 17:12:54 +02:00
Wim Taymans
57770c7e18 midifile: handle some other read errors 2026-05-08 17:07:51 +02:00
Wim Taymans
c94bbb55bc midifile: read up to 4 bytes for the varlen 2026-05-08 17:00:56 +02:00
Martin Geier
c7f2f0dc73 audioconvert: update rate also for nodes with disabled resampler
When the graph rate changes it is possible that the follower node can
renegotiate to the new suggested audioconvert rate without requiring
resampling and so the extra check for the disabled resampler is not
required.

Fixes #4933
2026-05-08 15:56:05 +02:00
Wim Taymans
4c8093fa72 combine-stream: clean up some variables
Move the source offs, stride, data and size calculations out of the
destination loop. We only need to clamp the size to copy to the maxsize
of the destination buffer.
2026-05-08 13:10:30 +02:00
Wim Taymans
93b940edef module-combine: limit size to buffer maxsize
Limit the amount of data we write to the destination buffer to its
maxsize.
2026-05-08 13:01:11 +02:00
Wim Taymans
136fc59765 bluez5: avoid heap overflow in AAC decoder
aacDecoder_DecodeFrame expects the number of destination INT_PCM samples,
not bytes. Since INT_PCM is int16_t (2 bytes), passing dst_size in bytes
tells the decoder the buffer is 2x larger than reality.

Note that we don't need to care about the number of channels in this
size, the decoder will do that for us.
2026-05-08 13:01:03 +02:00
Wim Taymans
6d3122c1b1 sap: avoid reading past the end of the string
parse_sdp_a_rtpmap used c += strlen(c) + 1 to skip past the MIME type to the
rate/channels part, but if the a=rtpmap: line had no / separator, strcspn
returned the full string length and the +1 advanced past the null terminator.

Fix this by checking if / was actually found, returning -EINVAL if not.
2026-05-08 11:57:07 +02:00
Wim Taymans
7fd3e13a3e netjack2: handle 0 in sync frames
JACK2 only sends -1 as the frames, meaning we should take the value from
the negotiated period as the frames to process.

We however send the actual number of frames and use the sync value to
decide how many frames to process. We need to be careful because a value
of 0 will cause a division by 0 so treat <= 0 frames the negotiated period
size as well.
2026-05-08 11:42:15 +02:00
Wim Taymans
753eae9302 netjack2: check config against MAX_CHANNELS
Check that the params don't include more than MAX_CHANNELS of audio or
else we overflow the position array.

Adapt to the compiled value of SPA_AUDIO_MAX_CHANNELS but allow at least
128 channels.
2026-05-08 11:07:03 +02:00
Wim Taymans
6cee86e509 sendspin: avoid buffer overread
Check that we have enough bytes (>=9) to parse the message type and the
timestamp.
2026-05-08 10:33:39 +02:00
Wim Taymans
22243d5ce9 sendspin: handle parse_player errors
Otherwise, this might leave the stride 0 and cause a division by 0
later.
2026-05-08 10:32:28 +02:00
Wim Taymans
5b37b9cf99 filter-graph: remove the pipe filter
It's a terrible idea, doesn't work so well (locks up the data-loop when
read is blocked) and a security mightmare. If you really need to pipe
samples through some program, do that somewhere else, like from the
command line with pw-cat and pw-record.
2026-05-08 10:16:12 +02:00
Wim Taymans
b3257ae425 context: add library.use-fallback option
Normally, when loading a plugin feature, often a library.name property
is given as well. If the feature to load is not explicitly listed in
context.spa-libs, the library.name is used a fallback library.

Add an option to ignore this library.name and only use the
context.spa-libs entries. This makes it possible to only load explicitly
listed features in the config file and makes it possible to lock down
what plugins can be loaded.

Set the option to true by default for now, which keeps the existing
behaviour of using the fallback library. Add some more entries to the
context.spa-libs in case the option is switched off to make things
work.

Set the option to false for the minimal.conf.
2026-05-08 09:57:52 +02:00
Barnabás Pőcze
ddab12a5aa bluez5: cancel RegisterApplication calls when adapter goes away
If an adapter's removal is processed before the pending `RegisterApplication()`
dbus calls return, then those pending calls are not cancelled, and when the
(error) replies arrive, the callbacks will run into use-after-free issues
since they reference the removed adapter.

See #5096
2026-05-08 07:11:52 +00:00
Wim Taymans
97c8a0a5ae modules: update docs for the pipe plugin 2026-05-07 14:46:21 +02:00
Wim Taymans
85decefbef filter-graph: move the pipe plugin to separate .so
It's quite dangerous to allow it to be loaded dynamically into the
pulse server so block it. The other plugins should be safe to load.
2026-05-07 14:37:44 +02:00
Wim Taymans
a4e2856d06 pulse-server: block arbitrary filter-graphs
Add a special 'blocked' spa-libs value that returns EPERM when trying to
load the factory.

Only allow loading the LADSPA filter.graph nodes for the LADSPA sink and
source. The most problematic part is the pipe filter, that allows it to
spawn arbirary programs as part of the filter.graph.

You can add a filter-graph to any stream with stream_props.
2026-05-07 14:13:38 +02:00
Wim Taymans
e3f75314be vban: fix timestamp overflows
Like how it is done in RTP.
2026-05-07 14:07:33 +02:00
Wim Taymans
143af979ab filter-graph: error when there are no valid nodes
The nodes might have failed to load or there was an empty array or the
array did not contain objects.
2026-05-07 13:30:41 +02:00
Wim Taymans
e9aff3040a modules: free the stream and impl on errors 2026-05-07 13:30:12 +02:00
Wim Taymans
aa36fd5a17 pulse-server: avoid double free of props
Use spa_steal_ptr to transfer props ownership when we can.

This fixes a problem in the upload stream where the props would be freed
twice when buffer allocation failed, once with properties_free and
then with stream_free.
2026-05-07 10:30:35 +02:00
Wim Taymans
e975a44d05 modules: handle more stream setup failures
Instead of silently ignoring things.
2026-05-06 16:23:08 +02:00
Wim Taymans
9825fb3647 impl-link: use the right port direction string
We might swap input and output so make sure we use the right one in the
debug log to avoid confusion.
2026-05-06 16:21:07 +02:00
Wim Taymans
c362bca5e9 loopback: handle stream setup failure
Instead of silently failing.
Also when we do the cleanup, make sure we don't free the props twice.
2026-05-06 16:07:59 +02:00
Wim Taymans
eadaa2608a alsa: clear output and log file after doing alsa API
Also be a bit more careful when the handles are NULL.
2026-05-06 16:05:30 +02:00
Wim Taymans
91755950dd spa: improve error handling
Use impl_clear to clean up partially allocated handles. Make sure we
only clean up the initialized parts.
2026-05-06 14:20:52 +02:00
Wim Taymans
3e53487c72 bluez5: fix cleanup
Clean up the transport listener and the codec_props on error.
Also clean up when the timerfd allocation fails.
2026-05-06 14:03:18 +02:00
Wim Taymans
7254b8288f alsa: clean up the impl on errors 2026-05-06 13:59:43 +02:00
Wim Taymans
517b6c3778 jack: handle get_registry failure 2026-05-06 13:54:19 +02:00
Wim Taymans
95a4772031 modules: handle get_registry NULL return value 2026-05-06 13:50:02 +02:00
Wim Taymans
ac95f1241e modules: fix portal error handling
Mostly to fix the property cleanup.
2026-05-06 13:48:35 +02:00
Wim Taymans
51b635cc98 modules: convert snprintf to strbuf
Use spa_strbuf instead of snprintf to handle errors better.
2026-05-06 13:35:09 +02:00
Wim Taymans
2c4dc2d22f raop: handle loop_add_io errors 2026-05-06 13:18:11 +02:00
Wim Taymans
cc2c7cc591 modules: handle some property allocation errors 2026-05-06 13:17:41 +02:00
Wim Taymans
b66614063d avb: use safer strbuf to construct strings 2026-05-06 13:09:03 +02:00
Wim Taymans
8276d615ba filter-graph: handle fcntl errors better
Don't set invalid flags when the F_GETFL failed.
2026-05-06 12:47:44 +02:00
Wim Taymans
fed4d14ab7 vulkan: check for fcntl errors 2026-05-06 12:45:37 +02:00
Wim Taymans
f5bbdc403f profiler: fix cleanup on errors
Make an impl_destroy function to clean up partially initialized impl.
Handle failed flush_event allocation. Handle all errors in one place.
2026-05-06 12:39:39 +02:00
Wim Taymans
aac0f13487 module-echo-cancel: fix cleanup
Go to the error label to make sure verything is cleaned up properly when
loading the aec plugin fails for some reason.
2026-05-06 12:27:09 +02:00
Wim Taymans
d33466a340 bluez5: fix wrong use of F_GETFL and F_SETFL
The pattern is to F_GETFL the flags, then clear the NONBLOCK flag and
then F_SETFL the new flags.
2026-05-06 12:22:39 +02:00
Wim Taymans
195c048d1c audioconvert: use strbuf to construct the channel names
This handles overflow and errors correctly, unlike snprintf which might
return -1 or the size that would have been written if truncated, causing
overwrite later.
2026-05-06 11:57:52 +02:00
Wim Taymans
bceaf1a212 vulkan: handle mmap failure 2026-05-06 11:57:52 +02:00
Wim Taymans
59a5392850 vulkan: fix the open() flags
The second argument are the flags, the mode is not needed for
RDONLY. Accidentally works because O_RDONLY is 0 but ignored the
O_CLOEXEC flags.
2026-05-06 11:57:52 +02:00
Wim Taymans
364436dd31 rtp-sap: handle out-of-bound SAP packet read
If the SAP packet contains the MIME type string but no SDP payload after it,
sdp would point past the null-terminated buffer. Check that we are
still inside the packet before parsing the SDP.
2026-05-06 11:57:52 +02:00
Wim Taymans
d32a21c4ee rtp-sap: handle uninitialized avail
When the ioctl fails, avail might be uninitialized. Check the ioctl
return value and handle invalid avail.
2026-05-06 11:57:52 +02:00
Wim Taymans
c4a2f9b480 rtp-sap: also handle short reads
Don't only handle -1 errors from read but also short reads that could
leave the buffer with uninitialized data.
2026-05-06 11:57:52 +02:00
Wim Taymans
ca46444d13 netjack2: limit period_size to quantum_limit
To avoid large periods that might not fit in the quantum.
2026-05-06 11:57:52 +02:00
Wim Taymans
aa29fefbd2 vban: limit the stream_name
The stream_name from the network is not 0 terminated so limit the
string to the max size.
2026-05-06 11:57:52 +02:00
Wim Taymans
8907d0860b modules: loop_add_io with close=true owns the fd
We should not close the fd when loop_add_io with close=true fails
because the fd is already closed.
2026-05-06 11:57:51 +02:00
Barnabás Pőcze
eec372ba9d bluez5: require RegisterApplication() support
The `{Un}Register{Endpoint,Player}()` functions of the `org.bluez.Media1`
interface were deprecated with the introduction of the `{Un}RegisterApplication()`
functions[0][1]. Fallback to the deprecated interfaces has been present for a
long time in pipewire, but those parts in their current form are prone to
use-after-free issues (#5096). Instead of fixing them, remove them as they
have been deprecated for a long time. The first version of bluez that supports
the new interfaces is 5.51, released on 2019-09-19 [2].

[0]: https://git.kernel.org/pub/scm/bluetooth/bluez.git/commit/?id=65bd68b907a95b4748df6929383a833ecfb4b660
[1]: https://git.kernel.org/pub/scm/bluetooth/bluez.git/commit/?id=01f8fc2997524d85817adb8176e542bac9d0cdfa
[2]: https://git.kernel.org/pub/scm/bluetooth/bluez.git/commit/?h=5.51&id=6de4bdb957cdc85d89851420ab06ca8e226f8d4e
2026-05-05 18:40:13 +02:00
Pauli Virtanen
31f0300c48 bluez5: fix wrong use of send_with_reply in HFP backends
The pattern if (!send_with_reply(...)) leaks DBusPendingCall and is
UAF prone.

Replace these with proper tracking and cancellation of the pending
calls in HFP backends.
2026-05-05 16:20:34 +00:00
Pauli Virtanen
81470db44f bluez5: lc3plus: add missing #include 2026-05-05 16:20:34 +00:00
Wim Taymans
620b18e9d8 zeroconf: check for NULL before doing strdup
Reject zeroconf entries without name, type, domain or host_name.
2026-05-05 18:17:42 +02:00
Wim Taymans
f57a2e9680 loop: add spa_goto_if_fail and use in loop_enter
When we lock and enter the loop but it is not the right thread, unlock
it again after logging an error. Otherwise we might deadlock.
2026-05-05 17:50:39 +02:00
Wim Taymans
593b5cf148 protocol-simple: close client_fd when client alloc fails 2026-05-05 17:19:06 +02:00
Wim Taymans
b9761926df netjack2: go to the right error label
socket_failed is for when the socket fd failed, otherwise we need to do
cleanup.
2026-05-05 17:14:02 +02:00
Wim Taymans
c2bf8c920e protocol-native: close fd and unset env in all cases
If we find the fd valid, we must attempt to close it.
Also unset the env variable when we read and processed it, even if
there was an error.
2026-05-05 17:09:12 +02:00
Wim Taymans
aa6fa6bdc3 modules: make sure we don't deref potential NULL 2026-05-05 16:39:27 +02:00
Wim Taymans
9124a09482 modules: handle allocation error of properties 2026-05-05 16:34:15 +02:00
Wim Taymans
f8a76d9f00 protocol-native: handle allocation failures 2026-05-05 16:26:25 +02:00
Wim Taymans
ea25c3f2b1 alsa-plugin: clean up the error handling
Also catch property copy errors.
2026-05-05 16:25:56 +02:00
Wim Taymans
6eac1efb20 jack: handle allocation errors better 2026-05-05 16:11:02 +02:00
Wim Taymans
01b2af13c7 pulse-server: handle strdup error 2026-05-05 14:54:43 +02:00
Wim Taymans
899051169e netjack2: handle property allocation failures 2026-05-05 14:51:43 +02:00
Wim Taymans
2ac7c81958 pipewire: handle allocation failures
And make sure we don't leak things in the error paths.
2026-05-05 14:44:39 +02:00
Wim Taymans
67e8da3390 modules: handle some allocation failures 2026-05-05 14:44:18 +02:00
Wim Taymans
3db8038652 modules: avoid useless props copy
We can use the properties we passed to the node.
2026-05-05 14:15:44 +02:00
Wim Taymans
c8b9b44d40 alsa: clean up the pitch element on errors 2026-05-05 14:15:13 +02:00
Wim Taymans
9946f5ec77 modules: handle some more allocation errors 2026-05-05 14:14:52 +02:00
Wim Taymans
379b4a8747 introspect: handle strdup failures
Abort and clean up the dict copy when allocation fails.
2026-05-05 13:50:39 +02:00
Wim Taymans
dd695ee5a7 modules: handle allocation errors gracefully 2026-05-05 13:10:21 +02:00
Wim Taymans
24f9b9a335 acp: handle channel map allocation errors 2026-05-05 13:09:41 +02:00
Wim Taymans
7bfc820ae8 filter-graph: handle allocation errors and do cleanup
Make sure clean up everything on error.
2026-05-05 12:53:10 +02:00
Wim Taymans
9972df2614 convert: use static string for convertname
We only use it for debugging and checking if it's the default
converter.
2026-05-05 12:51:55 +02:00
Wim Taymans
06ba2f792d bluez5: handle fd allocation errors
For the timers, only set the data after we allocated the fd because this
is checked to free the fd again later.
2026-05-05 11:37:18 +02:00
Wim Taymans
96594d6716 plugins: handle some fd allocation errors 2026-05-05 11:36:45 +02:00
Wim Taymans
09dda9e4e0 avb: clean up on allocation errors 2026-05-05 11:35:54 +02:00
Wim Taymans
3ef2b77915 pipewire: fix some allocation errors
Also fix a string leak in the device error path.
2026-05-05 11:08:52 +02:00
Barnabás Pőcze
ccfb61efa4 treewide: make more file descriptors cloexec
Avoid file descriptor leakage into child processes by marking them `O_CLOEXEC`.
2026-05-04 19:48:41 +02:00
Wim Taymans
8344117e7b avb: free the ringbuffer 2026-05-04 18:43:56 +02:00
Wim Taymans
f91b98a812 stream: check buffer_id in reuse_buffer 2026-05-04 18:42:52 +02:00
Alyssa Ross
49ce385c44
test: move SPA_NORETURN attributes to header
Otherwise, when building with fortify-headers, I get an error because
the compiler doesn't know pwtest_ptr_notnull will not return if the
passed pointer is null, so it doesn't know the pointer subsequently
passed to memmove via pw_array_remove will be non-null.

	In file included from ../spa/include/spa/utils/defs.h:11,
	                 from ../spa/include/spa/utils/string.h:15,
	                 from ../test/pwtest.h:15,
	                 from ../test/test-array.c:7:
	In function ‘memmove’,
	    inlined from ‘array_test’ at ../test/test-array.c:66:2:
	/nix/store/9031y56lsf4mq177s68ql1axsf241r7j-fortify-headers-3.0.1/include/string.h:77:16: error: argument 1 is null but the corresponding size argument 3 value is [8, 18446744073709551615] [-Werror=nonnull]
	   77 |         return __orig_memmove(__d, __s, __n);
	      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	/nix/store/9031y56lsf4mq177s68ql1axsf241r7j-fortify-headers-3.0.1/include/string.h:77:16: warning: ‘__orig_memmove’ reading 8 or more bytes from a region of size 0 [-Wstringop-overread]
	In function ‘array_test’:
	cc1: note: source object is likely at address zero
	In file included from /nix/store/9031y56lsf4mq177s68ql1axsf241r7j-fortify-headers-3.0.1/include/stdlib.h:33,
	                 from ../spa/include/spa/utils/string.h:11:
	/nix/store/9031y56lsf4mq177s68ql1axsf241r7j-fortify-headers-3.0.1/include/string.h:69:1: note: in a call to function ‘__orig_memmove’ declared with attribute ‘access (read_only, 2, 3)’
	   69 | _FORTIFY_FN(memmove) void *memmove(void * _FORTIFY_POS0 __d,
	      | ^~~~~~~~~~~
2026-05-04 17:51:58 +02:00
Wim Taymans
96ef2728b8 alsa-plugin: handle allocation errors
Make sure we don't leak channel maps.
2026-05-04 16:35:02 +02:00
Wim Taymans
c263a52ab7 audioconvert: reuse hist_mem as window scratch space
Instead of allocating a potentially unsafe 1MB array on the stack to
store the window, reuse the hist_mem, which has more than enough space
as a scratch space for the window.
2026-05-04 16:26:20 +02:00
Wim Taymans
057ae16504 bluez5: handle some allocation failures 2026-05-04 14:27:34 +02:00
Wim Taymans
6539c2bf8c fix some allocation failures 2026-05-04 14:18:31 +02:00
Wim Taymans
8cf1164eb6 midifile: reject midi files with invalid values
>64 tracks will overflow the track array and a division of 0 will cause
a division by zero later.
2026-05-04 14:13:09 +02:00
Wim Taymans
c4b198962c jack-tunnel: pass bytes to jack_to_midi
When the buffer has n_samples, we have n_samples * sizeof(float) bytes
to fill with midi.
2026-05-04 13:51:55 +02:00
Wim Taymans
bd8eab3ffb raop: handle allocation error and avoid crash later 2026-05-04 13:46:09 +02:00
Wim Taymans
782986baa1 filter-graph: guard against overflow of graph input/output 2026-05-04 13:42:14 +02:00
Wim Taymans
ed33b4d877 jack: dlclose on errors 2026-05-04 13:20:00 +02:00
Wim Taymans
4cf5acf18d raop: free pending messages 2026-05-04 13:16:37 +02:00
Wim Taymans
7ab2b7d24a raop: handle asprintf errors 2026-05-04 13:16:25 +02:00
Wim Taymans
732df7b978 filter-graph: fix pipe2 errors
Also free the arguments and close the child side pipe ends.
2026-05-04 13:10:48 +02:00
Wim Taymans
a4d7607a88 raop: avoid division by 0 2026-05-04 12:26:08 +02:00
Wim Taymans
2993bf1722 filter-graph: limit lv2 ports
Limit the lv2 ports to 512 and check for allocation errors.
2026-05-04 12:16:26 +02:00
Wim Taymans
e423844f9b filter-graph: reject too many impulse response files 2026-05-04 12:12:01 +02:00
Wim Taymans
62dbd49f5a jack: fix some potential NULL pointer derefs
If the client has no pid property, don't try to atoi a NULL string.

If the : is stripped of a very long port name, don't return a NULL+1
string in port_short_name().

Don't deref a potential NULL port in debug log.
2026-05-04 12:04:08 +02:00
Wim Taymans
16ba0ef59b jack: guard again append without an event in the buffer 2026-05-04 11:57:05 +02:00
Wim Taymans
7dacdc8cc8 avb: bounds check the attribute encoding
Add a maxsize to the attribute encode functions and add checks that
they don't overwrite the provided buffer.
2026-05-04 10:47:52 +02:00
Wim Taymans
cda2e2ee17 rtp: fix ringbuffer overflow check
We write raw samples to the ringbuffer from the opus decode function so
compare the available space to the max amount of samples we can decode,
which is 2880 as passed to opus_decode.
2026-05-04 10:26:30 +02:00
Wim Taymans
17723bc00d avb: fix MAAP packet send
Actually send the ehaternet header data as well. The send was skipping
the ehternet header and reading past the packet end.
2026-05-04 10:21:41 +02:00
Wim Taymans
67b70c8d23 netjack2: do some sanity checks on the MTU
It needs to be large enough foer the header, udp overhead and a uint32_t
for each audio channel to be able to send the sync packet.

Avoid string oob read when debugging the packet.
2026-05-04 10:13:44 +02:00
Wim Taymans
5a8a3b5a54 vban: don't write too large midi packets 2026-05-04 09:56:30 +02:00
Wim Taymans
be6bfba8eb avb: check some allocation errors 2026-05-04 09:56:18 +02:00
Wim Taymans
32bb568225 avb: make sure to no blindly use the type as a command index 2026-05-04 09:55:35 +02:00
Wim Taymans
172f969650 avb: check packet sizes
Clamp packet len to the buffer size.

Reject packets with invalid data_len.
2026-05-04 09:53:33 +02:00
Wim Taymans
47deb75247 remote-node: check memory offsets and size
Check that the metadata, chunks and optionally the buffer data fit in
the memory.
2026-05-04 09:29:00 +02:00
hackerman-kl
7a826b1580 milan-avb: gptp: track request timing on CLOCK_MONOTONIC 2026-05-03 10:26:16 +02:00
hackerman-kl
4da6e39281 milan-avb: gptp: handle MANAGEMENT_ERROR_STATUS TLV as stale-data signal 2026-05-03 10:26:16 +02:00
hackerman-kl
09b3f0dc34 milan-avb: gptp: invalidate cached state when ptp4l stops responding 2026-05-03 10:26:16 +02:00
hackerman-kl
97436efe1e milan-avb: cmd-get-as-path: build [gm,...,local] and emit unsolicited GET_AS_PATH 2026-05-03 10:26:16 +02:00
hackerman-kl
ff3367dc05 milan-avb: aecp-aem: emit unsolicited GET_AVB_INFO when gPTP changes 2026-05-03 10:26:16 +02:00
hackerman-kl
e02a4854de milan-avb: gptp: query PATH_TRACE_LIST and store Announce path trace 2026-05-03 10:26:16 +02:00
hackerman-kl
55bb0b6a6a milan-avb: gptp: query CURRENT_DATA_SET for canonical is_grandmaster check 2026-05-03 10:26:16 +02:00
hackerman-kl
c877ea4243 milan-avb: gptp: rate-limit management requests to 375 ms 2026-05-03 10:26:16 +02:00
hackerman-kl
fc08d2444a milan-avb: gptp: send PTP management with majorSdoId=1 for gPTP profile 2026-05-03 10:26:16 +02:00
hackerman-kl
1b81dbab85 milan-avb: adding the ptp4l command line information 2026-05-03 10:26:16 +02:00
hackerman-kl
4b44c15768 milan-avb; introducing gptp/as_path interface specific dirty flags 2026-05-03 10:26:16 +02:00
hackerman-kl
21dd14618c milan-avb: update banner 2026-05-03 10:26:16 +02:00
hackerman-kl
46f9c5130e milan-avb: cmd-get-as-path: build path from gptp data 2026-05-03 10:26:16 +02:00
hackerman-kl
9f019a061d milan-avb: aecp-aem: source GET_AVB_INFO grandmaster from gptp 2026-05-03 10:26:16 +02:00
hackerman-kl
e9a1e50996 milan-avb: adp: refresh grandmaster_id from gptp on each advertise 2026-05-03 10:26:16 +02:00
hackerman-kl
231b0950c5 milan-avb: descriptors: derive AVB_INTERFACE clock_identity from entity_id 2026-05-03 10:26:16 +02:00
hackerman-kl
3f63b51fcc milan-avb: gptp: rework management I/O as non-blocking with sequence-id matching 2026-05-03 10:26:16 +02:00
hackerman-kl
0da747fd44 milan-avb: gptp: do not fail server creation on missing PTP socket 2026-05-03 10:26:16 +02:00
hackerman-kl
0b09fb2b1e milan-avb: gptp: initialise ret in the drain loop 2026-05-03 10:26:16 +02:00
hackerman-kl
21c4c59587 milan-avb: gptp: use entity_id for management source port id 2026-05-03 10:26:16 +02:00
hackerman-kl
1e9f89b638 milan-avb: gptp: emit gm_changed only when the GM actually changes 2026-05-03 10:26:16 +02:00
hackerman-kl
f5389a4225 milan-avb: gptp: drop unused avb_gptp_destroy wrapper 2026-05-03 10:26:16 +02:00
hackerman-kl
8c9e5f1974 milan-avb: gptp: use PTP_DEFAULT_LOG_MESSAGE_INTERVAL macro 2026-05-03 10:26:16 +02:00
hackerman-kl
e83bbfacf1 milan-avb: gptp: drop %m from incomplete request/response warnings 2026-05-03 10:26:16 +02:00
hackerman-kl
d5e4f11be7 milan-avb: gptp: fix log message for SO_PASSCRED setsockopt failure 2026-05-03 10:26:16 +02:00
hackerman-kl
51a997294e milan-avb: gptp: drop unused struct fields 2026-05-03 10:26:16 +02:00
hackerman-kl
7f687cae77 milan-avb: gptp: drop unused includes 2026-05-03 10:26:16 +02:00
hackerman-kl
b197ae79c5 milan-avb: gptp: align code style with the rest of module-avb 2026-05-03 10:26:16 +02:00
Nils Tonnaett
0345623e97 module-avb: remove redundant init_descriptors() call 2026-05-03 10:26:16 +02:00
Nils Tonnaett
37efd5c5f9 module-avb: emit gm_changed event 2026-05-03 10:26:16 +02:00
Nils Tonnaett
5fd9c1eaff module-avb: fail if ptp management socket can't be created 2026-05-03 10:26:16 +02:00
Nils Tonnaett
bf6fae7df9 module-avb: fail if ptp.management-socket not set 2026-05-03 10:26:16 +02:00
Nils Tonnaett
f4c26cd3ed module-avb: check that ptp management request is complete 2026-05-03 10:26:16 +02:00
Nils Tonnaett
f360af0889 module-avb: check return value of read when clearing ptp_fd input buffer 2026-05-03 09:49:22 +02:00
Nils Tonnaett
c8f2edd94e module-avb: check ioctl for success 2026-05-03 09:49:22 +02:00
Nils Tonnaett
07533eb590 module-avb: check that PTP management response is complete 2026-05-03 09:49:22 +02:00
Nils Tonnaett
6c43bdfa85 module-avb: htobe16/be16toh to htons/ntohs 2026-05-03 09:49:22 +02:00
Nils Tonnaett
16189ae167 module-avb: add specs comments for PTP management message format 2026-05-03 09:49:22 +02:00
Nils Tonnaett
fc6f2e33e2 module-avb: close ptp_fd when destroyed 2026-05-03 09:49:22 +02:00
Nils Tonnaett
f752653814 module-avb: free gptp->ptp_mgmt_socket_path 2026-05-03 09:49:22 +02:00
Nils Tonnaett
54a9495715 module-avb: check ptp management socket periodically 2026-05-03 09:49:22 +02:00
Nils Tonnaett
4bc365cbb5 module-avb: add avb interface descriptor defines 2026-05-03 09:49:22 +02:00
Wim Taymans
f29d7875cf connection: reject too large messages
Instead of silently truncating the message size in the header, simply
reject the complete message.
2026-05-01 13:04:02 +02:00
Wim Taymans
ceb80723a9 modules: unset buffer and size after alloc failure
Set the buffer_data to NULL and the size to 0 after we free the
buffer in realloc failure to avoid problems later.
2026-05-01 13:02:00 +02:00
Wim Taymans
a4fb06073c modules: protect against invalid input and 0 division
Don't crash when the AEC reported latency is invalid.

Check that the parsed values make sense and avoid division by 0.
2026-05-01 12:42:53 +02:00
Wim Taymans
6f6b58785e modules: handle allocation errors 2026-05-01 12:29:54 +02:00
Wim Taymans
a55546c9df filter-chain: limit the number of graph in/out
There is no limit on the number of inputs/outputs of a graph but the
filter-chain assumes it is at most 128 and also that there are at most
128 buffer datas.

Increase the limit (1024) and clamp and log an error when the
filter-graph has more channels. Also clamp the buffer datas so that we
don't overflow the stack allocated buffers.
2026-05-01 12:29:12 +02:00
Wim Taymans
6cc92c0e2b security: add missing NULL checks and fix error handling in modules
module-access: add NULL check after pw_properties_new for
socket_access.

module-pulse-tunnel: add NULL check after 4MB calloc for ring
buffer.

module-rt: add NULL check after calloc in thread create.

module-rtp-session: add goto error after failed
pw_net_parse_address instead of falling through.

module-snapcast-discover: fix missing null-termination on
network-received data before logging it as a string.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 18:38:32 +02:00
Wim Taymans
47ca937905 fix capture rate assignment using logical NOT instead of copy
Three modules had "impl->capture_info.rate = !impl->playback_info.rate"
which evaluates to 0 (logical NOT of a non-zero rate) instead of
copying the playback rate. This is a copy-paste typo from the line
above which correctly uses "= impl->capture_info.rate".

Affects module-filter-chain, module-loopback, module-example-filter.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 18:38:26 +02:00
Wim Taymans
4b64b81d21 security: fix crash and overflow bugs in network-facing modules
module-filter-chain: fix NULL pointer dereference when
pw_stream_dequeue_buffer returns NULL and out->requested is
accessed outside the NULL check.

module-zeroconf-discover: add NULL checks for name, type,
host_name, address, and port from mDNS lookups that could be
missing in malformed announcements.

module-raop-sink: cap net.mtu to 9000 to prevent stack overflow
via VLA uint32_t out[8 + mtu].

module-rtp-sap: fix buffer over-read in SDP "i=" line parsing
that read past a self-inserted null terminator. Also fix fd leak
when fd is 0 (fd > 0 should be fd >= 0).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 18:38:19 +02:00
Wim Taymans
c551408ec2 security: reject path traversal in echo-cancel aec_method parameter
The aec_method parameter is interpolated into a SPA library path
as "aec/libspa-aec-%s". A client could use "../" sequences to
load arbitrary SPA plugins. Reject values containing ".." or "/".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 17:44:28 +02:00
Wim Taymans
5d0e806bdb security: limit blocklist regex length in switch-on-connect module
A PulseAudio client can load this module with an arbitrarily complex
blocklist regex, causing catastrophic backtracking in regexec on
every new device. Cap the regex string at 1024 characters.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 17:40:25 +02:00
Wim Taymans
dac6b4f2c5 security: clamp negative max-clients config to zero in pulse server
A negative max-clients value in the config is parsed as int then
assigned to uint32_t, wrapping to UINT32_MAX and effectively
disabling the client limit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 17:28:02 +02:00
Wim Taymans
c38a32e2e1 security: fix NULL pointer dereference in LADSPA sink/source modules
When sink_name/source_name is not provided, pw_properties_get for
PW_KEY_NODE_NAME returns NULL, which is then passed to
pw_properties_setf as a %s argument.

Add NULL check before calling pw_properties_setf.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 17:24:52 +02:00
Wim Taymans
99a89f8bd4 security: fix stack overflow via strndupa on long device names
A client-supplied device name ending in ".monitor" was stack-allocated
via strndupa without any size limit. Since protocol messages can be up
to 16MB, a malicious client could send a very long device name and
overflow the stack, crashing the daemon.

Cap the strndupa length at MAX_NAME (1024) in both find_device and
do_set_default.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 17:18:06 +02:00
Wim Taymans
6ea673b68a security: fix issues in pulse module core files
- volume.c: add spa_pod_is_object check before casting param to
  spa_pod_object, preventing out-of-bounds reads on malformed pods
- manager.c: add NULL check for p->param in has_param before
  dereferencing via SPA_POD_SIZE
- snap-policy.c: check strings1[1] and strings2[1] for NULL before
  passing to g_str_equal, fixing wrong operand order
- format.c: use map->channels consistently in format_build_param

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 17:08:04 +02:00
Wim Taymans
ef2541a1ef security: fix multiple issues in pulse module implementations
- module-zeroconf-publish: guard spa_hook_remove of impl_listener with
  a flag to prevent operating on uninitialized hook when unload is called
  after a partial load failure; bail out of create_service when
  pw_properties_new fails to prevent NULL dereference in publish_service
- module-device-restore: add missing NULL check after message_alloc in
  emit_event; make manager_events static const
- module-jackdbus-detect: fix memory leak on error paths in prepare by
  using goto out instead of early return; free props/sink_props/source_props
  in unload
- module-roc-sink-input: add missing valid_args whitelist
- module-rtp-recv: add missing valid_args whitelist
- module-rtp-send: add missing valid_args whitelist
- module-gsettings: add missing NULL check after strdup in load_group

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 16:50:30 +02:00
Wim Taymans
8e596bd85f impl-link: handle copy errors 2026-04-30 16:41:43 +02:00
Wim Taymans
dfc5fd86a7 pulse: only fixate when necessary
Check if the format is already fixated before doing a copy and fixate.
The copy can fail so we need to check this.
2026-04-30 16:27:15 +02:00
Wim Taymans
b7aae374bf pulse-server: keep allocate buffer size around
For playback and capture streams we allocate MAXLENGTH (4M) buffers but
for upload streams we must allow space for the total upload stream, which
can be up to the max allowed sample size (16M).

Keep the allocated size for the stream around in a variable so that we
can use it when writing/reading to/from the ringbuffer.

This could later also be extended to use the attr.maxlength variable to
size the buffer (but it's usually 4M anyway). This is more complicated
because we need to grow the buffer size when new attributes are set,
which is probably more complicated than useful.
2026-04-30 15:25:31 +02:00
Wim Taymans
32648b7cc7 pulse: handle wraparound near the end correctly
If offsetis near MAXLENGTH, we can still read past the end of the
buffer. Use the ringbuffer to wraparound.
2026-04-30 14:14:59 +02:00
Wim Taymans
b9b93f3cdb pulse: use json builder for message handler output
Makes sure we escape the string correctly.
2026-04-30 13:53:20 +02:00
Wim Taymans
96c3ada6f2 JSON: use the json builder instead of memstream and fprintf
Use the JSON builder to prepare arguments for modules and metadata
instead of custom memopen and fprintf. This makes it easier to ensure
the strings are all properly escaped.

This removes the use of spa_json_encode_string(), which could return a
truncated, non-zero terminated result, which we needed to check
everywhere.
2026-04-30 13:23:23 +02:00
Wim Taymans
4ddedc72cd security: add missing NULL checks after reply_new in stream creation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 10:23:07 +02:00
Wim Taymans
f29a4e6e14 pulse-server: fix loading of defaults 2026-04-30 10:11:38 +02:00
Wim Taymans
57c621e654 server: use the right client_fd
After accept, we transfered ownership of the client_fd to the source so
use the fd on the new owner.
2026-04-30 10:00:53 +02:00
Wim Taymans
4a34da368e security: fix potential buffer over-read in combine-sink name encoding
spa_json_encode_string was called with sizeof(name)-1, which would
not write a null terminator on truncation. Use sizeof(name) and skip
sink names that don't fit in the buffer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 09:27:37 +02:00
Wim Taymans
912f7f5c64 security: add missing NULL check after pw_properties_new in zeroconf
pw_properties_new can return NULL under OOM. The result was used
directly without a check, leading to a NULL pointer dereference.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 09:25:19 +02:00
Wim Taymans
e1f4c441f4 security: fix OOB read in IEC958 format enum parsing
In the SPA_CHOICE_Enum case, values[index+1] was used to skip the
default value at index 0, but the bounds check only validated index,
not index+1. Move bounds checks into each case with the correct limit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 09:19:41 +02:00
Wim Taymans
390874e7c3 security: fix JSON injection in simple-protocol-tcp address
The listen address was inserted into a JSON array without escaping.
Build the address string first, then encode it with
spa_json_encode_string.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 09:15:36 +02:00
Wim Taymans
0ae17566f2 security: reject unknown tags in message_get to prevent va_arg desync
The switch in message_get had no default case. An unrecognized tag byte
from a malicious client would skip the switch body without consuming
the va_arg parameter, desynchronizing all subsequent argument reads
and causing undefined behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 09:14:08 +02:00
Wim Taymans
d4a1278018 security: add missing create_tag checks in stream command handlers
do_cork_stream, do_flush_trigger_prebuf_stream, and do_set_stream_name
did not check whether the stream had completed format negotiation.
Add create_tag guards matching the pattern in do_set_stream_buffer_attr.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:26:01 +02:00
Wim Taymans
6d2600c09d security: fix one-byte OOB read in module_args_add_props
A trailing backslash in a module argument string would cause the
escape handling to advance past the null terminator, reading one
byte out of bounds on the next loop iteration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:24:13 +02:00
Wim Taymans
c6faaff410 security: add missing NULL check after strndup in cmd.c
strndup can return NULL under OOM. The result was passed directly to
spa_json_begin_array which would dereference the NULL pointer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:21:16 +02:00
Wim Taymans
8e7ca70352 security: add missing create_tag check in update_stream_sample_rate
If a client sends UPDATE_PLAYBACK_STREAM_SAMPLE_RATE before format
negotiation completes, stream->ss.rate could be 0, causing a
floating-point division by zero. Add the same create_tag guard used
in do_set_stream_buffer_attr.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:20:04 +02:00
Wim Taymans
890c06117a security: fix integer overflow in port latency offset conversion
Client-supplied int64_t offset was multiplied by 1000 without overflow
check. Use spa_overflow_mul to detect and reject values that would
overflow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:19:03 +02:00
Wim Taymans
6a8c2469c5 security: fix create_tag check to allow upload stream memblocks
The create_tag guard added in a2de6c886 also rejected memblocks for
upload streams, which never clear create_tag. Upload streams allocate
their buffer immediately, so the NULL deref risk does not apply to
them. Exempt STREAM_TYPE_UPLOAD from the check.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:16:52 +02:00
Wim Taymans
3a3579ed68 security: fix operation counter leak in operation_complete
operation_complete removed the operation from the list and freed it
but never decremented client->n_operations. After 64 completed
operations the client would be permanently locked out.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:15:22 +02:00
Wim Taymans
344c9265a6 security: fix JSON injection in pulse module arguments
Use spa_json_encode_string to escape user-supplied strings before
inserting them into JSON configs in module-always-sink,
module-x11-bell, and module-switch-on-connect.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:56:12 +02:00
Wim Taymans
7c2d8f7251 security: add missing NULL checks after message_alloc in reply
Both reply_new and reply_error passed the message_alloc result directly
to message_put without checking for NULL, which would cause a NULL
pointer dereference on allocation failure.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:54:21 +02:00
Wim Taymans
1b8962d7c2 security: fix JSON injection in native-protocol-tcp address
The listen address was inserted into JSON without escaping. Build the
address string first, then encode it with spa_json_encode_string to
prevent injection of arbitrary JSON keys.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:52:19 +02:00
Wim Taymans
c5c2d197dc security: fix JSON injection in LADSPA plugin/label strings
The plugin and label parameters in module-ladspa-sink and
module-ladspa-source were inserted into the filter-chain JSON config
without escaping. Use spa_json_encode_string to prevent injection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:50:49 +02:00
Wim Taymans
bc4e1a989c security: reject zero-channel volume in PulseAudio message parsing
read_cvolume accepted channels=0, creating a degenerate zero-length
volume array that is passed to pw_stream_set_control and SPA pod
building. Reject zero channels alongside the existing CHANNELS_MAX
upper bound check.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:23:43 +02:00
Wim Taymans
807b93fb05 security: add per-client pending sample limit in PulseAudio protocol
There was no limit on concurrent PLAY_SAMPLE operations per client.
Each creates a PipeWire stream, allowing a client to exhaust server
resources. Add a MAX_PENDING_SAMPLES (64) limit per client.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:19:08 +02:00
Wim Taymans
138e30df38 security: add per-client operation count limit in PulseAudio protocol
There was no limit on pending operations per client. Commands like
SET_SINK_VOLUME each allocate an operation that persists until a
manager sync completes. A client flooding these commands can exhaust
server memory. Add a MAX_OPERATIONS (64) limit per client.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:17:38 +02:00
Wim Taymans
f32295429f security: fix module leak on OOM in PulseAudio do_load_module
If module_create succeeded but the subsequent calloc for
pending_module failed, the module was leaked in the modules map.
Move the calloc before module_create so failure cleanup is trivial.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:13:56 +02:00
Wim Taymans
2d8dc8b457 security: fix JSON injection in PulseAudio do_set_default
The device name was interpolated into a JSON metadata string without
escaping. A node with crafted name containing quote characters could
inject arbitrary JSON keys into the default sink/source metadata.
Use spa_json_encode_string to properly escape the value.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:09:50 +02:00
Wim Taymans
d3e1be8b6e security: fix division by zero in PulseAudio set_stream_buffer_attr
A client can create a stream with invalid sample_spec (rate=0) via
format_info negotiation, then send SET_STREAM_BUFFER_ATTR before
negotiation completes. fix_playback_buffer_attr divides by ss.rate,
crashing the daemon. Reject buffer attr changes on streams that
have not completed format negotiation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:08:17 +02:00
Wim Taymans
cd7bb1e37d security: validate sample rate in PulseAudio update_stream_sample_rate
The client-provided rate was used without validation. A zero or
excessively large rate produces extreme correction values passed
to pw_stream_set_control. Reject rates that are zero or exceed
RATE_MAX.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:51:46 +02:00
Wim Taymans
5f02641859 security: add missing NULL check in PulseAudio message_dump
pw_properties_new can return NULL on OOM. Passing NULL to read_props
causes a NULL pointer dereference through pw_properties_set. Only
reachable when debug logging is enabled.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:45:09 +02:00
Wim Taymans
9c1bc64af4 security: add missing NULL check after message_alloc in PulseAudio server
message_alloc can return NULL on allocation failure but the result
was not checked, causing the next do_read call to misinterpret
the NULL as a protocol error instead of an OOM condition.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:42:59 +02:00
Wim Taymans
52afec565b security: add total sample cache size limit in PulseAudio protocol
There was no limit on the total size of the sample cache. A client
could upload many samples to exhaust server memory. Add a configurable
pulse.max-sample-cache property (default 64MB) to cap the total size
of all cached samples.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:39:57 +02:00
Wim Taymans
37990b5e90 security: add per-client stream limit in PulseAudio protocol
There was no limit on the number of streams a single client could
create. Each stream allocates a 4MB ring buffer, allowing a malicious
client to exhaust server memory. Add a configurable pulse.max-streams
property (default 64) to limit streams per client.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:28:05 +02:00
Wim Taymans
80ec1f1d10 security: fix JSON injection in PulseAudio stream-restore
The device_name from a client message was interpolated directly into
a JSON string without escaping. A malicious client could inject
arbitrary JSON keys by including quote characters in the device name.
Use spa_json_encode_string to properly escape the value.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:16:44 +02:00
Wim Taymans
a2de6c886e security: fix NULL dereference in PulseAudio handle_memblock
A client can send memblock data to a playback stream channel before
format negotiation completes and the stream buffer is allocated,
causing a NULL pointer dereference crash. Reject memblock data for
streams that are still being created (create_tag != SPA_ID_INVALID).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:12:49 +02:00
Wim Taymans
808bcf39cd security: fix heap OOB read in PulseAudio sample cache playback
The sample cache upload buffer is allocated as MAXLENGTH (4MB) but
sample->length can be up to SCACHE_ENTRY_SIZE_MAX (16MB). During
playback, the read offset can exceed the buffer size, causing an
out-of-bounds heap read. Wrap the offset into the ring buffer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:10:44 +02:00
Wim Taymans
c1f6cde926 raop: handle strdup allocation error 2026-04-29 15:52:46 +02:00
Wim Taymans
d23ec56f0d security: fix stack buffer overflow in PulseAudio channel map parsing
format_info_to_spec parses the format.channel_map property without
checking against CHANNELS_MAX (64) before writing to map->map[].
A client supplying more than 64 channel names overflows the stack-
allocated channel_map buffer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 15:49:50 +02:00
Wim Taymans
0c4a0dcf7d security: fix file descriptor leak in PulseAudio server on_connect error path
File and Resource Handling: Medium

In on_connect(), if client_new() fails or pw_loop_add_io() fails, the
accepted client_fd is never closed. The error path only calls
client_free() which relies on pw_loop_destroy_source() to close the fd,
but if the source was never created, the fd leaks.

Fix by closing client_fd in the error path when it has not been
transferred to a loop source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 14:28:06 +02:00
Wim Taymans
398326f19c security: add missing NULL checks after calloc in Bluetooth backend
Memory Safety: Medium

Two calloc() calls in backend-native.c do not check the return value
before dereferencing the pointer:

1. rfcomm_send_cmd_enqueue() allocates an rfcomm_cmd struct and
   immediately passes cmd->cmd to vsnprintf without a NULL check.

2. rfcomm_hfp_ag_clcc() allocates an updated_call struct and
   immediately dereferences updated_call->id without a NULL check.

Both would crash on allocation failure. Add NULL checks that return
an error instead of dereferencing NULL.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 14:22:53 +02:00
Wim Taymans
d4cf1d0d6f security: bound alloca size for udev property strings
Memory Safety: Low

The udev device enumeration code uses alloca(strlen(str) + 1) to
allocate stack buffers for unescaping ID_VENDOR_ENC and ID_MODEL_ENC
udev properties. These property values originate from the udev database
and could theoretically be manipulated through custom udev rules or
crafted USB device descriptors. An excessively long property value
would cause unbounded stack allocation.

Add a 1024-byte cap on the alloca size and skip the unescape step for
oversized values, falling back to the raw encoded string.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 14:22:33 +02:00
Wim Taymans
6bcefd0d59 security: add missing NULL checks after calloc/strdup in filter-graph
Memory Safety: Medium

parse_graph() does not check the return values of calloc() for
input_names/output_names arrays, or strdup() for individual name
entries. If any allocation fails, the code dereferences a NULL pointer
or stores NULL without detection. Add NULL checks that return -ENOMEM
on allocation failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 14:20:46 +02:00
Wim Taymans
715d1736e9 security: add missing NULL checks after calloc in LADSPA plugin
Memory Safety: Medium

ladspa_plugin_make_desc() calls calloc() twice without checking the
return value. If either allocation fails, the code dereferences a NULL
pointer, causing a crash. Add NULL checks after both calloc calls and
properly free the descriptor struct if the ports allocation fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 14:19:59 +02:00
Wim Taymans
5f50055750 security: add iteration limit to netjack2 sync wait loops
Add MAX_RECV_PACKETS limit to both sync_wait functions to prevent
busy-spinning on the real-time thread under a packet flood, where
SO_RCVTIMEO never fires because data is always available.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 14:18:41 +02:00
Wim Taymans
c21616d1ee security: check netjack2_init return value in driver
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 14:08:06 +02:00
Wim Taymans
0816d4a2fd security: reduce MAX_PERMISSIONS to limit alloca stack usage
Memory Safety: Medium

The parse_permissions_struct macro in protocol-native uses alloca()
to allocate space for permissions received from protocol messages.
With MAX_PERMISSIONS=4096 and sizeof(struct pw_permission)=8, this
could allocate up to 32KB on the stack from a single message. Combined
with parse_dict (up to 16KB), a crafted message could consume ~48KB
of stack space.

Reduce MAX_PERMISSIONS from 4096 to 1024 (matching MAX_DICT) to limit
the maximum stack allocation to 8KB. This is still more than sufficient
for any legitimate permission update - typical systems have far fewer
than 1024 objects that need individual permission entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 13:58:04 +02:00
Wim Taymans
c3c11e4c76 security: add max packet limit to netjack2 recv_data loop
Input Validation: High

The netjack2_recv_data loop terminates based on the is_last flag
from received network packets. A malicious peer could continuously
send packets with is_last=0, causing the receive loop to run
indefinitely and blocking the audio processing thread. This is
a denial of service vulnerability.

Add a maximum packet count (1024) per receive cycle. This is
well above what any legitimate netjack2 session would produce
but prevents a malicious peer from stalling the processing thread.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 13:58:02 +02:00
Wim Taymans
110495ed9f security: fix unchecked write_event return value in RTP MIDI
Memory Safety: Critical

write_event() returns a negative int on error (-ENOSPC or -ERANGE),
but its return value was added directly to the uint32_t len variable
without checking. A negative return value would wrap len to a very
large number due to unsigned integer conversion, causing subsequent
buffer writes to go far out of bounds. This could lead to stack
corruption and potential code execution.

Fix by checking the return value of write_event() before using it.
If write_event() fails, abort the flush operation safely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 13:57:45 +02:00
Wim Taymans
739e2d1107 netjack2: handle sync_wait errors
netjack2_driver_sync_wait can return an negative value on error, don't
do any processing in that case instead of wrapping around the negative
value into a huge unsigned int and breaking things..
2026-04-29 13:56:11 +02:00
Wim Taymans
3d414960c2 security: clamp netjack2 sync.frames to quantum limit
Clamp sync.frames to quantum_limit in both sync_wait functions so all
recv paths (float, int, opus, and the fallback memset in recv_data) use
a bounded frame count. A malicious remote could send a large sync.frames
causing buffer overflows in recv_int, recv_opus, and the unfilled-buffer
memset.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 13:42:49 +02:00
Charles
c5083e7f32 impl-core: gate portal clients on stolen-fd reconnect
When the XDG camera portal steals a PW fd and hands it to an
app, the app can connect before the session manager has set up
permissions, seeing no camera nodes.

Gate portal clients in core_hello by removing PW_PERM_R from
PW_ID_CORE, triggering the busy state so daemon stops reading from the
client socket until the session manager restores permissions.

A marker property (pipewire.access.portal.gated) notifies the
session manager to ungate after attaching the PermissionManager.
The property is cleared before each gate cycle to handle repeated
fd steals on the same client.

Only gate when the session manager has set the capability property
pipewire.access.portal.gate-supported on the client, so older session
managers that cannot ungate are unaffected. Check ALL clients in the
context rather than this client specifically because the portal may
create a brand new client for each camera session and the session
manager won't have processed it yet.

Fixes: https://gitlab.freedesktop.org/pipewire/wireplumber/-/work_items/941
2026-04-29 12:31:57 +01:00
Wim Taymans
593132e434 security: fix error path resource leaks in netjack2 manager
Fix handle_follower_available to properly clean up on all error paths
after the follower has been added to the list. Add missing NULL checks
for pw_properties_copy and check the netjack2_init return value.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 13:21:21 +02:00
Wim Taymans
4ac5364004 security: add max followers limit to netjack2 manager
Add a configurable netjack2.max-followers property (default 64) to
limit the number of concurrent followers, preventing resource exhaustion
from unbounded follower connections.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 13:10:46 +02:00
Wim Taymans
7dee2c158f security: fix integer overflow in netjack2 opus encoded size calculation
Cast the denominator to uint64_t to prevent sample_rate * 8 from
overflowing uint32_t, which could produce a tiny denominator and
an inflated max_encoded_size.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 12:39:37 +02:00
Wim Taymans
be4fe881e3 security: validate opus encoded length in netjack2 recv
Validate that the encoded length from the network does not exceed
the available encoded data region before passing it to the opus
decoder.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 12:37:55 +02:00
Wim Taymans
3a77f9c28a security: fix OOB read in netjack2 MIDI buffer parsing
Validate that the midi buffer metadata fits within the buffer size
before computing the offset, preventing a size_t underflow. Also
bounds-check non-inline event data pointers against the validated
buffer region.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 12:30:37 +02:00
Wim Taymans
0186bccdce netjack2: account for overhead
The period calculation now subtracts the per-port int32_t overhead
from max_size before computing how many float samples fit. This guarantees
active_ports * (period * sizeof(float) + sizeof(int32_t)) <= max_size, so
packet_size = sizeof(header) + active_ports * sub_period_bytes <= mtu.

sub_cycle is bounded by nframes / sub_period_size, matching the sender's
num_packets = nframes / sub_period_size. Also ensure sub_period_size != 0
to avoid division by 0.
2026-04-29 12:27:29 +02:00
Wim Taymans
2af9e879a7 netjack2: use peer params name and follower_name
peer->params.name and peer->params.follower_name are null-terminated
by nj2_session_params_ntoh, whereas the raw params->name from the network
packet had no such guarantee.
2026-04-29 11:59:55 +02:00
Wim Taymans
bc93a745ab security: add missing NULL check after strdup in MIDI server
Memory Safety: Medium

spa_bt_midi_server_new() did not check the return value of strdup()
when duplicating the characteristic path. On allocation failure, a
NULL chr_path would be returned as part of the server object,
leading to a NULL pointer dereference when later used. Add a NULL
check that jumps to the existing fail cleanup path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:36:53 +02:00
Wim Taymans
acabcf085d security: add missing NULL checks after strdup/calloc in backend-hsphfpd
Memory Safety: Medium

Multiple allocation results in the HSP/HFP daemon backend were not
checked for NULL:

- transport_data->transport_path strdup in new_audio_connection()
- endpoint->remote_address and local_address strdup in property parsing
- t_path strdup before spa_bt_transport_create()
- endpoint calloc and endpoint->path strdup in interface enumeration
- backend->hsphfpd_service_id strdup after registration

Each could cause a NULL pointer dereference under memory pressure. Add
appropriate NULL checks with error returns matching the existing patterns
in each function (DBUS_HANDLER_RESULT_NEED_MEMORY or -ENOMEM).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:36:32 +02:00
Wim Taymans
c3c1216633 security: add missing NULL check after strdup in reserve
Memory Safety: Medium

rd_device_new() did not check the return value of strdup() when
duplicating application_name. On allocation failure, a NULL pointer
would be stored and later passed to D-Bus functions, causing a
crash. Add a NULL check that jumps to the existing error_free
cleanup path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:35:31 +02:00
Wim Taymans
4de0f83aca security: add missing NULL checks after realloc/strdup in LV2 plugin
Memory Safety: Medium

Two issues in the LV2 filter-graph plugin:

1. uri_table_map(): realloc() result was assigned directly to
   table->data, losing the original pointer on failure (memory leak)
   and causing a NULL pointer dereference on the next access. Also
   the subsequent strdup() had no NULL check. Fixed by using a
   temporary pointer for realloc and checking strdup's return.

2. lv2_state_retrieve(): realloc() of sd->tmp was used without a
   NULL check, so a failed allocation would cause sd->tmp to become
   NULL and be immediately passed to spa_json_parse_stringn(). Fixed
   by checking the realloc result before assignment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:35:08 +02:00
Wim Taymans
dcf28ff248 security: add missing NULL checks after strdup in modemmanager
Memory Safety: Medium

Four strdup() calls in the ModemManager Bluetooth integration had no
NULL checks, which could lead to NULL pointer dereferences under
memory pressure:

- mm_parse_call_properties(): call->number assignment
- mm_parse_interfaces(): this->modem.path assignment
- mm_filter_cb(): call_object->path assignment (also leaked calloc
  on failure)
- mm_register(): this->allowed_modem_device assignment

Each site now checks for NULL and handles the failure appropriately
for its context (early return, goto cleanup, or return error).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:34:37 +02:00
Wim Taymans
9a4e0e4c85 security: fix format string vulnerability in hook.h example code
Input Validation: Low

The documentation example code in hook.h passed the msg parameter
directly as the format string to printf() and fprintf(). If copied
by developers, this pattern creates a format string vulnerability
where specially crafted msg content with format specifiers (%x, %n,
etc.) could read/write memory. Use "%s" as the format string and
pass msg as a data argument instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:33:44 +02:00
Wim Taymans
7982f52830 security: replace sprintf with snprintf in spa_debugc_mem
Memory Safety: Medium

The spa_debugc_mem() function used unbounded sprintf() calls to format
hex dump output into a fixed 512-byte stack buffer. While the current
line-by-line output (16 bytes per line) fits within the buffer, sprintf
provides no overflow protection if the format changes or assumptions
are violated. Replace with snprintf() using sizeof(buffer) and remaining
space tracking to guarantee the buffer cannot be overflowed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:33:25 +02:00
Wim Taymans
106f641ff3 security: add missing NULL check after strdup in pw_strv_insert
Memory Safety: Medium

In pw_strv_insert(), the strdup(str) result at the insertion position
was not checked for failure. A NULL would be stored in the string
vector, causing NULL dereferences when callers iterate the vector.

Fix by checking the strdup() return value and cleaning up on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:11:58 +02:00
Wim Taymans
e474303991 security: add missing NULL checks after strdup/strndup in pw_split_strv
Memory Safety: Medium

In pw_split_strv(), the return values of strndup() and strdup() were
passed directly to pw_array_add_ptr() without checking for NULL. If
memory allocation fails, NULL pointers would be stored in the string
array and later dereferenced by callers iterating the result.

The return value of pw_array_add_ptr() was also not checked, which
could lead to silently dropped strings.

Fix by checking both allocation and array insertion return values,
and properly cleaning up all previously allocated strings on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:06:20 +02:00
Wim Taymans
ec04c4bf9a security: add missing NULL check after strdup in pw-dump
Memory Safety: Medium

In the registry event handler, strdup(type) was not checked for
failure. A NULL o->type would cause NULL pointer dereferences in
subsequent code that uses the type string for comparison and logging.

Fix by checking the strdup() return value and cleaning up on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:06:18 +02:00
Wim Taymans
43931caccb security: add missing NULL check after strdup in context factory registry
Memory Safety: Medium

In pw_context_set_spa_libs(), strdup(lib) was not checked for failure.
A NULL entry->lib would cause a NULL dereference when the factory
library path is later looked up and used for dlopen().

Fix by checking the strdup() return value and cleaning up the regex
and array entry on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:06:16 +02:00
Wim Taymans
640af6b20f security: add missing NULL checks after strdup in data-loop
Memory Safety: Medium

In pw_data_loop construction, strdup() calls for the thread affinity
and class strings were not checked for failure. A failed strdup()
would store NULL, leading to NULL pointer dereferences when these
strings are later used for thread configuration.

Fix by checking strdup() return values and failing initialization
with -ENOMEM on allocation failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:06:11 +02:00
Wim Taymans
382533da96 security: add missing NULL checks after strdup in impl-metadata
Memory Safety: Medium

The set_item() function called strdup() for key, type, and value
without checking the return values. If any strdup() fails due to
memory exhaustion, the NULL pointer would be stored in the item
struct and later dereferenced when the metadata is accessed or
logged.

Fix by checking strdup() return values and cleaning up on failure.
Change set_item() to return an error code so callers can handle
allocation failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:06:09 +02:00
Wim Taymans
eaaf125d13 filter-graph: protect against large values
Limit the delay in the convolver to 10 seconds.

Limit the convolver block sizes to 64K.

Avoid overflows when using large rates, file size or number of
channels in the provided impulse response.
2026-04-29 11:02:11 +02:00
Wim Taymans
72b9577d3c filter: avoid losing buffers in some cases
If the filter process doesn't dequeue/queue a buffer (as can be the
case in jack-tunnel-sink under xrun cases), pw-filter will set the
io to NEED_DATA with ID_INVALID.

This will then make the mixer in the next cycle not recycle any buffers
and it won't be able to produce any new ones either.
If the filter the dequeues/queues a buffer in the next process, it won't
dequeue a buffer for recycle because io is NEED_DATA/INVALID from the
previous cycle (io != HAVE_DATA -> continue).

This will the continue in an infinite loop producing "out of buffers"
forever.

Also check that we actually have a buffer to recycle, if we don't we can
try to dequeue one and place that in the io. This will then unlock the
loop, make the mixer recycle the buffer and produce a new one.

This is the same logic as is present in pw-stream for the same reason.

Fixes #5246
Maybe also #3547
2026-04-28 14:55:13 +02:00
Wim Taymans
08efbf2254 security: add missing NULL check after calloc in plugin_builtin
Memory Safety: Medium

In the fallback code path when spa-plugins support is not compiled in,
calloc() for the output sample buffer was not checked for NULL. If the
allocation fails (e.g., due to a large n_samples value from filter
configuration), spa_memcpy would dereference a NULL pointer.

Fixed by adding a NULL check and returning NULL on allocation failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 13:05:38 +02:00
Wim Taymans
1de8615caf security: fix missing NULL check and integer overflow in AVB ringbuffer
Memory Safety: Medium

The AVB PCM ringbuffer allocation used calloc(1, size * 4) which has
two issues: the multiplication can overflow for large ringbuffer_size
values (derived from quantum_limit config parameter), and the return
value was never checked for NULL.

Fixed by using calloc(size, 4) which lets calloc check for overflow
internally, and added a NULL check for the allocation result.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 13:05:09 +02:00
Wim Taymans
bf614354cc security: fix integer overflow in pw-cli param info allocation
Memory Safety: High

Three places in pw-cli allocated param info arrays using
malloc(n_params * sizeof(struct spa_param_info)) where n_params
comes from remote protocol data. The multiplication can overflow,
causing a small buffer to be allocated while n_params remains large.
Later code iterating over n_params entries would read past the
allocated buffer.

Fixed by using calloc(n_params, sizeof(...)) which internally checks
for multiplication overflow and returns NULL on failure. Also added
NULL checks and proper fallback when allocation fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 13:04:45 +02:00
Wim Taymans
2fee779161 security: add missing NULL check after calloc in sendspin-recv
Memory Safety: Medium

The ring buffer allocation in the sendspin receiver module was not
checked for NULL. If calloc fails (e.g., due to a large stride value
from network-controlled audio format parameters), the code proceeds
to use the NULL pointer, causing a crash.

Also changed calloc(1, size*stride) to calloc(size, stride) so that
calloc itself checks for multiplication overflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 13:03:21 +02:00
Wim Taymans
e3c20982a8 security: add missing NULL checks after calloc in filter-graph
Memory Safety: Medium

Multiple calloc() calls for node port arrays and the graph handle
array were not checked for NULL returns. If memory allocation fails,
the code immediately dereferences the NULL pointers in subsequent
loops, causing a crash. An attacker who can influence the filter
graph configuration (e.g., through config files specifying many
ports) could potentially trigger this condition.

Fixed by adding NULL checks after all unchecked calloc calls and
properly cleaning up on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 13:02:50 +02:00
Wim Taymans
695f25600b security: add missing O_CLOEXEC flag to V4L2 device open
File and Resource Handling: Medium

The V4L2 device file descriptor was opened without the O_CLOEXEC flag.
If a child process is subsequently spawned (e.g., via fork+exec), the
video device fd would be inherited, potentially allowing the child
process unauthorized access to the camera device.

Fixed by adding O_CLOEXEC to the open() flags.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 12:56:40 +02:00
Wim Taymans
a12cc84df4 security: fix integer overflow in PulseAudio message read_arbitrary
Memory Safety: High

The read_arbitrary() bounds check used `m->offset + len > m->length`
where len is an attacker-controlled uint32_t read from the PulseAudio
protocol message. When m->offset is small and len is close to
UINT32_MAX, the addition wraps around to a small value, bypassing
the bounds check. This allows read_arbitrary() to return a pointer
within the message buffer but report an enormous length to the caller,
leading to out-of-bounds memory reads.

Fixed by rearranging the arithmetic to use subtraction:
`len > m->length - m->offset`, which cannot overflow since
m->offset <= m->length is maintained as an invariant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 12:56:28 +02:00
Wim Taymans
7bfa93de05 security: add missing O_CLOEXEC/SOCK_CLOEXEC flags
File and Resource Handling: Medium

Several file and socket operations were missing the close-on-exec flag,
which causes file descriptors to leak to child processes created via
fork+exec. This could allow child processes unintended access to
privileged resources.

- node-driver.c: SOCK_DGRAM socket for SIOCETHTOOL ioctl leaked to
  child processes
- pw-container.c: Unix domain listen socket leaked to spawned
  container processes
- compress-offload-api.c: ALSA compress-offload device fd leaked to
  child processes

Added O_CLOEXEC to open() calls and SOCK_CLOEXEC to socket() calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 12:20:33 +02:00
Wim Taymans
3b7e9b0779 security: replace atoi() with validated parsing in RAOP module
Input Validation: Medium

The RAOP sink module used atoi() to parse port numbers from RTSP
Transport headers received over the network. atoi() does not validate
input and its int return was silently truncated to uint16_t, meaning
out-of-range or negative values could produce unexpected port numbers.

Replaced RTSP Transport header port parsing with strtoul() plus range
validation (1-65535). Replaced the raop.port property parsing with
spa_atou32() and range checking. Replaced raop.latency.ms parsing with
spa_atou32() for consistency with safe parsing patterns used elsewhere
in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 12:16:06 +02:00
Wim Taymans
a1aa9b0d75 security: replace atoi() with spa_atou32() for RTP session parameters
Input Validation: Medium

The RTP-SAP module used atoi() to parse rtp.rate, rtp.channels,
rtp.ssrc, and rtp.ts-offset properties into uint32_t fields. atoi()
returns int, which has undefined behavior on overflow and silently
converts negative values. When assigned to uint32_t, a negative result
wraps to a large value.

These properties can originate from received SDP announcements over the
network. Replaced with spa_atou32() which properly validates the input
and rejects non-numeric or out-of-range values. This is consistent with
how the same function already handles rtp.framecount using spa_atou32().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 12:14:46 +02:00
Wim Taymans
7465199fff security: replace unsafe atoi() with validated parsing in websocket
Input Validation: High

The WebSocket HTTP reply parser used atoi() to parse the Content-Length
header from network data. atoi() does not detect overflow or invalid
input, and its int return value was assigned to a size_t, meaning a
negative value from a malicious server would silently convert to a very
large unsigned value, potentially causing excessive memory allocation.

Replaced with spa_atou32() which validates the entire string is a valid
number and fits in uint32_t, plus an explicit upper bound (16 MB) on
content length to prevent resource exhaustion.

Similarly, pw_websocket_listen() used atoi() to parse the port number
into a uint16_t without validation. Replaced with spa_atou32() and a
range check against 65535.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 12:14:36 +02:00
Wim Taymans
aaa7076b52 acp: partially revert f76327e076
The Line Out mute seems to break things.

See #5246
2026-04-28 12:01:06 +02:00
Wim Taymans
06421554d3 security: cap alloca size in JSON-to-POD string conversion
Memory Safety: Medium

spa_json_to_pod_part() uses alloca(len+1) to allocate a stack buffer
for JSON string values, where len comes from the JSON parser. Since
this function is recursive (for nested JSON objects/arrays), a
crafted JSON document with large string values can cause stack
exhaustion through unbounded alloca calls.

Add a size check capping the alloca to 8192 bytes, which is generous
for all legitimate PipeWire configuration values (type names, IDs,
property strings) while preventing stack overflow from malicious or
malformed JSON input.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 11:52:41 +02:00
Wim Taymans
39ac8cf996 filter-chain: improve docs about LADSPA 2026-04-28 11:32:53 +02:00
Wim Taymans
026ae3af7a security: add bounds check for exec argv array in filter-graph
Memory Safety: Medium

The do_exec() function in the filter-graph builtin plugin parses a
JSON array of arguments into a fixed-size argv[512] stack buffer
without checking whether argc exceeds the array bounds. A crafted
filter-graph configuration with more than 511 arguments would cause
a stack buffer overflow.

Add a bounds check before each insertion to ensure argc stays within
the array limits, reserving space for the NULL terminator.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 10:41:34 +02:00
Wim Taymans
9f3d894c10 audiomixer: rate limit the "out of buffers" debug
See #5249
2026-04-28 10:34:39 +02:00
Wim Taymans
b2790f610c debug: demote some info log to debug 2026-04-28 09:50:02 +02:00
Wim Taymans
5faf043f6c roc-source: handle some errors better 2026-04-27 18:44:04 +02:00
Wim Taymans
c889edf172 roc-source: start/stop receiving in streaming/pause
Only start receiving packets when we are streaming.

Otherwise the ROC source will start receiving and queueing packets and
consume a lot of memory while we don't read the packets from the queue.

Likewise, stop receiving packets when we pause.

Fixes #5250
2026-04-27 18:29:39 +02:00
Wim Taymans
f00c84ccad security: replace strcpy with memcpy in alsa_id_decode
Memory Safety: Low

alsa_id_decode() uses strcpy() to copy into a caller-provided buffer
without knowing its size. Although all current callers allocate the
buffer correctly (via alloca(strlen(src) + 1) or with a pre-validated
fixed buffer), the function signature does not encode this requirement.
Replace strcpy with memcpy using the known source length to make the
bounded copy explicit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 16:15:10 +02:00
Wim Taymans
ebe9b087ad security: replace strcat with bounds-explicit memcpy in pulse utils
Memory Safety: Low

Although the preceding length check ensures the strcat is safe, using
strcat makes the bounds guarantee implicit. Replace with memcpy using
the already-computed length, making the bounded copy explicit and
avoiding a redundant scan of the destination string.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 16:14:23 +02:00
Wim Taymans
1ebbd9d7bc security: replace strcpy with memcpy using known lengths in pw-dump
Memory Safety: Low

The strcpy() calls here operate on buffers that are correctly sized,
but using strcpy obscures the bounds guarantee and forces redundant
strlen() calls to compute pointer offsets. Replace with memcpy()
using the lengths already computed for the allocation, making the
bounds safety explicit and avoiding repeated string scanning.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 16:13:52 +02:00
Wim Taymans
d456be1943 security: fix strcpy into fixed-size buffer in netjack2 driver
Memory Safety: Low

strcpy() into the fixed-size params.type[8] buffer has no bounds
checking. While the current literal string "params" fits exactly,
this pattern is fragile and would silently overflow if the string
were ever changed. Use snprintf() with sizeof() for bounds safety,
consistent with how params.name and params.follower_name are
handled on the lines immediately following.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 16:13:34 +02:00
Wim Taymans
9b845f4415 security: fix unsafe atoi() on network RTSP status code
Input Validation: Medium

atoi() on network-received data returns 0 on parse failure, which is
indistinguishable from a valid "0" input. It also accepts negative
values and does not detect overflow. Replace with strtol() and
validate that the status code is in the valid HTTP/RTSP range
(100-599) to prevent protocol state confusion from malformed
responses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 16:13:14 +02:00
Wim Taymans
ca0fa1e4e1 security: fix missing NULL check after strdup in module-raop-discover
Memory Safety: Medium

strdup() can return NULL on allocation failure. The return value was
used without checking, which would cause a NULL pointer dereference
(crash) when the name is later compared with spa_streq(). Add a NULL
check and free the partially-allocated struct on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 16:12:47 +02:00
Wim Taymans
15c32c66f0 security: fix command injection via system() in pw-container
Input Validation: High

system() passes its argument to /bin/sh -c, which interprets shell
metacharacters (;, |, &&, $(), etc.). If pw-container is invoked by
another program with untrusted input, this allows arbitrary command
execution. Replace with fork()+execvp() which executes the command
directly without shell interpretation, and passes all remaining
arguments to the child process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 16:12:26 +02:00
Wim Taymans
edb3c27aa4 channelmix: add SEE 7p1 to stereo downmix 2026-04-27 15:59:38 +02:00
Wim Taymans
67f1e3a889 combine-stream: add combine.mode = monitor
Add a monitor mode that creates an Audio/Source combining audio from the
monitor ports of all Audio/Sink nodes. This allows capturing everything
that is being played back across all sinks into a single source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 13:36:35 +02:00
Wim Taymans
87ee525b01 security: limit RTSP content-length and check allocation in RAOP client
Input Validation / Memory Safety: Medium

The RTSP client used for RAOP/AirPlay communication accepted arbitrarily
large Content-Length values from the remote server without any upper
bound. A malicious or compromised AirPlay server could specify a very
large Content-Length, causing the client to allocate unbounded memory
and potentially exhaust system resources (denial of service).

Additionally, the return value of pw_array_add() was not checked. If
the allocation failed, the subsequent memcpy would dereference a NULL
pointer, causing a crash.

Add a 64KB limit on Content-Length (more than sufficient for RTSP
control messages) and check the pw_array_add return value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 13:30:36 +02:00
hackerman-kl
74b6f237d1 milan-avb: mvu certification number Disclamer to avoid any confusion 2026-04-27 10:56:44 +00:00
hackerman-kl
30711940c4 milan-avb: aecp: route VENDOR_UNIQUE_COMMAND through msg_info table 2026-04-27 10:56:44 +00:00
hackerman-kl
a16f3d704e milan-avb: aecp-vendor-unique-milan-v12: dispatch via per-cmd table per Milan v1.2 Section 5.4.4 2026-04-27 10:56:44 +00:00
hackerman-kl
deeea620f6 milan-avb: aecp-aem: GET_AS_PATH placeholder per IEEE 1722.1-2021 Section 7.4.41 2026-04-27 10:56:44 +00:00
hackerman-kl
df1605a333 milan-avb: entity-model: advertise VENDOR_UNIQUE_SUPPORTED in capabilities 2026-04-27 10:56:44 +00:00
hackerman-kl
d8b9a0f5ab milan-avb: aecp-aem: GET_STREAM_INFO CDL excludes 12-octet AVTPDU common 2026-04-27 10:56:44 +00:00
hackerman-kl
c967b39f18 milan-avb: avdecc: drop dead debug gate around avb_log_state 2026-04-27 10:56:44 +00:00
hackerman-kl
9c0007173b milan-avb: stream: wire Milan Section 5.4.5 stream counters, TX heartbeat, and MAX_TRANSIT_TIME plumbing 2026-04-27 10:56:44 +00:00
hackerman-kl
16d793db38 milan-avb: acmp: fixing the missing stream deactivate 2026-04-27 10:56:44 +00:00
hackerman-kl
de17f14da4 milan-avb: introducing GET_AS_PATH and GET/SET_MAX_TRANSIT 2026-04-27 10:56:44 +00:00
hackerman-kl
197bab7931 milan-avb: hook stream output to MSRP listener_observed + add max_transit_time_ns 2026-04-27 10:56:44 +00:00
hackerman-kl
a5fbeef6f8 milan-avb: add AVDECC stream_format decoder in aecp-aem.h 2026-04-27 10:56:44 +00:00
hackerman-kl
d9f8bacc76 milan-avb: AEM non-success replies preserve command payload size 2026-04-27 10:56:44 +00:00
hackerman-kl
25e3556050 milan-avb: ACMP status use the status of the FSM rather than the connection count to decide if bound or not 2026-04-27 10:56:44 +00:00
hackerman-kl
52c6c0a0cf milan-avb: GET_STREAM_INFO: fixing the bound state according tol the ACMP status 2026-04-27 10:56:44 +00:00
hackerman-kl
0bf4864d84 milan-avb: move teh descriptor FAM at the end of the structure to avoid overflow 2026-04-27 10:56:44 +00:00
hackerman-kl
4d33f57325 milan-avb: msrp: add debug msrp_talker back 2026-04-27 10:56:44 +00:00
hackerman-kl
ce42b7c1da milan-avb: msrp: mark listener stream-info dirty on TA/TF registrar change 2026-04-27 10:56:44 +00:00
hackerman-kl
995def4927 milan-avb: msrp: log notify_* at info level by default 2026-04-27 10:56:44 +00:00
hackerman-kl
76e7806251 milan-avb: cmd-get-set-stream-info: treat LV registrar as still registering 2026-04-27 10:56:44 +00:00
hackerman-kl
9f81c82100 milan-avb: avdecc: drive periodic timer at 100 ms 2026-04-27 10:56:44 +00:00
hackerman-kl
df62776308 milan-avb: mrp: set vector lva bit on outgoing LeaveAll frames 2026-04-27 10:56:44 +00:00
hackerman-kl
5c9a06c03d milan-avb: mrp: registrar treats RX_IN as a registration event 2026-04-27 10:56:44 +00:00
hackerman-kl
3b820add3b milan-avb: acmp-milan-v12: 'tmr_delay no saved packet' to debug, it may
happen
2026-04-27 10:56:44 +00:00
hackerman-kl
0572e41b65 milan-avb: acmp-milan-v12: 'no timer' not at warning, debug 2026-04-27 10:56:44 +00:00
hackerman-kl
0080739830 milan-avb: meson: register new module-avb sources 2026-04-27 10:56:44 +00:00
hackerman-kl
e46f2487fb milan-avb: aecp-vendor-unique-milan-v12: add Milan MVU handler 2026-04-27 10:56:44 +00:00
hackerman-kl
8bcdc2896c milan-avb: cmd-get-as-path: add command handler stub 2026-04-27 10:56:44 +00:00
hackerman-kl
2f4dbe3ca7 milan-avb: cmd-audio-mappings: add command handler stub 2026-04-27 10:56:44 +00:00
hackerman-kl
d9f224b122 milan-avb: cmd-start-stop-streaming: add command handler 2026-04-27 10:56:44 +00:00
hackerman-kl
6bf27b6c4e milan-avb: cmd-get-set-stream-info: add command handler 2026-04-27 10:56:44 +00:00
hackerman-kl
99c9248a17 milan-avb: cmd-get-counters: add header 2026-04-27 10:56:44 +00:00
hackerman-kl
6cc669e4e2 milan-avb: stream: Milan listener registrar and stream-output prep 2026-04-27 10:56:44 +00:00
hackerman-kl
d139b97a28 milan-avb: es-builder: allocate Milan wrapper for stream descriptors 2026-04-27 10:56:44 +00:00
hackerman-kl
e9e271ec30 milan-avb: aecp: dispatch Milan MVU vendor-unique commands 2026-04-27 10:56:44 +00:00
hackerman-kl
363418bee2 milan-avb: aecp-aem: stream-info dirty tracking and unsolicited counters 2026-04-27 10:56:44 +00:00
hackerman-kl
7f558a1a3b milan-avb: aecp-aem-state: add interface counters and descriptor storage 2026-04-27 10:56:44 +00:00
hackerman-kl
38f3cdf7cf milan-avb: aecp-aem: Milan flags_ex bitfield refactor 2026-04-27 10:56:44 +00:00
hackerman-kl
0a02161943 milan-avb: acmp: log state on incoming messages 2026-04-27 10:56:44 +00:00
hackerman-kl
b2a5f7f97e milan-avb: avdecc: add avb_log_state aggregator and detailed send error 2026-04-27 10:56:44 +00:00
hackerman-kl
18b61154cd milan-avb: acmp-milan-v12: log_state diagnostic and FSM refinements 2026-04-27 10:56:44 +00:00
hackerman-kl
b126943143 milan-avb: msrp: state logging, Milan listener_observed and log refinements 2026-04-27 10:56:44 +00:00
hackerman-kl
2cc60d6167 milan-avb: adp: add log_state diagnostic 2026-04-27 10:56:44 +00:00
hackerman-kl
e7f2fc9ab0 milan-avb: mrp: expose applicant/registrar state accessors 2026-04-27 10:56:44 +00:00
hackerman-kl
ca039e5e25 milan-avb: stream: track descriptor index in struct stream 2026-04-27 10:56:44 +00:00
hackerman-kl
e8e7f7a9fb milan-avb: mvrp: drop notify VID to debug log level 2026-04-27 10:56:44 +00:00
hackerman-kl
2d476cab79 milan-avb: msrp: gate listener Ready on talker registrar 2026-04-27 10:56:44 +00:00
hackerman-kl
080d4b6f09 milan-avb: msrp: capture listener rx param 2026-04-27 10:56:44 +00:00
hackerman-kl
2118aefcdd milan-avb: aecp-aem: NOT_IMPLEMENTED replies use CDL=12 2026-04-27 10:56:44 +00:00
hackerman-kl
5355671022 milan-avb: mrp: fix REG log notify label 2026-04-27 10:56:44 +00:00
hackerman-kl
e6f1245ffc milan-avb: cmd-get-counters: fix CDL field 2026-04-27 10:56:44 +00:00
hackerman-kl
5bc1eafd3e milan-avb: streams: seperate legacy-avb and milan-avb 2026-04-27 10:56:44 +00:00
hackerman-kl
6cb03f8e04 milan-avb: acmp: do not return 0xfffe as a guid in the case of a null peer-id 2026-04-27 10:56:44 +00:00
hackerman-kl
4cabb5842c module-avb: es_builder: re-enable the msrp on interface (domain) and mvrp 2026-04-27 10:56:44 +00:00
hackerman-kl
2013ded3cb modules-avb: milan: acmp: FAST_CONNECT is not working now without any non-volatile configuration 2026-04-27 10:56:44 +00:00
hackerman-kl
2e2c365d57 modules-avb: legacy-avb: use old way of init avb_interface 2026-04-27 10:56:44 +00:00
hackerman-kl
b2e45b8151 module-avb: milan: es_builder: ensure that the created for the milan differently than for the legacy-avb 2026-04-27 10:56:44 +00:00
hackerman-kl
c7f4108eff module-avb: milan: acmp: fix activate to work according to the Milan specication, just SRP 2026-04-27 10:56:44 +00:00
hackerman-kl
6c5c184e6d module-avb: milan: acmp: return IMCOMPATIBLE if the interface is invalid 2026-04-27 10:56:44 +00:00
hackerman-kl
fdcd818fba module-avb: milan: return if server endity-id is different than the talker_guid 2026-04-27 10:56:44 +00:00
hackerman-kl
b8b58c81f2 module-avb: milan: implement the acmp's talker 2026-04-27 10:56:44 +00:00
hackerman-kl
baec6f9e33 module-avb: milan: use the msrp and store the vlan_id 2026-04-27 10:56:44 +00:00
hackerman-kl
393a1809ca module-avb: milan: use the msrp etc... instead of duplicating things 2026-04-27 10:56:44 +00:00
hackerman-kl
45a94f58bd milan-avb: stream: initialisation of the msrp/mrp stream components 2026-04-27 10:56:44 +00:00
hackerman-kl
5b06b5457a milan-avb: msrp/acmp: talker failed can be handled now 2026-04-27 10:56:44 +00:00
hackerman-kl
f5e97f0f6b milan-avb: adjust the msrp/adp and acmp state machine to communicate talker discovery and srp reserveration 2026-04-27 10:56:44 +00:00
hackerman-kl
219adaa456 milan-avb: adjusting the msrp-domain to work accordingly to the milan specification 2026-04-27 10:56:44 +00:00
hackerman-kl
2746898e9a milan-avb: rework mrp join/begin:
* join/begin mrp protocol for attributes of mvrp and msrp within stream_activate.
 * Creation of the attribute done on stream creation during es_buidler
2026-04-27 10:56:44 +00:00
hackerman-kl
0309d598a1 milan-avb: use vlan and mac address from the stream itself 2026-04-27 10:56:44 +00:00
hackerman-kl
e9a51bd84f milan-avb: rename msrp attribute to stream_attr 2026-04-27 10:56:44 +00:00
hackerman-kl
42925490bd milan-avb: a SRP domain may be defined per interface, make it possiblie ot have multiple avb domain per interface 2026-04-27 10:56:44 +00:00
hackerman-kl
4856f85de2 first draft for ACMP/ timeout handling, and communication between SRP/ADP and the ACMP state machine 2026-04-27 10:56:44 +00:00
hackerman-kl
ad543e37f5 milan-avb: split the acmp module into milan and legacy-avb 2026-04-27 10:56:44 +00:00
hackerman-kl
9ee0ddf24f milan-avb: preparing acmp state machine dividing between milan and legacy avb 2026-04-27 10:56:44 +00:00
hackerman-kl
d46523e6ad module-avb: milan: introducing GET_DYNAMIC_INFO 2026-04-27 10:56:44 +00:00
Wim Taymans
42d51098ae security: validate recv length and use overflow-safe bounds in NetJack2 OPUS/INT
Memory Safety: High

The netjack2_recv_opus() and netjack2_recv_int() functions had two
issues:

1. Missing recv length validation: after recv(), neither function
   checked that the received data was at least sizeof(header) bytes.
   A short packet would cause the pointer to advance past received
   data, reading uninitialized VLA memory into the encoded buffer.

2. Integer overflow in bounds check: the expression
   (active_ports-1)*max_encoded + sub_cycle*sub_period_bytes + data_size
   uses sub_cycle from the network packet header. A large sub_cycle
   value can overflow the uint32_t multiplication, wrapping around to
   a small value and bypassing the encoded_size bounds check, leading
   to an out-of-bounds write into encoded_data.

Additionally, validate that the received data is large enough for the
active_ports * data_size memcpy to prevent reading past the buffer.

Fix by adding recv length checks, using spa_overflow_mul/add for the
bounds arithmetic, and validating recv'd data covers the copy region.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 12:47:55 +02:00
Wim Taymans
931505a0e4 security: validate packet length in AVB IEC 61883 stream handler
Input Validation: High

The on_socket_data() handler only checked that the received packet was
at least avb_packet_header size before casting to avb_packet_iec61883,
which is larger. A packet between these two sizes would cause
out-of-bounds reads when accessing iec61883 fields like data_len.

Additionally, handle_iec61883_packet() used the data_len field from the
packet to determine how many bytes to copy into the ring buffer without
checking that the claimed data_len didn't exceed the actual received
data. A crafted packet with an inflated data_len could cause an
out-of-bounds read from the receive buffer.

Fix by requiring the minimum packet size to cover both the ethernet
header and the iec61883 header, and by validating that the claimed
payload size doesn't exceed the received data length.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 12:37:54 +02:00
Wim Taymans
918d0f2f8a security: validate packet bounds in AVB MRP protocol parser
Input Validation: High

The avb_mrp_parse_packet() function, used by both MSRP and MVRP
protocol handlers, had several missing bounds checks:

1. No minimum length validation: the parser began accessing packet
   data at sizeof(avb_packet_mrp) without checking len was large
   enough, causing out-of-bounds reads on truncated packets.

2. Unsafe loop terminator checks: the while loops checked m[0] and
   m[1] without ensuring at least 2 bytes remained in the buffer.

3. Missing hdr_size bounds check: the header size returned by the
   check_header callback was used to advance the pointer without
   verifying it stayed within the packet bounds.

Fix by adding a minimum packet length check, using structure-size-aware
bounds checks in loop conditions, and validating hdr_size against
remaining packet data before advancing the pointer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 12:37:51 +02:00
Wim Taymans
f16042d52a security: validate packet length in AVB MAAP message handler
Input Validation: High

The maap_message() handler cast the incoming network data directly to
avb_packet_maap without checking that the received data was at least
sizeof(avb_packet_maap) bytes. The caller only validates the packet is
at least avb_packet_header size, which is smaller. A truncated MAAP
packet could cause out-of-bounds reads when accessing request_start,
request_count, conflict_start, and conflict_count fields in the probe
and defend handlers.

Fix by adding a minimum packet length check at the beginning of
maap_message().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 12:37:49 +02:00
Wim Taymans
c9d4854114 security: validate packet length in AVB ADP message handler
Input Validation: High

The adp_message() handler accessed avb_ethernet_header and
avb_packet_adp fields from network packet data without checking that
the packet was large enough to contain these structures. A truncated
ADP packet could cause out-of-bounds reads when accessing entity_id,
message_type, and other header fields.

Fix by adding a minimum packet length check before any field access.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 12:37:46 +02:00
Wim Taymans
11226544f7 security: validate packet length in AVB ACMP message handler
Input Validation: High

The acmp_message() handler accessed fields of avb_ethernet_header and
avb_packet_acmp from network packet data without first checking that
the received packet was large enough to contain these structures.
A short packet could cause out-of-bounds reads when accessing packet
header fields.

The VLA-based reply buffers in reply_not_supported(),
handle_connect_tx_command(), and handle_disconnect_tx_command() also
lacked an upper bound on the packet length, allowing a packet claiming
a very large size to cause excessive stack allocation.

Fix by adding minimum length (sizeof(header) + sizeof(acmp)) and
maximum length (MTU) validation at the entry point before any field
access or buffer allocation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 12:37:43 +02:00
Wim Taymans
0d41a7b82f security: validate Apple MIDI packet length and name termination in RTP session
Input Validation: High

The Apple MIDI command parser in module-rtp-session had two issues:

1. Insufficient minimum length check: the caller validated len >= 12,
   but struct rtp_apple_midi is 16 bytes (cmd + protocol + initiator +
   ssrc). Accessing hdr->ssrc on a 12-byte packet reads 4 bytes past
   the received data.

2. Missing null-termination check: the name field (flexible array
   member) from the network packet was passed directly to pw_log_info
   with %s format and to find_session_by_addr_name for string
   comparison, without verifying it contains a null terminator within
   the received data. This could read past the receive buffer into
   uninitialized stack memory, potentially leaking data into logs.

Fix by adding a sizeof check in parse_apple_midi_cmd and by validating
that the name is null-terminated within the received data in
parse_apple_midi_cmd_in.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 12:35:06 +02:00
Wim Taymans
60e2857d82 security: fix incorrect sizeof in RAOP packet size log messages
Input Validation: Low

The log messages for short timing and control packets used sizeof(bytes)
(size of the ssize_t variable, always 8 on 64-bit) instead of
sizeof(packet) (the actual expected packet size). This caused misleading
log output that could mask packet truncation attacks or debugging issues
with RAOP timing/control packet validation.

Fix by using sizeof(packet) to correctly report the expected packet size.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 12:32:00 +02:00
Wim Taymans
8ed6fe5edf security: fix infinite loop via MSG_PEEK on mismatched NetJack2 packets
Memory Safety: High

When netjack2_recv_data() receives a packet that doesn't match the
expected data_stream or id, it logs "not our packet" and continues the
loop. However, since the previous recv() used MSG_PEEK, the packet is
not consumed from the socket buffer. This causes the loop to spin
indefinitely on the same mismatched packet, consuming 100% CPU.

A remote attacker on the same network can trigger this by sending a
single crafted NetJack2 packet with a mismatched stream or id field,
causing a denial of service on the audio processing thread.

Fix by consuming (discarding) the mismatched packet with a non-peeking
recv() before continuing the loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 12:31:09 +02:00
Wim Taymans
daa66c0646 overflow: fix some more potential overflows 2026-04-27 12:29:31 +02:00
Wim Taymans
fb4e148985 conf: avoid overflow in pw_strv_insert_at
Purely theoretical when there are a lot of arguments...
2026-04-27 12:15:32 +02:00
Wim Taymans
710414730d security: validate packet length in AVB AECP AEM command handlers
Memory Safety: High

Multiple AVB AECP AEM command handler functions copied network packet
data into stack buffers via memcpy(buf, m, len) without validating
that len fits within the destination buffer. A crafted AVB packet with
an oversized length could overflow the stack buffer.

Added bounds validation before each memcpy in:
- cmd-available.c: handle_cmd_entity_available_milan_v12
- cmd-get-set-configuration.c: set and get configuration handlers
- cmd-get-set-sampling-rate.c: unsolicited, invalid response, and get handlers
- cmd-get-set-stream-format.c: get and set stream format handlers
- cmd-lock-entity.c: handle_cmd_lock_entity_milan_v12

This matches the bounds checking pattern already used in
cmd-get-set-control.c, cmd-get-set-clock-source.c, and
cmd-get-set-name.c.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:35:41 +02:00
Wim Taymans
0bd9a4d033 security: use overflow-safe arithmetic for NetJack2 MIDI buffer sizes
Memory Safety: High

The recv_midi function calculated MIDI buffer usage from network packet
fields (event_count, write_pos) using plain arithmetic that could
overflow on 32-bit platforms. A crafted NetJack2 packet with a large
event_count could wrap the size_t multiplication, bypassing the bounds
check and causing out-of-bounds memory access. Replaced with
spa_overflow_mul/spa_overflow_add to detect overflow before use.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:27:34 +02:00
Wim Taymans
6efaf12d00 security: clamp channel count in PulseAudio volume control handler
Memory Safety: High

The stream_control_info() callback copied control->n_values floats
into stream->volume.values without checking bounds. The source allows
up to MAX_VALUES (256) entries but the destination volume array is
only CHANNELS_MAX (64) entries, so a stream with more than 64 channel
volumes would overflow the buffer. Clamp n_values to CHANNELS_MAX
before the copy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:24:30 +02:00
Wim Taymans
88a3bf8aab security: validate packet length in AVB get_avb_info handler
Memory Safety: High

The handle_get_avb_info_common() function copied network packet data
into a stack buffer using memcpy(buf, m, len) without validating that
len fits within the 2048-byte buffer. A crafted AVB packet with a
large length could overflow the stack buffer. Added bounds validation
matching the pattern already used in handle_read_descriptor_common().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:24:11 +02:00
Wim Taymans
46eefd16ee security: fix out-of-bounds read in AVB AECP AEM command handler
Memory Safety: High

The cmd_names[] array was indexed with a network-provided command type
value before the bounds check, allowing an out-of-bounds read when
processing crafted AVB network packets. Moved the bounds validation
before the array access to prevent reading past the end of the array.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:23:44 +02:00
Wim Taymans
328ab5a611 security: validate VBAN MIDI variable-length integers to prevent overflow
Memory Safety: High

The VBAN MIDI parse_varlen() function performed unbounded left-shifting
of a uint32_t value without overflow checking, allowing a crafted VBAN
network packet to cause integer overflow. This could produce incorrect
size calculations in get_midi_size(), leading to out-of-bounds memory
access when processing MIDI packets.

Added overflow guard (value > UINT32_MAX >> 7) matching the existing
fix in the RTP MIDI implementation, plus an overflow check on the size
addition in get_midi_size() and an avail bounds check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:22:50 +02:00
Wim Taymans
83373292f0 security: clear RAOP auth nonce and realm before freeing
Information Disclosure: Low

The RAOP module's connection cleanup frees the Digest authentication
nonce and realm strings without clearing them first. The nonce is
cryptographic material used in the Digest auth response, and the realm
is combined with the password to produce the h1 hash. After free(),
this data persists in heap memory and could be recovered through memory
disclosure vulnerabilities or core dumps.

Apply explicit_bzero before freeing, consistent with the existing
treatment of impl->password in the module destroy path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:08:04 +02:00
Wim Taymans
a7619fdfdb security: validate MTU bounds in NetJack2 to prevent stack overflow
Memory Safety: High

The NetJack2 driver and manager modules use VLA (variable-length array)
stack buffers sized by peer->params.mtu in every send and receive
function. In the driver module, this MTU value comes directly from the
remote peer via nj2_session_params_ntoh() without any upper bound
validation. A malicious remote peer could advertise an extremely large
MTU value (up to UINT32_MAX), causing multi-gigabyte VLA stack
allocations that overflow the stack.

Both modules also read net.mtu from user properties via
pw_properties_get_uint32() without capping the value, even though
MAX_MTU (9000) was already defined but never enforced.

Add MTU validation against MAX_MTU in the driver's session setup
handler, and cap the configured MTU value in both driver and manager
initialization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:06:14 +02:00
Wim Taymans
e2c7ed2d0c security: clear auth credential buffers from stack after use
Information Disclosure: Medium

The RAOP authentication header construction leaves sensitive material
on the stack after the function returns: Base64-encoded credentials in
enc[], MD5 password-derived hashes in h1/h2/resp[], and the assembled
Authorization header in auth[]. These persist in stack memory and can
be recovered via core dumps, memory disclosure vulnerabilities, or
cold boot attacks.

The plaintext password buffer (buf[]) was already properly cleared with
explicit_bzero, but the derived credential buffers were not. Apply
explicit_bzero to enc, h1, h2, resp, and auth before returning.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:05:20 +02:00
Wim Taymans
c525cfcced security: reject negative DBus array lengths in Bluetooth transport
Memory Safety: High

dbus_message_iter_get_fixed_array() returns the array length as a
signed int. A malformed DBus message could produce a negative length
value. In the Configuration property handler, the check 'if (!len)'
does not catch negative values, allowing negative lengths to be passed
to malloc() and memcpy() where sign extension to size_t creates
enormous values. The debug logging call spa_debug_log_mem() also
receives the negative length cast to size_t, causing an out-of-bounds
read.

In the Capabilities/Metadata handler, 'if (n)' is similarly true for
negative values, and the negative int assigned to the size_t *size
output parameter corrupts the stored length.

Fix by using 'len <= 0' and 'n > 0' checks respectively, and move
debug logging after validation. Explicitly zero the length on the
negative/zero path to prevent storing corrupted sizes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:04:52 +02:00
Wim Taymans
f3538dd7fe security: validate metadata length before subtraction in BIS config
Memory Safety: Critical

When a Bluetooth BIS metadata entry has length=0 (e.g. when the JSON
config contains a "type" key but no "value" key, leaving the
calloc-initialized length at zero), the expression
'metadata_entry->length - 1' underflows to SIZE_MAX because the int
value is implicitly converted to size_t in the memcpy call. This causes
memcpy to read far past the metadata_entry->value buffer, leading to a
heap buffer overflow and likely crash.

Add a check that metadata_entry->length >= 1 before the subtraction,
rejecting entries with invalid length.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:03:54 +02:00
Wim Taymans
470c63d436 security: log warning when falling back to weak PRNG
Cryptography: Low

When getrandom() fails, pw_random() silently falls back to rand() or
random_r() seeded from the system clock. This fallback produces
predictable output that should not be used for security-sensitive
operations like WebSocket key generation or network protocol IDs.

Add a warning log message when the fallback is triggered so that
administrators are aware of the degraded random number generation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:03:12 +02:00
Wim Taymans
ebbc9acc90 security: downgrade RAOP auth logging from info to debug level
Information Disclosure: High

The RTSP client logs all HTTP headers and full RTSP request messages
at INFO level, which includes Authorization headers containing
credentials (Base64-encoded for Basic auth, hash responses for Digest
auth). The WWW-Authenticate challenge header with realm and nonce
values is also logged at INFO level.

INFO-level logs are commonly collected by system logging daemons and
may be stored in world-readable log files, exposing credentials.

Downgrade all three logging calls to DEBUG level, which is only
enabled during explicit debugging sessions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:02:48 +02:00
Wim Taymans
4f9e59b87d security: fix missing null termination in Bluetooth broadcast code
Memory Safety: Medium

The broadcast_code field is a 16-byte array that can be filled with
exactly 16 bytes of data via memcpy without null termination when the
input string length equals BROADCAST_CODE_LEN. The field is then
logged with %s format, which reads past the buffer boundary into
adjacent struct fields, potentially disclosing sensitive data.

Fix by changing the boundary check from > to >= to ensure room for
the null terminator, and copy the terminator along with the data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:02:35 +02:00
Wim Taymans
ed2c0ad4ee spa: add spa_alloca that does overflow and limit checks
Make a function like alloca but with overflow checks and a max
allocation size.

Use this function where we can and also make sure that all alloca calls
are in some way limited.
2026-04-27 10:53:44 +02:00
Wim Taymans
a9f1ad414e security: fix integer truncation in peer_name alloca size
Memory Safety: Medium

The strlen() return value (size_t) is stored in an int before being
passed to alloca(). If a malicious client sets an extremely long
PW_KEY_NODE_NAME property, the addition could overflow the int,
resulting in a small or negative alloca size and a subsequent buffer
overflow in snprintf().

Change the type to size_t and add a bounds check to prevent
excessively large stack allocations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 16:09:08 +02:00
Wim Taymans
d60ae4a1df security: fix unchecked alloca in pulse protocol message handling
Memory Safety: High

The add_stream_group() function computes a buffer size from the sum of
multiple string lengths, including user-controlled dictionary values
(media role, app name, etc.), and passes it to alloca() without any
bounds check. A malicious client could send very long property strings
causing an integer overflow in the size computation (wrapping a
negative/small int) or an excessively large stack allocation, leading
to a stack overflow.

Add a bounds check to reject sizes that are negative or exceed 1024
bytes before calling alloca().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 16:08:45 +02:00
Wim Taymans
0f8d5c6e57 spa: add and use spa_overflow macros 2026-04-24 15:55:35 +02:00
Wim Taymans
84f8230a47 security: fix TOCTOU race and symlink following in pulse protocol socket
File and Resource Handling: Medium

The PulseAudio protocol server socket initialization uses stat() to
check for existing socket files before unlinking and rebinding. This has
the same two vulnerabilities recently fixed in module-protocol-native:

1. stat() follows symlinks, so an attacker who places a symlink at the
   socket path (e.g., in XDG_RUNTIME_DIR/pulse/) can trick the daemon
   into unlinking the symlink target.

2. The gap between stat() and unlink() creates a TOCTOU race window.

Fix by using lstat() instead of stat() and adding an explicit symlink
check that refuses to proceed if the path is a symbolic link, consistent
with the approach in module-protocol-native.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
a6155387da security: fix unchecked alloca in pulse-server property list handling
Memory Safety: Medium

Two alloca() calls in the PulseAudio protocol server were missed by the
previous alloca bounds-checking fix (commit 0d2877c0d):

1. fill_node_info_proplist() adds n_items counts from node properties
   and client properties without checking the total before alloca().
   A client with a very large number of properties can exhaust the stack.

2. fill_card_info() uses pi->n_props from port info for an alloca()
   without bounds checking. A card object with many port properties can
   similarly exhaust the stack.

Add MAX_ALLOCA_SIZE checks consistent with the existing pattern to
prevent stack overflow from large property counts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
95ef466b9b security: add O_NOFOLLOW to native protocol lock file creation
File and Resource Handling: Medium

The lock_socket() function opens the lock file without O_NOFOLLOW. If an
attacker places a symlink at the lock file path, open() follows it and
creates or truncates a file at the symlink target with the caller's
privileges. While the lock path is typically in a user-owned runtime
directory, adding O_NOFOLLOW is a low-cost defense-in-depth measure that
prevents symlink attacks in case the directory permissions are
misconfigured or the path is influenced by untrusted input.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
613b35eedf security: fix TOCTOU race and symlink following in native protocol socket
File and Resource Handling: Medium

The socket initialization code uses stat() followed by unlink() to clean
up stale socket files. This creates two issues:

1. stat() follows symlinks, so an attacker who places a symlink at the
   socket path can trick the daemon into unlinking an arbitrary file.

2. The gap between stat() and unlink() creates a TOCTOU race window
   where the file could be replaced between the check and the removal.

Fix by using lstat() to detect symlinks without following them, refusing
to proceed if the path is a symlink, and only unlinking files that are
actually sockets (S_ISSOCK). This narrows the race window and prevents
symlink-based file deletion attacks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
4c9ec363a3 security: fix inverted overflow check in RTP MIDI message size parsing
Memory Safety: High

In get_midi_size(), the overflow check for SysEx and meta-event message
sizes has the comparison operator inverted. The check
  (unsigned int)(INT_MAX - size - 1) > value
rejects small (safe) payload sizes and accepts large sizes that cause
signed integer overflow in the subsequent size += (int)value + 1.

This means all SysEx messages (0xF0, 0xF7) and system reset/meta events
(0xFF) with valid payloads are incorrectly rejected, while crafted
packets with very large variable-length values bypass the check. Although
the caller has a secondary bounds check that mitigates most exploitation,
the inverted check is both a functional bug (breaks SysEx over RTP) and
a defense-in-depth failure.

Fix by swapping the operands so that the check correctly rejects values
that would overflow: value > (unsigned int)(INT_MAX - size - 1).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
7a969654f6 security: fix out-of-bounds read from non-null-terminated netjack2 strings
Memory Safety: High

The nj2_dump_session_params() function logs char array fields (type,
name, driver_name, follower_name) from network-received
nj2_session_params structs using %s format. These fields are fixed-size
char arrays filled by recvfrom() and are not guaranteed to contain a null
terminator. A malicious peer can send a packet with no null bytes in
these fields, causing pw_log_info to read past the struct boundary,
potentially crashing the process or leaking adjacent heap memory.

Use %.*s format specifier with explicit maximum lengths in the dump
function to bound the string reads. Also force null-terminate the
string fields in nj2_session_params_ntoh() so that all downstream
consumers after byte-order conversion are safe.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
e01ca8919e security: fix integer underflow in AVB stream packet handling
Memory Safety: Critical

In handle_iec61883_packet(), the data_len field from an incoming network
packet is converted via ntohs() and then unconditionally has 8 subtracted
from it. If an attacker sends a malformed AVB packet with data_len < 8,
the subtraction wraps the uint32_t n_bytes to a very large value
(~4 billion). This corrupted size is then passed to
spa_ringbuffer_write_data(), which can overwrite the ring buffer and
adjacent heap memory with attacker-controlled network data.

Add a bounds check to verify data_len >= 8 before the subtraction,
returning early on malformed packets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
e3e1c4d214 security: fix integer overflow in Bluetooth codec codesize calculations
Memory Safety: High

Several Bluetooth audio codec implementations calculate codesize by
multiplying samples * channels * sizeof(sample_type) without overflow
checks. The parameters come from Bluetooth codec negotiation, which is
influenced by the remote peer. If the multiplication overflows, codesize
wraps to a small value, causing subsequent buffer size checks to pass
while the actual data processing operates on the full (larger) sample
count, leading to heap buffer overflows.

Affected codecs: LC3 (BAP), LC3plus (A2DP), Opus (A2DP), Opus-G (A2DP).

Add overflow checks before each codesize multiplication to ensure the
result fits in the target integer type, returning -EINVAL on overflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
00413a3263 security: fix stack exhaustion via unbounded alloca in pulse-server
Memory Safety: Medium

Several functions in the PulseAudio protocol implementation use alloca()
to allocate arrays of port_info, profile_info, or dict_item structs
based on counts derived from card parameters or client property lists.
These counts have no upper bounds, so a card object with a very large
number of parameters or a client sending many properties can cause
alloca() to exhaust the stack, resulting in a stack overflow crash.

Add a MAX_ALLOCA_SIZE (64KB) limit and check element counts before each
alloca() call. If the requested allocation exceeds the limit, the
function returns -ENOMEM instead of crashing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
62e1da2ea3 security: fix unchecked allocation returns in filter-graph descriptor loading
Memory Safety: High

In descriptor_load(), the initial calloc for the descriptor struct, the
strdup for the label, and four calloc calls for port arrays (input,
output, control, notify) all lacked NULL checks. If any allocation fails
under memory pressure, the code proceeds to dereference NULL pointers
when populating the port arrays, causing a crash.

Add NULL checks after all allocation calls, using the existing
descriptor_unref cleanup path which already handles freeing partially
initialized descriptors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
b2bdd65338 security: fix TOCTOU and symlink vulnerabilities in pipe-tunnel FIFO
File and Resource Handling: High

The pipe-tunnel module creates FIFOs and then adjusts their
permissions using chmod() on the path. Between mkfifo() and
chmod(), an attacker with write access to the directory (e.g.,
/tmp with the default hardcoded paths) can delete the FIFO and
replace it with a symlink to a target file. The chmod(0666) then
changes permissions on the symlink target, allowing the attacker
to make arbitrary files world-readable/writable.

Fix by:
1. Adding O_NOFOLLOW to the open() call so symlinks are rejected
   at open time rather than followed.
2. Moving the permission change from chmod() (path-based, follows
   symlinks) to fchmod() (fd-based, operates on the already-opened
   and validated file descriptor), and doing it after the fstat
   S_ISFIFO check confirms we opened an actual FIFO.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
56c5eaf317 security: fix integer overflow in netjack2 socket buffer size calculation
Memory Safety: Medium

In both module-netjack2-driver.c and module-netjack2-manager.c, the
socket buffer size is computed as:
  NETWORK_MAX_LATENCY * (mtu + period_size * sizeof(float) * n_ports)

This arithmetic is performed in int (signed 32-bit) but the
intermediate values can exceed INT_MAX with large but valid network
parameters. Signed integer overflow is undefined behavior in C,
and the resulting negative value passed to setsockopt would set an
incorrect socket buffer size.

Fix by widening the intermediate computation to size_t and clamping
the result to INT_MAX before storing in the int variable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
835ba5efd6 security: fix integer truncation in combine-stream delay calculation
Memory Safety: Medium

In update_delay(), the delay compensation size is computed as
delay * sizeof(float) where delay is int64_t but size is uint32_t.
When the delay value is very large, the multiplication result
truncates to a small uint32_t value. This causes an undersized
buffer allocation in resize_delay(), while compensate_samples
retains the original large value. Subsequent use of
compensate_samples could then write past the end of the buffer.

A negative delay (possible if delay_samples overflows) would also
produce a large unsigned size due to implicit conversion.

Fix by clamping the delay to be non-negative and within the maximum
delay buffer size before the multiplication, ensuring the size
cannot truncate or wrap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
1d68d7f2e9 security: fix integer overflow in loopback delay buffer allocation
Memory Safety: Medium

In recalculate_buffer(), the buffer size computation
(delay + (1u<<15)) * 4 can overflow uint32_t when delay is large.
Additionally, the subsequent multiplication buffer_size * channels
for the realloc size can also overflow uint32_t, wrapping to a
small value and causing an undersized allocation. Later writes to
the buffer using the logical buffer_size would then overflow the
heap.

The delay value derives from rate * target_delay where both values
come from stream negotiation properties.

Fix by adding an overflow check on the delay computation and
widening the allocation size to size_t to prevent the second
overflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
4305d7e82d security: fix integer overflows in netjack2 peer buffer allocations
Memory Safety: High

In netjack2_init(), several buffer sizes are computed by multiplying
network-provided session parameters (period_size, channel counts,
kbps) without overflow checks. A malicious network peer can send
crafted session parameters that cause these multiplications to
overflow, resulting in undersized buffer allocations. Subsequent
writes to these buffers then overflow the heap.

Specific issues fixed:
1. midi_size = period_size * sizeof(float) * max_midi_channels can
   overflow, causing calloc to allocate a small buffer.
2. encoded_size = max_encoded_size * max_audio_channels can overflow
   for both INT and OPUS encoders.
3. OPUS kbps * period_size * 1024 numerator can overflow uint32_t;
   widen to uint64_t for the intermediate calculation.
4. Division by zero if sample_rate is 0 in OPUS encoder path.
5. Missing NULL checks on calloc for empty and midi_data buffers.
6. Channel counts not capped to MAX_CHANNELS before use.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
18df76b654 security: fix integer overflow in pw_reallocarray fallback path
Memory Safety: High

When the system does not provide reallocarray(), pw_reallocarray()
falls back to realloc(ptr, nmemb * size). The multiplication
nmemb * size can silently overflow, causing a smaller-than-expected
allocation. Subsequent writes to the allocation then overflow the
heap buffer.

This function is used extensively throughout PipeWire for allocating
arrays from protocol data, making it a wide attack surface.

Fix by adding an explicit overflow check before the multiplication
in the fallback path, matching the behavior of the real
reallocarray().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
d4ec630b2f security: fix missing packet length validation in VBAN MIDI receive
Memory Safety: High

In vban_midi_receive(), the received buffer is cast to struct
vban_header and its n_frames field is accessed before validating
that the packet is large enough to contain the header. A truncated
packet shorter than VBAN_HEADER_SIZE would cause an out-of-bounds
read.

Fix by checking that len >= VBAN_HEADER_SIZE before accessing the
header, matching the fix applied to vban_audio_receive().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
3709cac938 security: fix missing packet length validation in VBAN audio receive
Memory Safety: High

In vban_audio_receive(), the received buffer is cast to struct
vban_header and its fields are accessed before validating that the
packet is large enough to contain the header. If a truncated packet
shorter than VBAN_HEADER_SIZE is received, this reads past the end
of the buffer.

Additionally, when len < hlen, the plen calculation (len - hlen)
produces a negative ssize_t value which, when used in the unsigned
division plen / stride, gets implicitly converted to a very large
value, potentially causing further out-of-bounds reads.

Fix by checking that len >= VBAN_HEADER_SIZE before accessing the
header.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
hackerman-kl
0ac8b1c5fa module-avb: fix GET_NAME to validate length before field reads and reply with fixed size 2026-04-24 11:50:23 +02:00
hackerman-kl
01dd7e607c module-avb: bound packet copy length in reply_status helper 2026-04-24 11:50:20 +02:00
hackerman-kl
f091b85b03 module-avb: enforce minimum AECP packet length and replace VLA on dispatch 2026-04-23 17:25:26 +02:00
hackerman-kl
6ca2f509e3 module-avb: bound descriptor size in READ_DESCRIPTOR reply to prevent stack overflow 2026-04-22 19:19:10 +02:00
hackerman-kl
a8832c74d0 module-avb: bound packet copy length in get-set-name handlers 2026-04-20 18:29:56 +02:00
hackerman-kl
e6b4de6442 milan-avb: guard against size_t underflow on short packets in unsol reply prepare 2026-04-19 18:58:01 +02:00
hackerman-kl
09f9100ce7 milan-avb: validate packet length before dereferencing SET_CONTROL value byte 2026-04-19 07:39:03 +02:00
hackerman-kl
0291895498 milan-avb: zero-pad oversized SET_CONTROL reply buffer to avoid stack info leak 2026-04-18 17:13:05 +02:00
hackerman-kl
b831fd857f milan-avb: bound packet copy length in get-set-control handlers 2026-04-16 19:50:33 +02:00
hackerman-kl
f06234fda8 milan-avb: bound packet copy length in clock-source handlers 2026-04-16 19:07:59 +02:00
hackerman-kl
d1deabe5ac milan-avb: fix descriptor field and endianness in GET_CLOCK_SOURCE lookup 2026-04-14 19:00:02 +02:00
hackerman-kl
8fbeb23bbf milan-avb: implement deregister unsolicited notifications to actually clear registration 2026-04-13 18:52:30 +02:00
Wim Taymans
cd00ea2462 security: clear sensitive auth data from stack buffers in RAOP
Information Disclosure: Medium

The MD5_hash() function formats password material into a 1024-byte
stack buffer for hashing but never clears it afterward. Similarly,
the Basic auth path in rtsp_add_raop_auth_header() formats
username:password into a stack buffer without clearing it.

These buffers remain on the stack after the functions return, and
could be exposed through memory disclosure vulnerabilities, core
dumps, or memory inspection.

Clear the buffers with explicit_bzero() immediately after they are
no longer needed, consistent with the existing practice of clearing
the password before freeing in impl_destroy().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 17:49:43 +02:00
Wim Taymans
2c78c1e1fb security: fix integer overflows in netjack2 float packet handling
Memory Safety: High

In netjack2_recv_float(), several values from untrusted network packet
headers are used in arithmetic without overflow protection:

1. active_ports from the network header had no upper bound check. A
   very large value causes `active_ports * sub_period_bytes` to
   overflow uint32_t, producing a small value that passes the length
   check, then the loop iterates out of bounds on the receive buffer.

2. The sub_cycle bounds check `sub_cycle * sub_period_size >
   quantum_limit` can overflow, allowing a large sub_cycle to pass
   the check and cause an out-of-bounds write when computing the
   destination offset.

Fix by capping active_ports to MAX_CHANNELS, casting to size_t for the
length check to prevent overflow, and rewriting the sub_cycle check as
a division to avoid overflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 17:48:15 +02:00
Wim Taymans
e277a91842 security: fix integer overflows in netjack2 MIDI packet handling
Memory Safety: High

In netjack2_recv_midi(), the offset calculation `max_size * sub_cycle`
uses sub_cycle from an untrusted network packet header. A large
sub_cycle value could cause integer overflow, producing a small offset
that passes the subsequent bounds check and leads to an out-of-bounds
write into the MIDI data buffer.

Similarly, the bounds check `offset + len < midi_size` could itself
overflow, and the `used` size calculation from network-controlled
event_count and write_pos fields could overflow to bypass the size
check.

Fix by adding an explicit overflow check before the multiplication,
rewriting the bounds check to use subtraction (which cannot overflow
after the prior check), and adding an underflow check on the `used`
calculation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 17:47:27 +02:00
Wim Taymans
8d352fe52e security: fix integer overflow in PulseAudio message buffer allocation
Memory Safety: High

In ensure_size(), the check `m->length + size <= m->allocated` could
overflow when both m->length and size are large uint32_t values,
wrapping around to a small number and incorrectly passing the bounds
check. This could allow writing past the end of the allocated buffer.

Rewrite the check as `size <= m->allocated - m->length` which cannot
overflow since we already verified m->length <= m->allocated. Also add
an explicit overflow check for the new allocation size calculation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 17:46:47 +02:00
Wim Taymans
05bcfa7a2a security: fix missing fdopen() NULL check in conf.c
Memory Safety: Medium

In pw_conf_save_state(), the return value of fdopen() was not checked
for NULL. If fdopen() fails, subsequent fprintf() and fclose() calls
would operate on a NULL FILE pointer, causing a crash. Additionally,
the file descriptor would be leaked since fclose() would not be called.

Added a NULL check after fdopen() that closes the raw fd and returns
an error on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 17:45:29 +02:00
Wim Taymans
6798f591bd security: clear RAOP password from memory before freeing
Information Disclosure: Medium

The RAOP authentication password was freed without first clearing the
memory contents. This leaves the plaintext password in freed heap
memory where it could be recovered by an attacker with access to
process memory (e.g. via /proc/pid/mem, core dumps, or a separate
memory safety vulnerability).

Use explicit_bzero() to securely clear the password before freeing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:59:20 +02:00
Wim Taymans
e75f72476b security: fix missing malloc NULL checks in pffft
Memory Safety: Medium

In new_setup_simd(), the return value of malloc() for the PFFFT_Setup
struct was not checked before dereferencing. Similarly,
pffft_aligned_malloc() for the data buffer was not checked. If either
allocation fails, the code dereferences NULL causing a crash.

Add NULL checks for both allocations, freeing previously allocated
memory on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:59:17 +02:00
Wim Taymans
2ccb8a7d88 security: fix integer overflow in DSF file buffer allocation
Memory Safety: High

When parsing a DSF audio file, blocksize and channels are read as
uint32_t from untrusted file data and multiplied together for the
buffer allocation. A malicious file could set these to values whose
product overflows, resulting in a small allocation followed by
out-of-bounds writes when the buffer is filled.

Add overflow checking before the multiplication and validate that
neither value is zero. Also use calloc(channels, blocksize) instead
of calloc(1, blocksize * channels) to let calloc perform its own
internal overflow check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:59:14 +02:00
Wim Taymans
440f24f35f security: fix missing strdup NULL checks in RAOP authentication
Memory Safety: High

In rtsp_do_options_auth(), the return values of strdup() for
auth_method, realm, and nonce were not checked for NULL. If strdup()
fails due to memory exhaustion, spa_streq() on auth_method will
dereference NULL, and the realm/nonce pointers will be used later in
MD5_hash() causing NULL pointer dereferences.

Add NULL checks after each strdup() call, returning -ENOMEM on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:59:10 +02:00
Wim Taymans
508407b350 security: fix missing malloc/realloc NULL checks in pw-dot
Memory Safety: High

In dot_data_init(), the return value of malloc() was not checked before
dereferencing, causing a NULL pointer dereference if allocation fails.

In dot_data_ensure_max_size(), the return value of realloc() was
assigned directly to dd->data without checking for NULL, which both
loses the original pointer (memory leak) and causes a NULL pointer
dereference on subsequent use.

Add NULL checks for both cases. For realloc, use a temporary variable
to preserve the original pointer on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:58:06 +02:00
Wim Taymans
b414d2af19 pw-top: use spa_strbuf to create status bar
There is nothing wrong with the use of strcat here but security tools
keep complaining about it and creating bad patches for it so fix it
with a strbuf.
2026-04-23 16:29:16 +02:00
Wim Taymans
135620ab64 security: fix missing malloc NULL checks in echo-cancel
Memory Safety: High

Three malloc calls for ring buffers (rec_buffer, play_buffer,
out_buffer) had no NULL checks. If any allocation fails, the
NULL pointers would be passed to memset and ringbuffer
operations in reset_buffers(), causing a NULL pointer
dereference crash.

Additionally, the ring size calculations used uint32_t
arithmetic which could overflow with large user-configurable
buffer.max_size values. Cast to size_t to perform the
multiplication in 64-bit, preventing intermediate overflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:25:19 +02:00
Wim Taymans
9cf4d05c9e security: fix unbounded strcpy for JACK port names
Memory Safety: Medium

strcpy was used to copy port names into fixed-size buffers
(REAL_JACK_PORT_NAME_SIZE+1) without explicit bounds checking.
Port names originate from JACK client API calls and PipeWire
port info, which are external inputs. Replaced with snprintf
using sizeof(destination) to guarantee the copy is always
bounded, preventing potential buffer overflows if source
strings exceed the expected maximum length.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:25:16 +02:00
Wim Taymans
329e0ddb02 security: fix unbounded sprintf in pw_conf_save_state
Memory Safety: Low

sprintf was used to format a temporary filename into an alloca'd
buffer. While the buffer was correctly sized (strlen + 5), using
snprintf with an explicit size makes the bound check enforceable
and prevents potential overflow if the sizing logic is modified
in the future.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:25:11 +02:00
Wim Taymans
46e732c28b security: fix unbounded sprintf in RAOP MD5 hash formatting
Memory Safety: Low

sprintf was used to format MD5 hex digest bytes into a fixed-size
buffer without explicit bounds. While the output is bounded by the
fixed MD5 digest length (16 bytes = 32 hex chars), using snprintf
with an explicit size of 3 (2 hex chars + null) ensures correctness
even if the surrounding code changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:25:01 +02:00
Wim Taymans
6353eb526d security: fix unbounded sprintf in check_flatpak
Memory Safety: Medium

sprintf was used to format a /proc path without bounds checking.
While pid_t values are practically bounded, using snprintf with
sizeof(root_path) ensures the buffer cannot overflow regardless
of the input value, following defense-in-depth principles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:24:46 +02:00
Wim Taymans
2707269118 doc: try to improve the docs 2026-04-23 13:59:41 +02:00
Wim Taymans
bb9d306399 audioconvert: also benchmark the inter versions 2026-04-23 09:38:01 +02:00
Wim Taymans
596047aaef resample: use some extra accumulators to improve pipelining 2026-04-23 09:32:31 +02:00
Wim Taymans
75e432a49a resample: use independent accumulators for SSE and SSSE3 2026-04-23 09:18:08 +02:00
Wim Taymans
37f9f7773c resample: implement inter for ssse3 2026-04-22 18:28:25 +02:00
Wim Taymans
dfeca5806f resample: don't use hadd, it is slow 2026-04-22 18:23:33 +02:00
Wim Taymans
a0518e28bb audioconvert: avoid some float/double/int conversions 2026-04-22 18:00:59 +02:00
Wim Taymans
9c9a5ac435 convolver: returned processed samples 2026-04-22 16:13:56 +02:00
Wim Taymans
495c1c9dd0 dsp: precalculate the scale 2026-04-22 16:13:56 +02:00
Wim Taymans
3c2552e671 dsp: add SSE and AVX2 mult and linear functions 2026-04-22 16:13:56 +02:00
Wim Taymans
3e7e61dcb7 convolver: small cleanups
Remove unused field.
We can also remove the ifft and reuse the fft.
2026-04-22 16:13:56 +02:00
Wim Taymans
aabcbf1261 dsp: move scaling out of complex multiply
do scaling as part of iFFT.
2026-04-22 16:13:56 +02:00
Wim Taymans
7fc020098c dsp: shuffle per implementation 2026-04-22 16:13:56 +02:00
Wim Taymans
46b8380490 dsp: store Real/Imag in blocks of 8
Shuffle FFT output into real/imag blocks so that they are easier to
handle in the complex multiply. Do the unshuffle again before doing the
inverse FFT.
2026-04-22 16:12:20 +02:00
Charles
43b19e7668 gst: fix crop height typo in pipewiresink do_send_buffer
The crop region height was incorrectly set to meta->width instead of
meta->height when copying GstVideoCropMeta into the SPA buffer.
2026-04-21 20:19:24 +01:00
Wang Yu
2953f48d9b vulkan: fix wrong descriptor image info index
When streams are skipped via continue in updateDescriptors(),
the loop index i and descriptorSetLen diverge. The image info
is written at descriptorSetLen but pImageInfo was referencing
index i, pointing to uninitialized memory and causing incorrect
Vulkan descriptor updates.

Fix by using descriptorSetLen consistently.

Signed-off-by: Wang Yu <wangyu@uniontech.com>
2026-04-21 15:13:03 +00:00
Wim Taymans
da19aa4eb1 filter-chain: update virtual surround with convolver2 2026-04-21 17:03:55 +02:00
Wim Taymans
c6ae30593c filter-graph: use convolver2 for sofa
We don't need 2 convolvers anymore, we can use the same convolver with
2 outputs with the left and right ir.

Add latency option to the sofa plugin. I believe the latency of the
SOFA filters is by default 0, so use that.
2026-04-21 16:52:49 +02:00
Wim Taymans
9cae4ce7e7 filter-chain: add convolver2
Add support for multiple convolver outputs. This makes things more
efficient because we only need to do the input FFT once to produce the N
outputs.

Add convolver2 that can have multiple outputs.
2026-04-21 16:24:38 +02:00
Wim Taymans
2b96f694f7 convolver: rename some fields 2026-04-20 14:00:15 +02:00
Wim Taymans
d8db536d36 convolver: remove some useless loops 2026-04-20 14:00:04 +02:00
Wim Taymans
777851a7ec convolver: support more than 2 partitions
Currently wired up to only support 2 but it can be changed.
2026-04-20 13:59:08 +02:00
Wim Taymans
b9e62aad8a convolver: handle partial blocks 2026-04-20 13:57:47 +02:00
zhiwei zuo
c5e1a3eae3 Apply 1 suggestion(s) to 1 file(s)
Co-authored-by: Barnabás Pőcze <pobrn@protonmail.com>
2026-04-20 10:06:31 +00:00
zuozhiwei
0a38fedeec conf: clamp pw_strv_insert_at invalid pos to [0, len]
Invalid pos was clamped to len+1, which could insert past the terminating NULL.
Clamp negative indices to 0 and values above len to len.
2026-04-20 10:06:31 +00:00
hackerman-kl
f0a33cddbd module-avb: es_builder: use the descriptor rather than a pointer to avoid overwriting it 2026-04-20 10:10:58 +02:00
zuozhiwei
2722d16303 audioadapter: remap port id for port_reuse_buffer on target
port_use_buffers and related port methods increment port_id when the
implicit output direction differs from the adapter's primary direction.
port_reuse_buffer only receives a port id but applies to output ports,
so apply the same offset before forwarding to this->target.

Also update videoadapter for the same mapping.
2026-04-20 07:55:54 +00:00
zuozhiwei
c3d16a39f5 audioconvert: fix tmp_datas[1] when scratch ports grow 2026-04-20 10:24:42 +08:00
Pauli Virtanen
cee1bdfb5a bluez5: more MT7925 quirks
The MT7925 chipset has several alternative USB ids recognized by kernel,
list them all.
2026-04-19 16:02:56 +00:00
hackerman-kl
e66a24dc5b modules-avb: legacy-avb: entity warnings 2026-04-19 08:15:55 +02:00
Pauli Virtanen
84e6845aa6 bluez5: add quirk for LC3-24kHz for HFP
MT7925 fails to setup a SCO connection that results to working LC3-24kHz
audio. Other controllers (Intel etc) appear to work OK.

Add quirk for disabling this codec, and disable it for this Mediatek
controller.
2026-04-17 22:10:32 +00:00
Pauli Virtanen
6e8e234e61 bluez5: fix disabling HFP codecs via bluez5.codecs
backend-native should not advertise disabled HFP codecs as available.
2026-04-17 19:35:43 +03:00
Wim Taymans
7df106bc25 filter-chain: deactivate when Format is unset
We need to deactivate the graph when the format was cleared on both the
input and output. This means we got suspended and we need to clear. We
can safely do this now because we take the right locks.
2026-04-17 13:05:28 +02:00
Wim Taymans
5f9811d085 convolver: clear the input buffer
The last part of the buffer needs to be 0 filled or else we get noise
from the FFT.
2026-04-17 12:47:32 +02:00
Wim Taymans
f210d93ea6 convolver: remove intermediate convolver
We can simply use the first one with more segments.
2026-04-17 10:39:36 +02:00
Wim Taymans
839e0a4aaf convolver: optimize loops
We can use a single loop with just one memcpy to implement the delay
buffer and feed the different FFTs.
2026-04-17 10:38:38 +02:00
Wim Taymans
37b648a3e0 convolver: convolver1 -> partition 2026-04-17 10:37:13 +02:00
zuozhiwei
d4b472d2e5 tools: fix realloc failure handling in midifile ensure_buffer
On realloc failure, the old mf->buffer pointer should be preserverd to avoid memory leaks.
2026-04-17 10:04:35 +08:00
Chiluka Rohith
61a9c78e1d pw-cat: Fix waveX format endian assign
SF_FORMAT_WAVEX is not supported to SF_ENDIAN_CPU. Due to that, unable
to record in .wav file (for > 2 channels).  Add case for SF_FORMAT_WAVEX
to get assign SF_ENDIAN_FILE.

Fixes #5233
2026-04-16 13:54:24 +02:00
Wim Taymans
c5d8113302 pipewire-jack: also ignore ports with other DSP type
The DSP port type needs to be something else than "other" for it
to become visible. This way we can also remove the IS_VISIBLE check
because we never add invisible ports to the object list.
2026-04-16 13:47:08 +02:00
Wim Taymans
9454c71a58 jack: don't emit "other" ports
When we see a non-dsp port, simply ignore it. This will make sure also
the links are ignored.

See #3512
2026-04-16 13:36:23 +02:00
Masum Reza
f76327e076 alsa: acp: don’t override user-selected port on availability changes
ACP was re-selecting the “best” port on every port availability event,
even when a port was already explicitly selected by the user. This
differs from PulseAudio’s behavior, where port switching decisions are
left to higher-level policy.

This caused issues on devices where Line Out (speakers) and Headphones
share the same analog interface: when headphones are plugged in, ACP
would immediately switch away from the user-selected Line Out, or end up
in a state where no sound is produced despite selecting speakers explicitly from
clients like pwvucontrol.

Fix this by only re-evaluating and switching ports when:
  - no active port is currently selected, or
  - the active port has become unavailable

This preserves manual user choices and prevents ACP from fighting client
port selections during route activation.

Additionally, adjust ALSA mixer paths to better separate Line Out and
Headphones behavior:
  - Disable Line Out controls in the headphones path
  - Add explicit Line Out and Auto-Mute Mode handling in the lineout path

Together, these changes align PipeWire’s behavior more closely with
PulseAudio and fix cases where selecting speakers while headphones are
plugged results in no audio output.

Signed-off-by: John Titor <masumrezarock100@gmail.com>
2026-04-16 10:45:01 +00:00
hackerman-kl
c551acf4d1 milan-avb: lock: make it lockable:
1. The period calls were added to handle timeouts.
2. Handle the case where lock must be unlocked after 60s if the
   controller owning the locked does not release it.
2026-04-16 12:42:23 +02:00
Wim Taymans
adad89dc0e add spa_memcpy to more places
Use spa_memcpy yo where we memcpy data so that when the debug option is
enabled we can see more.
2026-04-16 12:16:06 +02:00
Wim Taymans
54aba261d2 tools: add pw-audioconvert
Takes an input file, processes it with audioconvert and writes to an
output file. Can be used to test all audioconvert features such as
resample, channelmix, filter-graph, format conversion, dither, etc.

Boilerplate written by Claude.
2026-04-16 12:14:21 +02:00
zuozhiwei
b4457b871f core: use %u format specifier for uint32_t IDs
The object, node, client, factory, module, and link IDs are all uint32_t values but were being formatted with %d.
This would produce incorrect negative values if an ID ever exceeded INT_MAX
2026-04-16 08:54:15 +00:00
Wim Taymans
35cbd2e56a audioconvert: don't setup again in suspend
Remove the fallthrough in suspend, we don't want to do the setup that we
do in the paused mode, just reset the node and unset the started state.
2026-04-16 10:12:09 +02:00
Wim Taymans
e490c503fd pulse-server: update initial stream is_paused state
When the stream starts corked, we set the INACTIVE flag and we also need
to set the stream state as PAUSED or else uncork will not unpause
anything.
2026-04-15 18:25:28 +02:00
Wim Taymans
03fd89abea alsa-seq: add : between client and port name
The separator is important for applications to find the client name and
group ports.

Fixes #5229
2026-04-15 11:20:18 +02:00
zuozhiwei
3277f3acfb alsa: fix inverted port validity check in port_reuse_buffer
The CHECK_PORT condition in impl_node_port_reuse_buffer was inverted with a negation operator, causing the function to reject valid output ports and accept invalid ones.

Fixes the logic so that valid ports proceed to buffer recycling and invalid ports are properly rejected.
2026-04-15 09:17:49 +00:00
Wim Taymans
ae723a69f6 filter-graph: allow negative Gain in mixer
To allow for polarity changes.

Fixes #5228
2026-04-15 09:41:55 +02:00
Wim Taymans
823dcd8843 scheduler: make nodes move to IDLE when inactive
When a node is inactive but linked to a driver, the only reason it is
not being scheduled is because it is inactive.

We already set up the links and negotiate the format and buffers to
prepare going to RUNNING. This patch now also make the node go to IDLE,
which makes the adapter negotiate a forma and buffers with the internal
node.

This makes things more symetrical, when linking a node, it becomes IDLE,
when activating it becomes RUNNABLE, when inactive it goes back to IDLE.
The switch to RUNNING will also be faster when things are already set up
in the IDLE state.

The main advantage is that it allows us to implement the startup of
corked streams in pulseaudio better. Before this patch we had to set the
stream to active to make it go through the Format and buffer negotiation
and then quickly set it back to inactive, hopefully without skipping a
cycle. After this patch, the corked stream goes all the way to IDLE,
where it then waits to become active.

See #4991
2026-04-14 14:28:29 +02:00
Wim Taymans
474253719f alsa-seq: remove port from mix_list on destroy
When the port is destroyed we need to remove it from the mix_list or
else the process function will keep trying to use the invalid memory.

This is because the port logic does not want to call any functions on
the port (like clearing the IO or Format) after it emitted the destroy
signal and we need to clean up ourselves.

Fixes #5221
2026-04-14 11:09:29 +02:00
Wim Taymans
0cc3644e55 dlopen: support search path ending in /
When the search path is /usr/lib/, /usr/lib/foo.so fails to load because
there is no / after the search path. Fix this by requiring that either
the search path end with / or the following char is a /.
2026-04-13 10:26:33 +02:00
Julian Bouzas
14b74962d6 tools: connect to the manager socket by default
This gives the tools unrestricted access by default if access.socket is defined
in the configuration access module.
2026-04-13 07:39:45 +00:00
Wim Taymans
b12b7f785e pipewire: check init count before loading plugins
When pw_init() was not called and the init_count is 0, the plugin path
was not set and loading plugins will fail/segfault.

Avoid this and return en error early instead with a message that
pw_init() should be called first.

See !2784
2026-04-13 09:33:05 +02:00
zuozhiwei
11d28c661b Fix spelling errors in comments and log messages
- Fix durring → during in es-builder.c error message
- Fix capabilty → capability in impl-port.c debug log
- Fix supress → suppress in rate-control.h comment

Improve code readability
2026-04-13 07:20:11 +00:00
hackerman-kl
4b886d07f7 milan-avb: pw-avb-virtual: activate only when necessary 2026-04-13 07:15:39 +00:00
Марко М. Костић (Marko M. Kostić)
c3c74257fa
po: Update Serbian and add Serbian Latin translations 2026-04-11 15:14:16 +02:00
Марко М. Костић (Marko M. Kostić)
013bd12592
po: Alpha-sort LINGUAS 2026-04-11 15:14:06 +02:00
Wim Taymans
5075f27ea0 filter-graph: small convolver optimizations
Use FMA when we can, make sure FMA compilation is supported and the CPU
also supports it at runtime.

Avoid divisions by doing the modulo increment more explicitly.
2026-04-10 12:17:25 +02:00
Wim Taymans
c2f85ffc51 filter-chain: improve docs
Add the default values to the docs for some of the convolver config
variables.
2026-04-09 16:35:17 +02:00
Wim Taymans
b1a9bc966b tools: fix compiler error on older compilers
COLS could be very small and the statusbar array might overflow with
strcpy and strcat. Also initializing the variable array seems to cause
problems on older compilers.

Instead use a fixed array that is big enough to hold all possible
values we write into it.
2026-04-09 16:32:16 +02:00
Wim Taymans
1ed1349e7c test: fix some compiler warnings
be64toh returns a uint64_t so there is no need for casts in the test.
2026-04-09 13:04:35 +02:00
Martin Geier
42415eadd9 bluez5: iso-io: don't use streams without tx_latency enabled for fill level calculation
When there is a stream without tx_latency enabled, the fill_count ends
with MIN_FILL value. This causes one buffer of silence to be written to
every stream before the actual data in each iteration.
Consequently, more data is written than consumed in each iteration.
After several iterations, spa_bt_send fails, triggering a
group_latency_check failure in few next iterations and leading to
dropped data.

Skip streams without tx_latency enabled in fill level calculations
to prevent these audio glitches.
2026-04-09 08:00:37 +00:00
lumingzh
eeaddbb385 update Chinese translation 2026-04-09 07:45:56 +00:00
Christian F.K. Schaller
f5259828b6 tools: add pw-avb-virtual for virtual AVB graph nodes
Add a standalone tool that creates virtual AVB talker/listener endpoints
visible in the PipeWire graph (e.g. Helvum). Uses the loopback transport
so no AVB hardware or network access is needed.

The sink node consumes audio silently, the source produces silence.
Supports --milan flag for Milan v1.2 mode and --name for custom node
name prefix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
14310e66fe module-avb: extend transport abstraction to stream data path
Add stream_setup_socket and stream_send ops to avb_transport_ops so the
stream data plane can use the same pluggable transport backend as the
control plane. Move the raw AF_PACKET socket setup from stream.c into
avdecc.c as raw_stream_setup_socket(), and add a raw_stream_send()
wrapper around sendmsg().

Add a stream list (spa_list) to struct server so streams can be iterated
after creation, and add stream_activate_virtual() for lightweight
activation without MRP/MAAP network operations.

Implement loopback stream ops: eventfd-based dummy sockets and no-op
send that discards audio data. This enables virtual AVB nodes that work
without network hardware or privileges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
ffa855d76e test: add additional AVB protocol coverage tests (phases 7-8)
Add 26 new tests covering protocol areas not yet exercised:

Phase 7 (12 tests):
- MAAP conflict detection: probe/announce conflicts, defend logic
- ACMP disconnect: RX forwarding, TX without stream, pending timeout
- AECP GET_AVB_INFO: success path and wrong descriptor type
- MRP timers: leave-all and periodic timer verification
- MSRP talker-failed: attribute processing with failure info

Phase 8 (14 tests):
- MVRP: attribute lifecycle, VID packet encoding
- MMRP: attribute type verification (MAC + service requirement)
- ADP: duplicate entity, targeted discover, readvertise, departure
- Descriptor lookup: edge cases, data integrity after add
- AECP commands: GET_CONFIGURATION, GET_SAMPLING_RATE, GET_NAME

Total test count: 72 tests across 8 phases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
cec53759dd test: add AVTP audio data path tests
Add 10 Phase 6 tests for the AVTP audio data path:
- IEC61883 and AAF packet structure layout validation
- 802.1Q frame header construction
- PDU size calculations for various audio configurations
- Ringbuffer audio data round-trip integrity
- Ringbuffer wrap-around with multiple PDU-sized writes
- IEC61883 receive simulation (packet → ringbuffer)
- IEC61883 transmit PDU construction and field verification
- Ringbuffer overrun detection
- Sequence number and DBC counter wrapping

These tests validate the AVTP packet formats and audio data path
logic without requiring hardware, AF_PACKET sockets, or CLOCK_TAI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
fdfede8b96 test: add AECP/AEM entity model tests and document new bugs
Add 12 Phase 5 tests for the AECP/AEM entity model:
- READ_DESCRIPTOR for existing and non-existent descriptors
- AECP packet filtering (wrong EtherType, wrong subtype)
- Unsupported AECP message types (ADDRESS_ACCESS, etc.)
- Unimplemented AEM commands (REBOOT, etc.)
- ACQUIRE_ENTITY and LOCK_ENTITY for legacy mode
- Milan ENTITY_AVAILABLE, LOCK_ENTITY (lock/contention/unlock)
- Milan LOCK_ENTITY for non-entity descriptors
- Milan ACQUIRE_ENTITY returns NOT_SUPPORTED
- Milan READ_DESCRIPTOR

Also adds Milan test server helper with properly sized entity
descriptor for lock state, and AECP/AEM packet builder utility.

Updates avb-bugs.md with 3 new bugs found (bugs #6-#8).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
e661c72272 test: add ACMP integration tests and bug documentation
Add Phase 4 ACMP integration tests:
- NOT_SUPPORTED response for unimplemented commands
- CONNECT_TX_COMMAND with no streams (error response)
- Entity ID filtering (wrong GUID ignored)
- CONNECT_RX_COMMAND forwarding to talker
- Pending request timeout and retry
- Packet filtering (wrong EtherType/subtype)

Also add avb-bugs.md documenting all bugs found by the test suite.

Total: 24 tests, all passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
f5c5c9d7a3 test: add MRP state machine, MSRP, and packet parsing tests
Extend the AVB test suite with Phase 3 tests:

MRP state machine tests:
- Begin/join/TX cycle with NEW attribute
- Join then leave lifecycle
- RX_NEW registrar notification callback
- Registrar leave timer (LV -> MT after timeout)
- Multiple coexisting attributes

MSRP protocol tests:
- All four attribute types (talker, talker-failed, listener, domain)
- Domain and talker transmit via loopback capture
- Talker-failed notification (validates NULL deref fix)

MRP packet parsing tests:
- Single domain value parse
- Leave-all (LVA) flag detection
- Three-value base-6 event decoding

Total: 18 tests, all passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
ef4ff8cfd0 test: add AVB protocol test suite with loopback transport
Add a test suite for the AVB (Audio Video Bridging) protocol stack that
runs entirely in software, requiring no hardware, root privileges, or
running PipeWire daemon.

The loopback transport (avb-transport-loopback.h) replaces raw AF_PACKET
sockets with in-memory packet capture, using a synthetic MAC address and
eventfd for protocol handlers that need a valid fd.

Test utilities (test-avb-utils.h) provide helpers for creating test
servers, injecting packets, advancing time, and building ADP packets.

Tests cover:
- ADP entity available/departing/discover/timeout
- MRP attribute lifecycle (create, begin, join)
- Milan v1.2 mode server creation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
a73988d38d module-avb: add transport abstraction for pluggable network backends
Introduce struct avb_transport_ops vtable with setup/send_packet/
make_socket/destroy callbacks. The existing raw AF_PACKET socket code
becomes the default "raw" transport. avdecc_server_new() defaults to
avb_transport_raw if no transport is set, and avdecc_server_free()
delegates cleanup through the transport ops.

This enables alternative transports (e.g. loopback for testing) without
modifying protocol handler code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
d9821d09c7 module-avb: fix Milan lock entity error response and re-lock timeout
Fix two bugs in handle_cmd_lock_entity_milan_v12():

1. When server_find_descriptor() returns NULL, reply_status() was called
   with the AEM packet pointer instead of the full ethernet frame,
   corrupting the response ethernet header.

2. When refreshing an existing lock, the expire timeout was extended by
   raw seconds (60) instead of nanoseconds (60 * SPA_NSEC_PER_SEC),
   causing the lock to expire almost immediately after re-lock.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
4e62826e01 module-avb: fix legacy AECP handlers reading payload at wrong offset
handle_acquire_entity_avb_legacy() and handle_lock_entity_avb_legacy()
incorrectly treated the full ethernet frame pointer as the AEM packet
pointer, causing p->payload to read descriptor_type and descriptor_id
from the wrong offset. Fix by properly skipping the ethernet header,
matching the pattern used by all other AEM command handlers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
3f386ecd34 module-avb: fix ACMP error responses sent with wrong message type
In handle_connect_tx_command() and handle_disconnect_tx_command(),
AVB_PACKET_ACMP_SET_MESSAGE_TYPE() is called after the goto done
target. When find_stream() fails and jumps to done, the response
is sent with the original command message type (e.g., CONNECT_TX_COMMAND)
instead of the correct response type (CONNECT_TX_RESPONSE).

Move the SET_MESSAGE_TYPE call before find_stream() so error responses
are always sent with the correct response message type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
1d0c51f057 module-avb: fix MRP NEW messages never being transmitted
AVB_MRP_SEND_NEW was defined as 0, making it indistinguishable from
"no pending send" in the MSRP and MVRP event handlers which check
`if (!pending_send)`. This meant that when an attribute was first
declared (applicant state VN or AN), the NEW message was silently
dropped instead of being transmitted on the network.

Fix by shifting all AVB_MRP_SEND_* values to start at 1, so that 0
unambiguously means "no send pending". Update the MSRP and MVRP
encoders to subtract 1 when encoding to the IEEE 802.1Q wire format
(which uses 0-based event values).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
ef8f820d4a module-avb: fix potential NULL pointer dereference in MSRP/MVRP notify
The msrp_notify() and mvrp_notify() functions call dispatch table
notify callbacks without checking for NULL. In MSRP, the
TALKER_FAILED attribute type has a NULL notify callback, which would
crash if a talker-failed attribute received a registrar state change
notification (e.g. RX_NEW triggering NOTIFY_NEW).

Add NULL checks before calling the dispatch notify callbacks, matching
the defensive pattern used in the encode path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
bdaecfebb8 module-avb: fix heap corruption in server_destroy_descriptors
server_add_descriptor() allocates the descriptor and its data in a
single calloc (d->ptr = SPA_PTROFF(d, sizeof(struct descriptor))),
so d->ptr points inside the same allocation as d. Calling free(d->ptr)
frees an interior pointer, corrupting the heap. Only free(d) is needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Wim Taymans
20d648aaad filter-chain: don't corrupt the enumerated properties
When we add a Format property after we dereffed all the other params in
the builder, we might relocate the builder memory and invalidate all
previously dereffed params, causing corruption.

Instead, first add all the params to the builder and then deref the
params.

There is a special case when we have both a capture and playback
stream. The capture stream will receive all filter params and the
playback stream will just receive its Format param.

Fixes #5202
2026-04-08 17:49:41 +02:00
Wim Taymans
dc47f9ea45 filter-graph: return current control value correctly
The control values are only set in the port control_data after the
filter has been activated and the instances are created.

Property enumerations might happen before that and then we can either
return the current_value (when set in a control section or later with a
param property) or the default value.
2026-04-08 17:45:28 +02:00
Wim Taymans
0f00ad19cb tools: clean up the preset filter code 2026-04-08 15:01:11 +02:00
pallaswept
bf7f2a5d88 pw-top: filter by status 2026-04-08 12:47:08 +00:00
Wim Taymans
49073cf527 port: improve parsing of passive modes
Make a function to parse the passive mode and use that in ports and
nodes. Improve the node passive mode parsing a little.

Also make Duplex nodes follow-suspend.
2026-04-08 14:07:57 +02:00
Wim Taymans
446e36807f port: debug passive modes better 2026-04-08 13:21:24 +02:00
Wim Taymans
7012594926 dlopen: improve prefix check some more
If we pass a path /usr/libevil/mycode.so, it might have a prefix of
/usr/lib but we should still reject it. Do thi by checking that after
the prefix match, we start a new directory.
2026-04-08 12:00:04 +02:00
Wim Taymans
fbbc4271a3 doc: improve formatting 2026-04-08 11:35:52 +02:00
Wim Taymans
0e0c325194 fix some uninitialized variables warnings 2026-04-08 11:29:36 +02:00
Wim Taymans
abd8c8f666 test: fix pod size
It's at least 24 bytes, 8 for struct header, 8 for int header and 8 for
int data.
2026-04-08 11:28:04 +02:00
Wim Taymans
5474c3c3a5 doc: add running.dox 2026-04-08 10:30:46 +02:00
Wim Taymans
1a3df16e27 mem: handle overflow in pw_map_range_init()
Integer overflows can result in map_range_init() to return wrong offset
or size that can result in access to invalid or unmapped memory.

Check for the overflows and return an EOVERFLOW error.

Found by Claude Code.
2026-04-08 09:47:57 +02:00
Wim Taymans
d7be4353ad tools: avoid strcat in pw-cat
We might overflow the path buffer when we strcat the provided filename
into it, which might crash or cause unexpected behaviour.

Instead use spa_scnprintf which avoids overflow and properly truncates
and null-terminates the string.

Found by Claude Code.
2026-04-07 18:44:43 +02:00
Wim Taymans
337801717e test: add unit test for fds mismatch 2026-04-07 18:31:56 +02:00
Wim Taymans
c9ecbf9fab protocol-native: check msg fds against available fds
Check that the number of fds for the message does not exceed the number
of received fds with SCM_RIGHTS.

The check was simply doing an array bounds check. This could still lead
to out-of-sync fds or usage of uninitialized/invalid fds when the
message header claims more fds than there were passed with SCM_RIGHTS.

Found by Claude Code.
2026-04-07 18:20:26 +02:00
Wim Taymans
247918339e doc: add docs about the runnable calculations 2026-04-07 16:20:46 +02:00
Wim Taymans
50aacea749 plugins: allow some absolute paths
Allow abosulte paths as long as they start with one of the search paths.
2026-04-07 09:44:22 +02:00
Wim Taymans
57299da899 filter-graph: improve debug when loading fails
List the path name and the plugin search path when we can't load an
error.
2026-04-06 15:01:43 +02:00
Wim Taymans
785bf36b9b docs: remove support for absolute paths from docs 2026-04-06 14:47:21 +02:00
Wim Taymans
8fd7982087 only dlopen from the defined search paths
Don't accept absolute library paths and skip the ../ in paths to avoid
opening arbitrary libraries from unexpected places.
2026-04-06 14:20:56 +02:00
Pauli Virtanen
ad0bab69a1 spa: system: make spa_poll_event compatible with epoll_events
spa_poll_event should have exactly same layout as epoll_events to be
compatible across platforms. The structure is packed only on x86-64.

Fix packing and replace the data member with similar union as
epoll_data, to fix compatibility on 32-bit etc.
2026-04-06 10:24:32 +00:00
Wim Taymans
6b0248d68c Revert "impl-node: Don't suspend when links are busy"
This reverts commit bb0efd777f.

It is unclear what the problem was before this commit. If there are any
pending operations, the suspend should simply cancel them.

See #5207
2026-04-06 10:39:15 +02:00
Alexander Sarmanow
54a4515b09 bluez5: bap: add support for manual BIS config 2026-04-05 20:18:22 +00:00
ValdikSS
ee1b429441 bluez5: aac: Use VBR encoding with Mode 5 by default 2026-04-05 20:24:47 +03:00
ValdikSS
a35b6b0c4b bluez5: aac: use higher band limit for CBR mode
FDK-AAC encoder uses band pass filter, which is automatically
applied at all bitrates.
For CBR encoding mode, its values are as follows (for stereo):

* 0-12 kb/s:   5   kHz
* 12-20 kb/s:  6.4 kHz
* 20-28 kb/s:  9.6 kHz
* 40-56 kb/s:  13  kHz
* 56-72 kb/s:  16  kHz
* 72-576 kb/s: 17  kHz

VBR uses the following table (stereo):

* Mode 1: 13   kHz
* Mode 2: 13   kHz
* Mode 3: 15.7 kHz
* Mode 4: 16.5 kHz
* Mode 5: 19.3 kHz

17 kHz for CBR is a limiting value for high bitrate.

Assume >110 kbit/s as a "high bitrate" CBR and increase the
band pass cutout up to 19.3 kHz (as in mode 5 VBR).

Link: d8e6b1a3aa/libAACenc/src/bandwidth.cpp (L114-L160)
2026-04-05 20:24:47 +03:00
ValdikSS
49d5f4f236 bluez5: aac: use maximum possible peak bitrate according to MTU
Android 11 and newer, in both CBR and VBR modes,
* Sets bitrate (AACENC_BITRATE) to the max_bitrate value of A2DP
* Sets peak bitrate (AACENC_PEAK_BITRATE) according to the maximum data
   which could fit into single audio packet based on MTU

AACENC_BITRATE is used only in CBR mode. For VBR mode, the only limiting
factor is AACENC_PEAK_BITRATE.

Do the same in Pipewire.

Link: https://gitlab.freedesktop.org/pipewire/pipewire/-/work_items/1482#note_2949680
Link: https://cs.android.com/android/platform/superproject/+/android16-qpr2-release:packages/modules/Bluetooth/system/stack/a2dp/a2dp_aac_encoder.cc;drc=37d7b4549f7b8740df1a290f04c20c591a2d3391;l=269
2026-04-05 20:24:47 +03:00
Wim Taymans
67dd3549a7 system: use attribute packed for the spa_poll_event
This makes it the same size as epoll_event and we don't need to copy the
results over.

It however technically causes an ABI break, in case someone was using
the system interface directly.
2026-04-01 16:28:47 +02:00
Martin Geier
54c517b2d9 module-rtp: Add more logging for debugging timer related issues 2026-03-30 23:45:34 +02:00
Carlos Rafael Giani
0121bdc475 module-rtp: Lower missing timeout log line from warn to trace
A warning is not warranted in this case, and this log line can spam
the logs, so set it to trace.
2026-03-30 23:45:34 +02:00
Carlos Rafael Giani
5b86e3d418 module-rtp-source: Only enable IGMP recovery when using multicast
IGMP recovery makes no sense with unicast IP addresses.
2026-03-30 23:45:34 +02:00
Carlos Rafael Giani
3080bca85a module-rtp-source: Fix unicast by-address packet filtering
Using connect() on a UDP receiver creates a strict filter based on
the sender's _source_ port, not the sender's destination port. The
source port specifies at what sender port the packet exits the sender.
The destination port specifies at what receiver port the packet enters
the receiver. But, the RTP sink uses an ephemeral (= random) port as the
source port. Consequently, connect() at the receiver will cause a
comparison of that ephemeral port with the fixated one (which is actually
the number of the _destination_ port). This incorrect filtering causes
all packets to be dropped.

Use bind() to filter for the local destination port, and use recvmsg()
with manual IP comparison to filter for the sender's identity.
2026-03-30 23:45:34 +02:00
Carlos Rafael Giani
41b5bc662e network-utils: pw_net_are_addresses_equal() function 2026-03-30 23:45:33 +02:00
Carlos Rafael Giani
9ba92bd728 spa: Do not perform upper range check on 32-bit platforms 2026-03-30 23:31:36 +02:00
Wim Taymans
18c97222c4 channelmix: make up/downmix levels configurable
Add channelmix.center-level, channelmix.surround-level and
channelmix.lfe-level to control how center, surround and LFE is
up/downmixed.

Fixes #5198
2026-03-30 17:50:48 +02:00
Sriman Achanta
03f894bab0 alsa-udev: Add wireless device status monitoring 2026-03-30 07:43:27 +00:00
Barnabás Pőcze
7f4baba41c spa: do not use SPA_PROP_live
Remove support for changing `SPA_PROP_live` in node implementations
that supported it, and hard-code `SPA_PROP_live = true`. If a mode
of operation is desired where the data is processed as fast as possible,
it can be achieved by implementing non-driver operation and using the
freewheel driver in pipewire.
2026-03-27 18:03:24 +01:00
Barnabás Pőcze
00b4717c6e spa: remove async members
Some node implementations have an `async` member, but these members
are not written anywhere. So remove them.
2026-03-27 11:32:29 +01:00
Barnabás Pőcze
84e7b744a6 spa: libcamera: use std::span
libcamera is planning to move to C++20 and drop the custom `libcamera::Span`
type at some point in the future. Since pipewire already uses C++20, remove
all uses of it and instead use `std::span` so that things will compile
after the removal.
2026-03-27 09:57:56 +01:00
Barnabás Pőcze
29b221671f spa: remove timerspec members
These are only used when the timer is set, so convert them to local variables.
No functional changes intended.
2026-03-26 14:43:54 +01:00
Wim Taymans
7599079d61 jack: increase notify buffer and items
Make the notify buffer larger, it was 8K but we can make it 64K. Also
reorder the notify struct fields to make it smaller.

This should avoid "notify queue full" warnings. Ideally we should
dynamically size this queue and not lose any messages.
2026-03-26 11:59:28 +01:00
Wim Taymans
60062432b8 module-rtp: handle the send_packet/feedback as callbacks
They are emited from the streaming thread and therefore can be emitted
concurrently with the events on the main thread. This can cause crashes
when the hook list is iterated.

Instead, make those events into callbacks that are more efficient,
and threadsafe.
2026-03-26 09:34:45 +01:00
Wim Taymans
50fcf64058 tools: add -C | --monitor to pw-cat
It sets the stream.capture.sink property which makes a record stream
capture from a sink monitor instead.
2026-03-25 18:06:17 +01:00
Wim Taymans
9eeb2f1930 mixer: handle control.ump property
Add a control.ump port property. When true, the port wants UMP and the
mixer will convert to it. When false, the port supports both UMP and
Midi1 and no conversions will happen. When unset, the mixer will always
convert UMP to midi1.

Remove the CONTROL_types property from the filter. This causes problems
because this is the format negotiated with peers, which might not
support the types but can still be linked because the mixer will
convert.

The control.ump port property is supposed to be a temporary fix until we
can negotiate the mixer ports properly with the CONTROL_types.

Remove UMP handling from bluetooth midi, just use the raw Midi1 events
now that the mixer will give those and we are supposed to output our
unconverted format.

Fix midi events in-place in netjack because we can.

Update docs and pw-mididump to note that we are back to midi1 as the
default format.

With this, most of the midi<->UMP conversion should be gone again and we
should be able to avoid conversion problems in ALSA and PipeWire.

Fixes #5183
2026-03-25 11:59:43 +01:00
Wim Taymans
7fe191bc10 buffers: handle 0 result from Buffers param enumeration
Since abf37dbdde the param enumeration in
the client-node can return 0 when the parameter is supported but there
are no params uploaded.

When negotiating buffers we need to assume a 0 result as a NULL filter
as well or else we will error.
2026-03-25 11:59:43 +01:00
Wim Taymans
ea28343166 midi: don't convert Midi in nodes
Avoid doing conversions in the nodes between Midi formats, just assume
the imput is what we expect and output what we naturally produce.

For ALSA this means we produce and consume Midi1 or Midi2 depending on the
configurtation.

All of the other modules (ffado, RTP, netjack and VBAN) really only
produce and consume MIDI1.

Set the default MIDI format to MIDI1 in ALSA.

Whith this change, almost everything now produces and consumes MIDI1
again (previously the buffer format was forced to MIDI2).

The problem is that MIDI2 to and from MIDI1 conversion has problems in
some cases in PipeWire and ALSA and breaks compatibility with some
hardware.

The idea is to let elements produce their prefered format and that the
control mixer also negotiates and converts to the node prefered format.
There is then a mix of MIDI2 and MIDI1 on ports but with the control
port adapting, this should not be a problem.

There is one remaining problem to make this work, the port format is
taken from the node port and not the mixer port, which would then expose
the prefered format on the port and force negotiation to it with the
peer instead of in the mixer.

See #5183
2026-03-25 11:59:43 +01:00
Wim Taymans
67070762d0 mixer: only add input ports to mixer 2026-03-25 11:59:43 +01:00
George Kiagiadakis
0d1280a5b2 bluez5: parse the broadcast adapter value from the correct iterator 2026-03-24 16:55:00 +00:00
Barnabás Pőcze
22c1a16dce spa: audioconvert: benchmark-fmt-ops: initialize convert
Since c02cdcb5ce ("audioconvert: add avx2 optimized s32_to f32d")
`conv_s32_to_f32d_avx2()` reads `convert::cpu_flags`, which was
previously unitiailized, fix that by setting it to 0.
2026-03-24 17:50:51 +01:00
Barnabás Pőcze
39f4cbfc98 spa: audioconvert: test-fmt-ops: initialize convert
Since c02cdcb5ce ("audioconvert: add avx2 optimized s32_to f32d")
`conv_s32_to_f32d_avx2()` reads `convert::cpu_flags`, which was
previously unitiailized, fix that by setting it to 0.
2026-03-24 17:38:26 +01:00
lumingzh
bc87bc8588 update Chinese translation 2026-03-24 07:29:02 +00:00
Julian Bouzas
4f56442249 bluez: Discard latency and quality codecs worse than SBC for A2DP auto profiles.
Since SBC is mandatory in all devices that support A2DP, we dont need to inclide
them in the priority tables.

This change also increases the priority of OPUS_G codec as it has better latency
and quality than SBC.
2026-03-24 07:28:14 +00:00
Julian Bouzas
e1f53b7f39 bluez: Set initial profile to quality A2DP 2026-03-24 07:28:14 +00:00
Julian Bouzas
2e7aee3573 bluez: Increase priority of A2DP quality and latency profiles
This improves the UX as the highest A2DP profile will be selected by default.
2026-03-24 07:28:14 +00:00
Vitaliy Fadeev
01b9abc5ef Edit overview.dox
five flags
Node
2026-03-24 07:26:15 +00:00
Wim Taymans
d47b4974ce impl-node: add () around &&
To fix a compiler warning.
2026-03-23 11:55:05 +01:00
Martin Geier
52b23384e0 impl-node: correctly synchronize suspend state with remote node
Previously, if a remote node was set to running and immediately reverted
to suspended state, the remote node stayed in running state. This occurred
because suspend_node sent suspend command only when the locally cached
state was "idle" or "running."

Modified to send suspend to a node whenever its pending state is not
"suspended," ensuring the command is sent during state transitions.

Fixes #5026

Signed-off-by: Martin Geier <martin.geier@streamunlimited.com>
2026-03-23 11:41:09 +01:00
Wim Taymans
9ba0c3cfd3 audioconver: add avx channelmix copy function 2026-03-20 18:23:43 +01:00
Wim Taymans
c02cdcb5ce audioconvert: add avx2 optimized s32_to f32d
Add an alternative avx2 s32_to_f32d implementation that doesn't use the
gather function for when gather is slow.

Don't overwrite the orinal cpu_flags but store the selected flags in a
new variable. Use this to debug the selected function cpu flags.

Build libraries with defines from previous libraries so that we can
reuse functions from them.

We can then remove the SSE2 | SLOW_GATHER function selection from the
list. We will now select avx2 and it will then switch implementations
based on the CPU flags.
2026-03-20 17:57:59 +01:00
Pauli Virtanen
3dff64364f bluez5: media-source: don't crash if BAP streams doesn't have iso_io
Don't crash in update_target_latency() if a BAP stream doesn't have
iso_io for some reason.
2026-03-19 15:39:18 +00:00
Julian Bouzas
1dd8729d13 bluez: Add A2DP auto quality and latency profiles
These 2 new profiles will select the highest quality and lowest latency A2DP
codecs respectively, making it easier for users to know which codec is the best
based on their needs.

The priority for these 2 new profiles is 0, so the default behavior should not
change.
2026-03-19 15:38:43 +00:00
Wim Taymans
115525d000 fmt-ops: make function static 2026-03-19 16:36:11 +01:00
Wim Taymans
3b422e31a2 cpu: add SLOW_GATHER flag
Intel Skylake (level 0x16) is the first model with fast gather
opcodes. Mark lower versions with the SLOW_GATHER flag.

Prefer the SSE2 version of the format conversion without gather when
SLOW_GATHER is set. Makes the conversion much faster on my Ivy
Bridge.
2026-03-19 16:21:35 +01:00
Wim Taymans
5ade045654 pipewire-jack: only call timebase on 0 status
If the status passed to cycle_signal != 0 we should not call the
timebase callback. We already do this but we can use this fact to also
not call the timebase_callback when there was no buffer_frames
configured yet.

Do this by setting the fallback return value of process_callback to != 0.
When there is no buffer_frames yet, this will be used and we can then
avoid the buffer_frames check in cycle_signal.
2026-03-19 13:18:58 +01:00
Wim Taymans
244c3b597f pipewire-jack: call timebase_callback with valid buffer_frames
Check if we have a valid buffer_frames before we call the timebase
callback.

Also set the buffer_frames to the invalid value of -1 in jack_activate
instead of 0. -1 is the invalid value we check everywhere else.
2026-03-19 13:00:55 +01:00
Wim Taymans
41d8ce7fff audioconvert: optimize some fallback paths
We can use vector operations in some parts.

Also Make a macro for common store multiple operations.
2026-03-19 09:12:58 +01:00
Wim Taymans
b16a2e41e8 scheduler: remove sync group from runnable calculations
The sync group is only to group nodes when sync is enabled.
2026-03-18 13:19:04 +01:00
Wim Taymans
e4693ebc83 audioconvert: avoid OOB mix matrix read
Patch by David Nowotny

In make_matrix (channelmix-ops.c), the matrix-filling loop at the done:
label allows the index i (and j in the inner loop) to grow beyond MAX_CHANNELS
when dst_paired/src_paired has sparse bits set. In that case the continue
fires for most values of i < CHANNEL_BITS, so i advances much faster than ic,
and matrix[i][j] reads off the end of the stack-allocated array.

Add bounds guards to both loop conditions so i and j cannot exceed
MAX_CHANNELS.

Fixes #5176
2026-03-17 14:13:01 +01:00
Wim Taymans
6bf81ebe59 modules: align rtp_timestamps for sender
When the driver changes, the clock position can also change and there
would be a discont in the rtp_timestamp.

This is not usually a problem except in RAOP mode where the base rtp
timestamp is negotiated and anything that deviates too much is to be
discarded.

If we are not using direct_timestamp for the sender, make sure we always
keep the rtp_time aligned to avoid this problem.

See #5167
2026-03-17 14:05:09 +01:00
Wim Taymans
f4587ea434 modules: improve debug in RTP 2026-03-17 14:05:09 +01:00
Wim Taymans
8db1153519 modules: remove some unused fields 2026-03-17 14:05:09 +01:00
Arun Raghavan
36740e0100 spa: aec: Add some channel config validation
This is checked in AudioProcessing, but will fail at processing time
rather than in initialisation.
2026-03-17 12:06:25 +00:00
Arun Raghavan
edb074f438 spa: aec: Fix log topic name 2026-03-17 12:06:25 +00:00
Baurzhan Muftakhidinov
f846879399 po: update Kazakh translation 2026-03-16 19:07:54 +00:00
Robert Rosengren
e5809ee052 pipewiresrc: fix wait_negotiated from change_state
Going from PLAYING to PAUSED will have basesrc calling unlock and
setting flushing to false. Going back to PLAYING may then fail in
wait_negotiated, as unlock_stop will be called after change_state.

Fix by remove the flushing check, since already in that "state".
2026-03-16 17:08:44 +00:00
Rudi Heitbaum
1a37f445a2 spa/plugins/alsa/acp/compat.h: p is already const do not recast 2026-03-17 03:17:15 +11:00
Wim Taymans
653b8703bc filter-graph: fix up def/min/max values for lv2
lv2 filters can return NAN for the min/max and default values when not
specified. Handle them so that we don't end up clamping NAN numbers.

Fixes #5166
2026-03-16 12:19:57 +01:00
Wim Taymans
90fd6fbc65 filter-graph: only use min/max when defined in LADSPA
Fixes #5170
2026-03-16 09:59:14 +01:00
Wim Taymans
11d1f3653a doc: try to add 1.6 docs 2026-03-13 13:15:16 +01:00
Wim Taymans
b1b5653393 sendspin: negotiate the first raw format
Flac and Opus are not supported yet.
2026-03-13 12:03:11 +01:00
Nedko Arnaudov
6544996a33 pipewire-jack: emit foreign port registration callbacks on jack_activate
The jack_activate loop was only queuing NOTIFY_TYPE_PORTREGISTRATION
for the activating client's own ports. Ports belonging to other clients
— including all WirePlumber-managed ports and MIDI ports — were silently
skipped due to the o->port.port->client != c condition.

This caused two observable bugs for clients using libjackserver (e.g.
jackdbus):
- JackPortRegistrationCallback was not fired for any pre-existing
  foreign ports at activate time, leaving the patchbay empty unless
  the session manager happened to start after the client.
- JACK MIDI ports were never announced via callback, even though they
  are correctly returned by jack_get_ports().

The graph_order_callback fallback (used by jackdbus for initial port
enumeration) is also ineffective here because pipewire-jack only fires
it on connection events, not on activate.

Fix by iterating all non-removed foreign ports in the object list and
queuing registration callbacks for those whose node is active, matching
the semantics already implemented in node_info() for ports of nodes
that transition to running state after activate.

The change is libjackserver.so only. libjack.so behaviour is left
unmodifed, as carla is showing ports of each client twice.
2026-03-13 09:21:20 +01:00
Jonas Ådahl
3c1b8dcdcc spa/util/json-core: Fix condition checking for container end
Fixes the following warning:

    /usr/include/spa-0.2/spa/utils/json-core.h: In function ‘spa_json_is_container_end’:
    /usr/include/spa-0.2/spa/utils/json-core.h:404:41: warning: logical ‘or’ of equal expressions [-Wlogical-op]
      404 |         return len > 0 && (*val == '}'  || *val == '}');
          |                                         ^~
2026-03-12 22:21:41 +01:00
Barnabás Pőcze
3273ba6333 spa: support: logger: print thread id for each message
The thread id is very useful for debugging, add it to the every log
message so that it does not have to be inferred from the content itself.

This is already done in the systemd journal logger, so implement
it here as well.

Before:

  [I][00:13:11.120303] pw.context   | [      pipewire.c:  585 pw_init()] version 1.7.0

After:

  [I][365073][00:13:11.120303] pw.context   | [      pipewire.c:  585 pw_init()] version 1.7.0
2026-03-12 18:31:46 +01:00
Wim Taymans
95eac7b4e5 scheduler: add comment about the FOLLOW_SUSPEND special case 2026-03-12 18:12:42 +01:00
Barnabás Pőcze
9098996b87 spa: support: logger: use spa_strbuf
Using `spa_strbuf` simplifies a code a great deal by removing the manual
tracking of buffer sizes, and it also removes the need for the separate
strings for the timestamp, topic, etc.
2026-03-12 16:53:16 +00:00
Barnabás Pőcze
00148467ef spa: utils: string: add spa_strbuf_appendv()
Add a function that makes it possible to pass a `va_list` when
printing into a `spa_strbuf`.
2026-03-12 16:53:16 +00:00
Wim Taymans
e4e5f62d44 impl-node: accept more node.passive values
So that we can set separate values for in and out ports.

The node.passive=follow mode is a good idea for a filter.
2026-03-12 17:25:36 +01:00
Wim Taymans
16bde0c61d filter-graph: set some more def/min/max for control input
So that the values don't get clamped to 0.0
2026-03-12 16:01:52 +01:00
Jonas Holmberg
1344bec7bb filter-graph: set min, max and def values for ramp
Set min, max and def values for the control ports of ramp plugin to
avoid clamping all control values to 0.
2026-03-12 15:34:35 +01:00
Wim Taymans
f4558472df Revert "pipewire-jack: emit foreign port registration callbacks on jack_activate"
This reverts commit b0065bfe9a.

This causes double ports in carla.
2026-03-12 15:00:59 +01:00
Wim Taymans
3e209f6d20 modules: try to improve code readability some more 2026-03-12 14:57:23 +01:00
Wim Taymans
41520f1022 modules: remove a warning 2026-03-12 14:44:34 +01:00
Wim Taymans
ca91b368c1 impl-port: make a follow-suspend port mode
It's like the follow mode but when you link 2 follow-suspend ports
together, they will activate eachother.

This is to make Source -> Sink links work.
2026-03-12 14:40:04 +01:00
Wim Taymans
45e3af5cdc impl-port: make passive mode as an enum
Hopefully easier to understand.
2026-03-12 13:46:49 +01:00
Wim Taymans
ad195b289a modules: try to document the passive ports better 2026-03-12 12:13:43 +01:00
Arun Raghavan
f11ab0da3e spa: loop: Mark cancellation fields as volatile
Cancellation handlers use setjmp/longjmp, for which the C99
specification has the following note:

> 17.3.2.1 (3)
> All accessible objects have values, and all other components of the
> abstract machine) have state, as of the time the longjmp function was
> called, except that the values of objects of automatic storage
> duration that are local to the function containing the invocation of
> the corresponding setjmp macro that do not have volatile-qualified
> type and have been changed between the setjmp invocation and longjmp
> call are indeterminate.

While everything works fine with GCC, with Clang we see that the
cancellation handler doesn't seem to have an effect (loop-test fails
when it notices that its spa_source's priv and mask have not been
cleaned up).

The underlying cause is that the compiler can assume data.ep_count is
only used in loop_iterate_cancel(), and so can be cached in a register.
When we access that field in the cancellation handler, it was never
actually written to the memory on the stack, so the read in
cancellation_handler() does not see the current value.

We fix this by marking all fields on the stack that we expect to be
modified in loop_iterate_cancel() as volatile, forcing the memory to be
updated and correctly available to the cancellation handler.
2026-03-12 09:24:53 +00:00
Siva Mahadevan
0393fd8a72 spa/plugins: include <stdlib.h> for alloca() on non-linux 2026-03-12 09:21:40 +00:00
Siva Mahadevan
357f27658e spa/utils: remove FreeBSD workaround for bswap*
Both <endian.h> and <byteswap.h> have the things we need.
2026-03-12 09:20:50 +00:00
Siva Mahadevan
a671625637 spa/plugins: revert "Disable alsa plugin on !Linux platforms."
This reverts commit 01096bf669.

The linux-specific headers are gone, it builds fine now with alsa enabled.
2026-03-12 09:20:05 +00:00
Siva Mahadevan
9495e2b8a9 pipewire/thread.c: only handle reset_on_fork if SCHED_RESET_ON_FORK is defined
This fixes a missing definition error in thread.c:

../src/pipewire/thread.c:129:30: error: use of undeclared identifier 'SCHED_RESET_ON_FORK'
  129 |         SPA_FLAG_UPDATE(new_policy, SCHED_RESET_ON_FORK, reset_on_fork);
2026-03-12 09:18:41 +00:00
Nedko Arnaudov
7c5b5d12ed pipewire-jack: fix jack_port_type_id() to return jack1/jack2 compatible values
pipewire-jack defines TYPE_ID_VIDEO=1 between audio and MIDI, shifting
TYPE_ID_MIDI to 2. This caused jack_port_type_id() to return 2 for MIDI
ports, breaking compatibility with jack1/jack2 which return 1.

The jack_port_type_id() return value is part of the public JACK API and
consumers such as jackdbus rely on the conventional values established
by jack1/jack2: 0 for audio, 1 for MIDI.

Map internal TYPE_ID_* values to their jack1/jack2 compatible equivalents
before returning. All MIDI variants (MIDI, OSC, UMP) map to 1. Video has
no jack1/jack2 equivalent so maps to 3, beyond the conventional range.
2026-03-12 09:55:15 +01:00
Nedko Arnaudov
b0065bfe9a pipewire-jack: emit foreign port registration callbacks on jack_activate
The jack_activate loop was only queuing NOTIFY_TYPE_PORTREGISTRATION
for the activating client's own ports. Ports belonging to other clients
— including all WirePlumber-managed ports and MIDI ports — were silently
skipped due to the o->port.port->client != c condition.

This caused two observable bugs for clients using libjackserver (e.g.
jackdbus):
- JackPortRegistrationCallback was not fired for any pre-existing
  foreign ports at activate time, leaving the patchbay empty unless
  the session manager happened to start after the client.
- JACK MIDI ports were never announced via callback, even though they
  are correctly returned by jack_get_ports().

The graph_order_callback fallback (used by jackdbus for initial port
enumeration) is also ineffective here because pipewire-jack only fires
it on connection events, not on activate.

Fix by iterating all non-removed foreign ports in the object list and
queuing registration callbacks for those whose node is active, matching
the semantics already implemented in node_info() for ports of nodes
that transition to running state after activate.
2026-03-12 09:55:07 +01:00
Siva Mahadevan
e447b46d36 spa/tests: remove unused #include <linux/limits.h> 2026-03-11 21:50:21 +00:00
Siva Mahadevan
283052c15a examples: fix includes for makedev() macro
On non-linux, the makedev() macro comes from <sys/types.h>.
2026-03-11 21:49:48 +00:00
Siva Mahadevan
d17e68c43f network-utils: add missing #include <netinet/in.h>
As stated in https://pubs.opengroup.org/onlinepubs/9799919799//basedefs/netinet_in.h.html,

> The <netinet/in.h> header shall define the sockaddr_in structure [...]

This fixes the following build error:

In file included from ../src/modules/module-protocol-native.c:44:
../src/modules/network-utils.h:96:35: error: incomplete definition of type 'struct sockaddr_in'
   96 |                 if (inet_ntop(sa->ss_family, &in->sin_addr, ip, len) == NULL)
      |                                               ~~^
2026-03-11 17:20:12 -04:00
Siva Mahadevan
cd84d007cd spa: replace ECHRNG with EINVAL
ECHRNG is linux-specific and does not exist on all OSes.
On the other hand, EINVAL is specified in POSIX[0] (and is thus cross-platform)
and is commonly used to signify an index out of bounds error.

https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/errno.h.html
2026-03-11 20:40:24 +00:00
Siva Mahadevan
469929d4f6 pipewire: guard linux-specific RLIMIT_* inside #ifdef __linux__ 2026-03-11 14:20:48 -04:00
Wim Taymans
9c7aa4d423 impl-port: don't send NULL tag to clear
NULL tags are never handled and so tags just keeps configured on the
ports. It's also hard to know the direction from the NULL data.

Instead, send an empty Tag with the direction to clear everything.
2026-03-11 18:18:49 +01:00
Wim Taymans
2f65cf7124 modules: return the error of getsockopt instead of -EPIPE 2026-03-11 18:18:49 +01:00
Kisaragi Hiu
6e332a5d32 po: update Chinese (Taiwan) translation 2026-03-11 22:05:24 +09:00
Wim Taymans
00f1d6c3d8 modules: improve debug in profiler
Also log the node that we are inspecting.
2026-03-11 12:42:06 +01:00
Wim Taymans
f45e619559 filter-graph: emit control change after loading
Always do a control change to the instances when they are created. This
is to make sure the internal state is synced to the control values.

The sofa filter and biquads need this to correctly configure themselves
after a suspend.

Fixes #5152
2026-03-11 12:39:46 +01:00
Wim Taymans
5cc63c1b34 audiomixer: only add the input port to mix_list
Adding the output port is not a problem because there is never a buffer
to consume and mix but it wastes cycles.
2026-03-11 12:36:39 +01:00
Wim Taymans
5f4b422ab1 loop: improve cancellation handling
Now that the loop_leave function will assert when the unlock fails we
need to be extra careful with the cancellable loop. If it cancels inside
the poll or one of the before/after callbacks we need to make sure that
we lock the loop correctly again or we will create an assert later on.

Do this by pushing the cleanup earlier and then record all the things we
managed to do before we get canceled. If we ever get canceled and the
lock was unlocked but not locked again, fix this up.

Fix fixes issues when using the JACK API causing assertions when the
data loop is stopped/cancelled.
2026-03-10 16:46:29 +01:00
Wim Taymans
7ecd51dc80 client-node: avoid using invalid fd or mem in clear_data
Don't close an -1 fd in clear_data.

If we let the client allocate buffer, set our fd and data to invalid
values. If the client decides to renegotiate before we get the buffer
data we might otherwise try to clear the mem_id (default 0) or
close the fd (also default 0).

Fixes #5162
2026-03-10 14:25:07 +01:00
Wim Taymans
6a3ac7f7b0 examples: support REQUEST like video-src 2026-03-10 12:30:03 +01:00
Wim Taymans
8f22785cf0 json-core: use table to classify chars for number check 2026-03-10 12:30:03 +01:00
Barnabás Pőcze
5c67ab2a7a pipewire: mem: log if close() fails
If a file descriptor is expected to be closed by the associated `pw_memblock`,
and closing fails, then that usually signals a more serious issue in the memory
accounting. So add a log message when that happens.
2026-03-10 12:27:44 +01:00
Barnabás Pőcze
d33066cdd7 pipewire: module-client-node: use pw_log_*()
Use the `pw_log_*()` macros instead of the `spa_log_*()` ones since
this is a pipewire module, and otherwise not the expected log topic
would be used.
2026-03-10 12:09:36 +01:00
Barnabás Pőcze
810617997b spa: libcamera: source: SPA_PARAM_Props is write-only
There are no readable `SPA_PARAM_Props` on the node, so mark it write-only.
2026-03-09 21:19:38 +00:00
Barnabás Pőcze
a661f14d2c spa: support: loop: check enter_count before iterating
Calling "iterate()" on a loop that has not been entered by the
calling thread is invalid. So try to diagnose misbehaving applications
on a "best effort" basis by checking `impl::enter_count`.

This is not a foolproof check, and can also technically cause data
races while reading the variable.

See #5148
2026-03-09 22:08:23 +01:00
Barnabás Pőcze
dc1738ce57 spa: support: loop: more mutex lock checks
Check the return values of `pthread_mutex_{,un}lock()` in more places
to diagnose misbehaving applications.

See #5148
2026-03-09 22:08:22 +01:00
Barnabás Pőcze
abf37dbdde pipewire: module-client-node: use pw_param_info_find() to check validity
Previously when parameters were enumarated, it was checked if at least one
param was known for `id`. If not, `-ENOENT` was returned to signal that
this param id is not supported.

This is not necessarily true, since a param id might be supported, but it
might have zero params at the moment, in which case an unexpected error
would be returned.

Fix that by using `pw_param_info_find()` with the underlying impl object
to check if the param id is actually supported.
2026-03-09 21:34:19 +01:00
Barnabás Pőcze
5e37d43881 pipewire: impl-{node,port}: do not cache failed param enumerations
If the param enumeration fails, do not set `spa_param_info::user` to 1
indicating that the result is cached. Doing so can lead to the first
(uncached) call failing, while the rest will succeed (with 0 params).
2026-03-09 20:04:22 +00:00
Pauli Virtanen
22a5fad902 bluez5: cleanup get_codec_profile()
Check codec kinds for each direction properly when mapping to profiles
corresponding to it. Being sloppy here masked another bug, so best fix
it.
2026-03-09 21:35:22 +02:00
Pauli Virtanen
75c3d3ecf8 bluez5: fix spa_bt_device_supports_media_codec() for HFP codecs
HFP codecs don't have a direction dependent "target" profile, and this
function was returning false if A2DP is disabled.

Don't check target profile for HFP, leave checks to backend.

Fixes HFP-only configurations, which were missing profiles.
2026-03-09 21:33:28 +02:00
Wim Taymans
2548fcad25 spa: update lib.c 2026-03-09 18:33:32 +01:00
Wim Taymans
f6939a1cf0 json: Use state machine and fix 1E10 parsing
Use a state machine to check for valid numbers and fix the 1E10
parsing case.

With help from Claude.
2026-03-09 18:17:04 +01:00
Jonas Ådahl
c745582ef5 pipewire/capabilities: Remove left-over macro defiition
This was used for a previous iteration, that used tags, and was replaced
with PW_CAPABILITY_DEVICE_ID_NEGOTIATION.
2026-03-09 15:54:08 +00:00
Jonas Ådahl
f7bb4c95ed pipewire/capabilities: Update device ID negotation according to API docs
It was was documenting a previous iteration of the protocol which used a
boolean and a base64 encoded list.
2026-03-09 15:54:08 +00:00
Jonas Ådahl
99079dd955 docs/dma-buf: Document the correct device ID negotation key
The correct key is PW_CAPABILITY_DEVICE_ID_NEGOTIATION which contains a
number.
2026-03-09 15:54:08 +00:00
Pauli Virtanen
d42646e91f bluez5: sbc: clean up codec_enum_config
Non-spec compliant devices may set multiple bits in code config, which
we currently reject in validate_config().

enum_config() does work to deal with multiple bits set, but this is
never used, so write the code in a simpler way to return a single
configuration.
2026-03-09 15:53:35 +00:00
Pauli Virtanen
67b4732c26 bluez5: aac: for multiple bits in aot, normalize to mandatory
Non-spec compliant devices may set multiple bits in AAC AOT, which is
invalid.

In this case, we should normalize to MPEG-2 AAC LC which is the
mandatory value in spec, not to MPEG-4 AAC LC. In select_config() we
also prefer MPEG-2 over MPEG-4.
2026-03-09 15:53:35 +00:00
Pauli Virtanen
5f8ece7017 bluez5: aac: coerce non-spec compliant freq/channels to single choice
Some non-spec compliant devices (Sony XB100) set multiple bits
in all AAC field, including the frequency & channels.

Although they set multiple bits, these devices appear to intend that the
sender picks some specific format and uses it, and don't work correctly
with the others.

validate_config() already picks one configuration, so use the result in
enum_config(), instead of allowing also other settings.

Assume devices generally want preferably 44.1 kHz stereo.

Note we cannot reject the configuration, as BlueZ does not necessarily
retry, leaving the device connected but with no audio.
2026-03-09 15:53:35 +00:00
Pauli Virtanen
f9e2b1d8b9 bluez5: backend-native: don't crash without dbus session bus
When there is no DBus session bus, creation of the telephony backend
fails, and we later crash on null ptr deref.

In this case, avoid crash trying to create telephony_ag or iterate its
call list.
2026-03-09 15:52:05 +00:00
Wim Taymans
ddf63e0863 json: relax float parsing some more
We already support more float variants than standard JSON in the relaxed
format, adding extra restrictions does not actually help much. If you
need to know if this is a value JSON number, there is now a function to
check that instead.
2026-03-09 16:48:05 +01:00
Wim Taymans
231a41a22f impl-link: fix shared mem test
We can only use non-shared memory when both nodes live in the same
process _and_ we can be sure the output port is never going to be linked
to a remote node because it is exclusive.

This fixes the case where a null-sink is loaded inside the process space
of the server and linked to the ALSA sink. This would create a link
without shared mem and then as soon as something else (out of process)
wants to link to the null-sink output, it would get a -22 EINVAL
negotiation error because the memory can't be shared.

Fixes #5159
2026-03-09 16:18:58 +01:00
Wim Taymans
6f73e42d05 v4l2: use 0x as the prefix for hex values
fixes #5161
2026-03-09 13:50:38 +01:00
Wim Taymans
3a2d16a3bc json-builder: do better json number check
If we are going to produce valid JSON we need to do a better JSON number
check because our own float and int parser can accept more variants
and will let through numbers that are not valid JSON.

See #5161
2026-03-09 13:33:20 +01:00
Wim Taymans
48c22e2aa7 pw-dump: place key before None choice values
See #5161
2026-03-09 13:19:43 +01:00
qaqland
d1c372f5ee alsa-udev: support alsa.ignore-dB
Some sound cards are only adapted for Android/macOS and other
systems, without considering Linux. The hardware-reported dB
volume is incorrect (while the percentage volume is normal).
Add support for the ignore-dB option to simplify compatibility.

For example, the 3206:0798 HP SIMGOT GEW1 Sound Card reports:

numid=4,iface=MIXER,name='PCM Playback Volume'
  ; type=INTEGER,access=rw---R--,values=2,min=0,max=100,step=0
  : values=100,100
  | dBminmax-min=0.00dB,max=0.39dB

This dB value does not match actual audio perception, and the
vendor attributed this issue to non-target system compatibility.
2026-03-07 13:53:38 +08:00
Wim Taymans
70b7b42f5d pw-cat: fix encoded format playback
We keep a mapping between the sndfile formats and the format we would
like to decode them to for encoded formats. Make sure we don't mix up
the sample widths between them.

Make sure we don't send encoded formats as raw.

Debug the uncompressed format name correctly.

Fixes #5155
2026-03-06 17:03:20 +01:00
Wim Taymans
a179e8c695 sendspin: implement parsing of client state 2026-03-06 11:24:21 +01:00
Wim Taymans
784ad934a4 impl-node: remove the can_suspend boolean
Sink and Sources can now be implemented as the follow passive mode,
they won't activate anything themselves but follow the state of the
peer.
2026-03-06 10:42:00 +01:00
Wim Taymans
118d8574c8 impl-port: support mode port.passive values
Add a port.passive = follow mode and the node.passive equivalents
out-follow, in-follow, follow.

This makes it possible to control how a port influences the state of
the peer and how the peer influences the state of the node independently.

In passive mode, the port will not make the peer runnable and will also
not become runnable when the peer activates.

In the follow mode, the port will not make the peer runnable but it will
become runnable when the peer is active.

This makes it possible to do new things like (f for follow):

  Source -> (f)loopback1-in|loopback1-out(f) -> Sink

It will not make the source and sink run but when one of them start, all
will become runnable.

Or you can now better do the leak node hack that was previously used:

  Source -> (f)pw-record

That will only start running when the source is activated by something
else.

With port.passive = true|false|follow there is a potential 4th case
which would activate the peer but not be activated by the peer, which is
not something that makes sense.
2026-03-06 09:35:17 +01:00
Wim Taymans
437a8d32f2 impl-node: remove the leaf node check
This was a hack around some scheduler issues that we should be able to
handle better now.

One of the cases is:

  Source -> (p)pw-record

Which would never work otherwise because pw-record does not activate the
Source and when the Source is activated in any other way, pw-record
would not follow.

This will still fail with the current scheduler, but the leaf check is
not so great because it doesn't work in this case:

  Source -> (p)loopback-in|loopback-out(p) -> Sink

What is probably required is a passive mode that doesn't activate but
does follows the peer.
2026-03-05 15:03:30 +01:00
Wim Taymans
106b4a37d4 node: remove node.link-group from drivers
Sink/Source pairs should not have the same link-group otherwise the
session manager will not be able to autoconnect them with a loopback or
some other internally linked stream.
2026-03-05 14:32:41 +01:00
Wim Taymans
e6a5951a47 gst: fix compilation on older GStreamer
These functions are since 1.28, which is a little too new.
2026-03-04 17:55:53 +01:00
Robert Mader
efd1526423 pipewiresrc: Use clock time difference to update last_buffer time
Setting the current clock time when resending buffers is often wrong.
Especially for pseudo-live sources - the default mode - it discards
the original buffer time, which again is used by the base-class to
adjust the timestamps further, ultimately resulting in very wrong
timestamps.

Instead, try to calculate the delta between when we originally got the
buffer and now.
2026-03-03 22:52:52 +01:00
Robert Mader
49300d8ee0 pipewiresrc: Take a copy instead of a reference for last_buffer
Buffer timestamps get adjusted by the base class, GstBaseSrc, even if we
take an additional ref. Arguably the base class should check if buffers
are writable (gst_buffer_make_writable()), which would trigger a buffer
copy. That is currently not the case, though, thus do so on our side.

Notes:
1. Usually a buffer copy doesn't copy the underlying memory, i.e.
copying is cheap.
2. The copy holds a reference to the copied buffer, preventing the
buffer from getting recycled as before.
2026-03-03 22:51:04 +01:00
Wim Taymans
a3853c2c3d scheduler: activate links also in make_runnable
Also make sure we unset the preparing flag when the port state
changes.
2026-03-03 11:56:35 +01:00
Elliot Chen
95e89f786a pipewiresrc: update per-plane stride and offset according to chunk info 2026-03-03 09:16:51 +00:00
qaqland
182f52603c meson.build: bump sndfile version to 1.1.0
This commit follows 35817c0d85

Bump required sndfile version to 1.1.0 for SF_FORMAT_MPEG_LAYER
and other formats added in commit 35817c0d8c.
2026-03-03 09:14:25 +00:00
Wim Taymans
ea21281f38 scheduler: skip checking runnable from suspendable nodes
We used to skip the runnable state from driver nodes because we assume
that they will be activated from other nodes. We however need to make
this more general to all suspendable nodes.

This makes pw-play -> loopback1-sink loopback1-out -> loopback2-sink
loopback-out -> sink also work correctly because the loopback2-sink does
not activate loopback1-out then.
2026-03-02 17:22:05 +01:00
Wim Taymans
797cdbc72f impl-link: link.passive is no longer used 2026-03-02 17:19:41 +01:00
Wim Taymans
b8e27cc02b modules: IO_HUP also has an error 2026-03-02 12:22:30 +01:00
Wim Taymans
6e2522b657 modules: improve error reporting
Instead of reporting -EPIPE, get the error from the socket and report
that instead.
2026-03-02 12:13:22 +01:00
Wim Taymans
87087a4629 jack: never return NULL from jack_port_by_id()
JACK will never return NULL from jack_port_by_id() because the id
and the port_t are the same for JACK.

In PipeWire however we use the serial number as the id and so it can
be removed and become invalid. In this case, return a dummy port
from the client that can be used for some of the basic operations
you can do on a port_t, like get the name etc.

Also make sure that port_name() doesn't return NULL in case we use the
dummy port (which has the client set to NULL).

Fixes #3512
2026-03-02 11:41:21 +01:00
Jonas Holmberg
f4e174870e module-protocol-native: Fix socket activation
Fix path comparison in is_socket_unix() and don't unset LISTEN_FDS since
the function that uses it is called more than once and it was not unset
when sd_listen_fds() was used.

Fixes #5140
2026-03-02 10:28:26 +01:00
Wim Taymans
d206b06c70 sendspin: cleanup receive sync and logging
don't check the same thing twice.

Make sure values are set before we log them.
2026-03-01 12:49:24 +01:00
Wim Taymans
06f336a581 json-builder: handle allocation failures in vasprintf 2026-03-01 12:40:34 +01:00
thewrz
8ceb671cc8 module-vban: derive write position from frame counter
Instead of writing packets sequentially and losing sync on any
frame gap, compute the write position from the VBAN header's
n_frames field. Out-of-order packets land at the correct
ringbuffer offset, matching how module-rtp handles this.

Only advance the writeindex when a packet extends the frontier
so that late arrivals fill gaps without moving the pointer
backwards.

Fixes: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/5145
2026-02-27 22:48:12 -08:00
Wim Taymans
354ec08b9b doc: add sendspin modules to doc 2026-02-27 20:43:56 +01:00
Wim Taymans
b05fcdbfbd modules: fix compilation without avahi 2026-02-27 20:36:37 +01:00
Wim Taymans
8eed27820c meson: try to fix the doc build 2026-02-27 18:23:45 +01:00
Frédéric Danis
ee18160c4e bluez5: bap: Fix typos 2026-02-27 17:19:01 +00:00
Wim Taymans
9ad5ca2e5a websocket: fix some overflows
Fix some integer and buffer overflows as suggested by Sami Farin.
2026-02-27 17:58:51 +01:00
Wim Taymans
dee2d5ee06 zeroconf: sanitize the properties
Use some constants for the zeroconf properties. Make the right ones are
used in all places.
2026-02-27 17:31:42 +01:00
Wim Taymans
3a1b790588 modules: port snapcast to zeroconf helper 2026-02-27 17:31:38 +01:00
Wim Taymans
a065cff8c1 zeroconf: pass user data as const 2026-02-27 16:06:33 +01:00
Wim Taymans
db713c8264 modules: port rtp-session to new zeroconf helper 2026-02-27 13:50:49 +01:00
Wim Taymans
a1db2b8d35 pulse-server: port zeroconf publish to helper 2026-02-27 12:22:52 +01:00
Wim Taymans
c09bcfdc97 zeroconf: support proto, ifindex and subtypes
Also improve debug a little.
2026-02-27 12:22:24 +01:00
Wim Taymans
cd77dd0e62 zeroconf: add some more error checking
Also publish domain and host when we can and fix a memleak.
2026-02-26 23:19:53 +01:00
Wim Taymans
79d8e7f61d modules: port zeroconf-discover to new helper 2026-02-26 23:19:49 +01:00
Wim Taymans
b67937f247 module-raop: port to new zeroconf helper 2026-02-26 18:16:08 +01:00
Wim Taymans
fa04146cfb modules: move zeroconf code to zeroconf-utils 2026-02-26 17:09:45 +01:00
Wim Taymans
4a399172b6 sendspin: implement receiver zeroconf connect
Let a receiver connect to the zeroconf server. Add option to connect to
multiple serves with rules.

Fix some leaks.

Fixes #5095
2026-02-26 16:48:06 +01:00
Wim Taymans
d6654e84a7 sendspin: add sendspin sender and receiver
The sender makes an input stream for each connected client. This makes
it easier to do the per client conversion using the adapter and send
different channels to clients.

The receiver uses linear regression to map ringbuffer indexes to server
timestamps and server timestamps to client timestamps. It can then
schedule playback against its own clock.
2026-02-26 12:17:17 +01:00
Wim Taymans
6daa8ccc0d json-builder: zero the struct 2026-02-26 12:08:26 +01:00
Wim Taymans
be3c63d55e module-raop: move base64 to separate file 2026-02-26 12:00:31 +01:00
Wim Taymans
03662b3dfe json-builder: avoid indent on the first item
Avoids putting a \n or (now also) space as the first item.
2026-02-26 11:17:17 +01:00
Wim Taymans
bdbb5f6d27 json-builder: add raw mode that leaves keys/strings like they are 2026-02-26 11:09:13 +01:00
Wim Taymans
7dd924797b audioconver: reformat the graph description for properties 2026-02-26 10:59:57 +01:00
Wim Taymans
ed361a856f tools: port various tools to the new json-builder
Remove custom json serialization code and use the new json-builder
from various tools.

spa-json-dump now has color and raw mode and can probably be simplified
a bit more.

The properties can now serialize arbitrarily large keys, which makes
a unit test work. It also has a new 'simple' option for SPA output,
which is added and used in pw-config.
2026-02-26 10:51:17 +01:00
Wim Taymans
6753c51ab8 spa: add a new json-builder helper
It keeps track of the json bits like commas and indentation, supports
colors and recursive reformatting. It also supports a simple mode that
implements the SPA syntax.
2026-02-26 10:48:53 +01:00
Wim Taymans
692590b30a json: a container start also ends a bare string
This stops the parser from seeing foo{bar as a single string.

This also makes a valid test work, add another small test.
2026-02-26 10:42:14 +01:00
Barnabás Pőcze
848ac24490 spa: libcamera: source: fix stop sequence
Currently it is possible for the request completion handler (`impl::requestComplete`)
to observe `impl::source.fd` while it is being modified in `impl::stop()`.

Fix that by closing the eventfd after the camera has been stopped.

Fixes: 3e28f3e859 ("spa: libcamera: source: rework startup sequence")
2026-02-24 18:29:51 +01:00
Wim Taymans
6961bfeaa1 examples: adapt samplerate in audio-dsp-src
Follow the graph rate to generate the sine wav instead of using a
hardcoded value.

See #5135
2026-02-24 15:12:43 +01:00
Wim Taymans
5a285602e2 scheduler: sync-groups don't define the node runnable state
The sync-groups are only to group nodes with the same driver but don't
make them runnable.

This avoid making v4l2 runnable (without a link) when running ardour
because ardour uses the transport, which activates the sync group.
2026-02-23 11:32:40 +01:00
Anders Jonsson
426a5be235 po: Update Swedish translation 2026-02-22 21:50:02 +01:00
Ripley Tom
ff04b47942 meson.build: Add -Werror=discarded-qualifiers 2026-02-22 15:59:56 +00:00
Ripley Tom
c847b81629 spa/plugins/alsa/acp/compat.h: Fix missed -Wdiscarded-qualifiers warning 2026-02-22 15:59:56 +00:00
Ripley Tom
367ce4626c
src/modules/module-rtp-source.c: Fix alignment requirement for 32 bit build 2026-02-21 20:55:17 +01:00
Wim Taymans
846096d435 scheduler: prepare link before usage
A link might become unprepared because a node suspended. When we want
to make the nodes runnable, make sure the link is prepared again.
2026-02-21 17:25:54 +01:00
Wim Taymans
476220c18b scheduler: don't take active state into account for grouping
The grouping of the node does not depend on the active state.
2026-02-21 16:33:21 +01:00
Wim Taymans
973f48dde7 scheduler: make always_process nodes runnable
Don't just set the flag but call make_runnable for always_process node
so that the peers also get activated.
2026-02-21 16:32:30 +01:00
Wim Taymans
9e82e49446 scheduler: rework the runnable state calculation
Move the runnable state calculation out of the collect_nodes function.
They are really two different steps that doin't overlap much.

The runnable state of a node is very easy to calculate. A node is
runnable if it is linked to another node without a passive port. When we
find two runnable nodes, make them runnable, which makes all nodes
linked to them runnable, stopping at passive ports.

We don't have to check the active state of the nodes or links to group
them together. This ensures we don't swap nodes around too much when the
node or link state changes.
2026-02-21 16:19:34 +01:00
Wim Taymans
ce18660127 tests: fix test 2026-02-20 10:35:31 +01:00
Wim Taymans
8f3d8d77ab impl-link: prepare a link right after creating it
There is no reason to delay preparing the link (by the scheduler) when
both nodes are active, we can do that right from the start.

This makes things a bit more symetrical because deactivating a node does
not unprepare a link.

This however changes things a bit because you can no longer delay link
prepare until you activate the node. I don't know if this is actually in
use and it would probably be to delay format negotiation. The right way
do delay format negotiation is to wait until an EnumFormat is set but
that is something to improve later.
2026-02-20 10:35:31 +01:00
Arun Raghavan
5bd93b97ad Revert "spa: json: Add a helper method to shrink an object string"
Drop this until we have better API for building/visiting JSON.

This reverts commit c244fbf945.
2026-02-19 20:02:51 +00:00
Barnabás Pőcze
e46bfe67b6 treewide: fix some -Wdiscarded-qualifiers
Newer glibc versions have made certain `str*()` functions into macros
that ensure that the const-ness of the argument is propagated to the
return type.
2026-02-19 21:00:10 +01:00
Barnabás Pőcze
d7c3e8c2bc pipewire: module-roc-{sink,source}: fix log format string issues
Passing an unknown string as the format string is unsafe, so don't do it.

Fixes: b9922d8ed5 ("module-roc: forward roc-toolkit logs to pipewire logs")
2026-02-19 19:37:15 +00:00
Barnabás Pőcze
d8b06f94ee pipewire: module-roc-{sink,source}: remove logging related unused code
!2699 has been merged a bit prematurely and it contained things that are
not used. So remove the unused member variables, functions, fix module
usage strings, and move some functions from headers.
2026-02-19 19:37:15 +00:00
Arun Raghavan
d4329600d1 audioconvert: Report loaded filter graphs in props
Makes it easier to know what filters have been loaded.
2026-02-19 19:29:40 +00:00
Arun Raghavan
c244fbf945 spa: json: Add a helper method to shrink an object string 2026-02-19 19:29:40 +00:00
Arun Raghavan
f5107f3e83 ci: Fix doccheck error message 2026-02-19 11:04:23 -08:00
Arun Raghavan
569c2dce55 doc: Add module scheduler subpage 2026-02-19 11:04:23 -08:00
Arun Raghavan
987579b7b7 tests: Update context events test
New field and version, update the test.
2026-02-19 10:45:45 -08:00
Wim Taymans
2fb38af3e0 modules: move the scheduler to a module
Mostly because we can but also because there are more ways of doing
the scheduling and this opens the door for some experimentation.
2026-02-19 17:44:27 +01:00
Wim Taymans
7887c365d1 filter-graph: Make a new control_sync function
This function is run for all the nodes with the data loop locked. It can
be used to atomically update multiple node controls.

We can't use the control_changed function because this one runs without
the lock and might do slow things, like what the sofa plugin currently
does.

See #5019
2026-02-19 13:15:40 +01:00
Wim Taymans
e7ca02c4d8 filter-graph: sync control updates with data thread.
don't read the control ports from the processing thread and check for
updates. Use the control_changed signal to check and update the
parameters of the biquad atimically.

See #5019
2026-02-19 13:15:39 +01:00
Wim Taymans
be0e037809 pulse-server: avoid doing allocations and string copies
We can just concatenate the stream and client dict on the stack, reusing
all the strings without having to do allocations or copies.

Also filter out the object.id and object.serial from the client, we want
to keep the ones from the streams.
2026-02-19 13:03:50 +01:00
Wim Taymans
6eb4483069 pulse-server: add client props to sink_input/source_output
Instead of adding the client props to the stream props when we create
it, add them when we enumerate the streams.

This makes it possible to also have the client props in the stream props
for streams that are not created with the pulse API.

Fixes #5090
2026-02-19 12:08:23 +01:00
Wim Taymans
b8b2c58cda Development continues as 1.7.0 2026-02-19 11:09:50 +01:00
Wim Taymans
700cea78db 1.6.0 2026-02-19 09:59:34 +01:00
Misha Baranov
b9922d8ed5 module-roc: forward roc-toolkit logs to pipewire logs
Roc-toolkit log records are captured via a callback and
written to PipeWire log with corresponding verbosity level.
The log.level config parameter limits record verbosity at
the roc-toolkit level.
2026-02-19 08:42:29 +00:00
Damien Espitallier
88cbe24201 alsa-udev: Allow ACTION_REMOVE on ignored cards
Move the card->ignored check to only apply to ACTION_CHANGE, not ACTION_REMOVE. This ensures that ignored cards can still be properly removed when they are unplugged.
2026-02-17 19:19:52 +01:00
Wim Taymans
7956d7ceaf snapcast: support IPv4 link-local addresses 2026-02-17 13:08:56 +01:00
Wim Taymans
0e80287625 RAOP: also support link-local addresses for IPv4
Patch by Lairton Lelis da Fonseca Junior (@lairton)

Remove the hard skip for IPv4 link-local addresses and add an interface
binding (matching the existing IPv6 link-local behavior).

The host needs a link-local address on the interface (ip addr add
169.254.x.x/16 dev wlan0 or via NetworkManager +ipv4.addresses).

Fixes #4830
2026-02-17 13:02:03 +01:00
Wim Taymans
ab70dae0a8 modules: add PRIORITY_SESSION
For driver nodes, priority.session is needed to be able to change the
default device.

Fixes #5125
2026-02-16 10:38:05 +01:00
filmsi
de778e6235 Replace sl.po 2026-02-16 09:01:57 +00:00
Yedaya Katsman
fc723d7b15 pw-mon: Fix help message for --hide-params 2026-02-15 10:18:34 +00:00
Pauli Virtanen
7dd2c60b12 bluez5: synchronize transport state after acquire of an acquired transport
With keepalive enabled, we need to emit state change event on acquire
similarly as we do if refcount was already positive.

Co-authored-by: Martin Geier <martin.geier@streamunlimited.com>
2026-02-14 12:03:07 +02:00
Alexander Sarmanow
63129dd3dc fixup 2026-02-13 17:16:18 +00:00
Alexander Sarmanow
5c9b3ee05a bluez5: bap: use BD address for per-adapter BIG config
HCI indexed names are not stable. The adapters BD address is here a
better approach to map the configuration.
2026-02-13 17:16:18 +00:00
Alexander Sarmanow
5f12dd99a3 bluez5: add adapter reference to remote_endpoint
This should make adapter members easier accessible via remote endpoints.
2026-02-13 17:16:18 +00:00
lumingzh
b0b6f6ca37 update Chinese translation 2026-02-13 09:40:40 +08:00
Jonas Holmberg
ca4fa88598 context: set time in position for drivers
Set time in position for drivers to make sure an old time isn't copied
by followers before the driver is started.
2026-02-12 17:15:33 +01:00
Wim Taymans
7f08c0d404 doc: fix a typo 2026-02-12 13:46:04 +01:00
Wim Taymans
11d5e071ec stream: return -EIO when doing get_time in != STREAMING
The stream should be streaming before the get_time call is meaningful.

Various places in the code already check this an fall back to a default
value, we just need to return an error here.
2026-02-12 12:26:33 +01:00
Pauli Virtanen
12fb9ab831 bluez5: use correct A2DP profile in codec switch
We can only switch within currently connected A2DP profiles, as
generally remote endpoints are available only for the connected ones.
2026-02-11 18:53:32 +02:00
Wim Taymans
47de1e15a4 channelmix: handle more than 64 channels
When setting up the mix matrix, don't just iterate over the first 64
(CHANNEL_BITS) positions because then we will never be able to configure
more than 64 channels in the matrix.

Instead iterate until we have filled all src and dst entries in the
matrix. For the first 64 positions we might need to check the channel
mask to get the right positions in our source matrix.

Fixes the channel mixer for >64 channels where the positions above 64
where 0 in the matrix and muted.

Fixes #5118
2026-02-10 13:34:44 +01:00
Wim Taymans
494d727108 channelmix: improve debug
Instead of printing lines and lines of numbers, format everything as
a matrix. Only do the work when debug is enabled.
2026-02-10 13:18:01 +01:00
Wim Taymans
a2df282086 pw-cat: add a container option and some --list options
Add a container option to override the extension check and force a
container when saving.

Add some more formats that are supported by libsndfile.

Add some options to list all supported formats, extensions/containers,
layouts and channel names.

Fixes #5117
2026-02-09 13:55:52 +01:00
Frédéric Danis
0470f96887 bluez: bap: Select correct settings for select_config()
Depending on the codec kind, select appropriate settings to pass
to select_config().

This allows to pass the bluez5.bap.force-target-latency property,
and so to select the same configuration.
2026-02-09 09:27:10 +00:00
Frédéric Danis
c499255098 bluez5: bap: Add device property to force target latency
Some PTS tests (e.g. BAP/UCL/SCC/BV-046-C or BAP/UCL/SCC/BV-077-C)
requests to select QoS from low-latency or high-reliabilty.

The bluez5.bap.force-target-latency device property allows to force it.
For other values than low-latency or high-reliabilty the QoS selection
will use both tables to find the more appropriate configuration.
2026-02-09 09:27:10 +00:00
Barnabás Pőcze
2770143f50 gst: pool: fix buffer release race condition
A call to `release_buffer()` may happen in a gstreamer thread concurrently
with the pipewire stream emitting the `remove_buffer` event in the thread
loop, which, in pipewiresink calls `gst_pipewire_pool_remove_buffer()`, which
in turn modifies the `GstPipeWirePoolData` object. Thus a data race occurs
when accessing its members, which can lead to `pw_stream_return_buffer()`
being called with a null pointer.

Fix that by locking the thread loop before checking the conditions.

Fixes: c0a6a7ea32 ("gst: handle flush event in pipewiresink")
2026-02-09 09:25:36 +00:00
Stanislav Ruzani
64e0a9cbd9 alsa-pcm: set rate_match rate to 1.0 when not matching
Only set rate_match rate to DLL correction when matching is active.
When ALSA is driver and not matching, set rate to 1.0 to indicate no rate adjustment needed.
DLL still runs for buffer level management but rate_match should not expose correction
when matching is inactive to avoid confusion during debugging.

Signed-off-by: Stanislav Ruzani <stanislav.ruzani@streamunlimited.com>
2026-02-05 10:45:11 +01:00
Carlos Rafael Giani
bac776f8b4 doc: spa: Explain the nsec and next_nsec values in the driver docs better 2026-02-05 10:34:32 +01:00
Pauli Virtanen
84bfbd92a1 bluez5: bap: prefer high-reliability qos for 44.1/48 kHz
On some device combinations (MT7925 / Sony LinkBuds S) the low-latency
48 kHz QoS crackle.

It's probably better to default to high-reliability for those, until we
have proper quality vs. latency configuration in place.
2026-02-03 19:10:25 +02:00
Hugo Osvaldo Barrera
57efceeb02 Implement socket activation without libsystemd
Socket activation uses sd_listen_fds from libsystemd, and can only be
compiled on systems with systemd.

This is an issue for Alpine / postmarketOS, where upstream has no
systemd package, but downstream depends on upstream's pipewire package
and wants to rely on socket activation. This also prevents using
socket-activation on other non-systemd distributions, including
non-Linux.

Implement equivalent functionality without a dependency on libsystemd.
2026-02-03 13:17:59 +01:00
Wim Taymans
2c0988ab4c bluez-dbus: fix adapter memcpy length
sizeof(adapter) is larger than the big_entry->adapter and so the code
would copy too much. Instead only copy the strlen of the parsed
adapter, which we checked above to be smaller than the available size.

This doesn't copy the 0 byte because the memory is assumed to be 0
filled already by the calloc.

If the address is exactly the HCI_DEV_NAME_LEN, it will result in a non-0
terminated string, which may or may not be a problem...
2026-02-03 10:16:22 +01:00
Wim Taymans
ac7728097f snapcast: support newer snapcast service type
Newer snapcast servers publish the service as _snapcast-ctrl._tcp so
listen for that as well.

Fixes #5104
2026-02-03 10:01:38 +01:00
Carlos Rafael Giani
642504f5f9 module-rtp: Compensate for stream resampler effects in RTP sink node 2026-02-03 09:07:48 +01:00
Carlos Rafael Giani
6192c11038 module-rtp: Convert clock pos to RTP time in RTP source and factor in ASRC
This can easily be overlooked if the RTP rate equals the clock rate, which
is fairly common (for example, RTP rate and clock rate both being 48 kHz).

And, if an ASRC is active, and converting the output of the RTP source
node, the resampler's delay need to be taken into the account as well.
2026-02-03 09:07:48 +01:00
Stanislav Ruzani
e874f705a9 module-rtp: clear ringbuffer when stream stops to prevent old packets
Clear the ringbuffer in stream_stop() when processing stops to prevent old invalid packets
from being sent when processing resumes via rtp_audio_flush_packets().

This ensures a clean state when the stream restarts.
2026-02-03 09:07:48 +01:00
Carlos Rafael Giani
a16485f8aa module-rtp: Extract common RTP code into static library for better reuse 2026-02-03 09:07:48 +01:00
Carlos Rafael Giani
5d7f21f130 module-rtp: Improve rtp_audio_flush_packets() logging 2026-02-03 09:07:48 +01:00
Carlos Rafael Giani
9f0dc9c1af module-rtp: Update ringbuffer indices upon resync with proper API calls 2026-02-03 09:07:48 +01:00
Carlos Rafael Giani
543000151f module-rtp: Handle unsigned 32-bit integer overflow in constant delay mode 2026-02-03 09:07:48 +01:00
Carlos Rafael Giani
413f5762c4 module-rtp: Clear ringbuffer in constant delay mode
Clearing the ring buffer is important not only in the direct timestamp
mode, but also in the constant delay mode, since missed packets can lead
to gaps in the ring buffer. These gaps may have stale data inside if the
ringbuffer is not cleared after reading from it.
2026-02-03 09:07:48 +01:00
Carlos Rafael Giani
95970e539e module-rtp: Fix invalid ring buffer read attempts in direct timestamp mode
In corner cases where the read and write pointers are very close, it may
not be possible to read out all the wanted samples. This can for example
happen when there is a jump in the graph driver position. Currently, the
code reads the wanted number of samples out of the ring buffer regardless
of the write and read pointer positions. It does so even when the read
pointer is ahead of the write pointer (that is, an underrun occurs).

Fix this by checking the fill level and reading only the available amount
of samples if that amount is less than the wanted amount. The remaining
space in the target buffer is then filled with nullbytes.
2026-02-03 09:07:48 +01:00
Carlos Rafael Giani
a32e6e108c Revert "module-rtp: Remove device_delay from timestamp math"
This reverts commit dcdc19238b.

Reverting this because it caused big sync errors of ~62 ms in test setups.
Further discussions about this can be found here:

https://gitlab.freedesktop.org/pipewire/pipewire/-/merge_requests/2666

Followup commits modify the device delay application (by scaling it),
which is another reason why this needs to be reverted.
2026-02-03 09:07:36 +01:00
Barnabás Pőcze
cad1df748e meson.build: define SPA_AUDIO_MAX_CHANNELS for C++ as well
Previously the override was only present in `cc_flags`, meaning that
C++ source files, like `aec-webrtc.cpp`, would not have it.

Fixes: 6d74eee874 ("spa: bump channels to 128 again")
2026-02-02 18:13:14 +01:00
Wim Taymans
3c80f0fb3e filter-graph: add gain option to sofa
So that we can apply the gain to the IR. This is more efficient than
doing a volume after the convolution.

See #5098
2026-02-02 16:28:54 +01:00
Pauli Virtanen
f60e03b4ef pulse-server: use timeout also for creating sample-play streams
Add 35 sec timeout for PLAY_SAMPLE streams to start streaming, similar
to what we do with normal streams, and fail playback if they don't
start.

This avoids pending sample playback using up resources indefinitely if
streams fail to start for some reason, e.g. session manager is not
linking them.
2026-02-02 11:25:01 +00:00
Pauli Virtanen
a50e9a995e pulse-server: disconnect from server on EPROTO
If we get EPROTO, we likely have missed on some messages from the
server, and our state is now out of sync.

It's likely we can't recover (e.g. if error is due to fd limit hit), so
just drop the server connection in this case, similarly as if we got
EPIPE.
2026-02-02 11:16:15 +00:00
Alexander Sarmanow
f34a87fe38 bluez5: bap: add support for per adapter broadcast config
By setting the hci handle (e.g. hci0) of the desired adapter, the BIG
config will only applied on that adapter. In case no "adapter" entry in
the config is given, it will be applied on all adapters.

Signed-off-by: Alexander Sarmanow <a.sarmanow@televic.com>
2026-02-02 11:15:41 +00:00
Frédéric Danis
1a478c7147 bluez5: Fix stack smashing crash in remote_endpoint_update_props()
Commit 2942bae034 introduced parsing of
"SupportedFeatures" which uses a third DBusMessageIter pointer.

*** stack smashing detected ***: terminated
==389050==
==389050== Process terminating with default action of signal 6 (SIGABRT)
==389050==    at 0x4F57B2C: __pthread_kill_implementation (pthread_kill.c:44)
==389050==    by 0x4F57B2C: __pthread_kill_internal (pthread_kill.c:78)
==389050==    by 0x4F57B2C: pthread_kill@@GLIBC_2.34 (pthread_kill.c:89)
==389050==    by 0x4EFE27D: raise (raise.c:26)
==389050==    by 0x4EE18FE: abort (abort.c:79)
==389050==    by 0x4EE27B5: __libc_message_impl.cold (libc_fatal.c:134)
==389050==    by 0x4FEFC48: __fortify_fail (fortify_fail.c:24)
==389050==    by 0x4FF0ED3: __stack_chk_fail (stack_chk_fail.c:24)
==389050==    by 0xBC1D1A1: remote_endpoint_update_props (bluez5-dbus.c:3137)
==389050==    by 0xB53609F: ???
==389050==    by 0x1DF: ???
==389050==    by 0x61C17BF: ??? (in /usr/lib/x86_64-linux-gnu/libdbus-1.so.3.32.4)
==389050==    by 0x1DF: ???
==389050==    by 0xC5ED113: ???
2026-01-30 18:02:38 +01:00
Wim Taymans
69d8822303 filter-chain: tweak spatializer gain
We're adding two signals together so half the gain to keep it at
the same volume.
2026-01-29 16:52:02 +01:00
Wim Taymans
47dd57faa7 filter-graph: handle other SOFA errors as errno 2026-01-29 16:50:52 +01:00
Wim Taymans
2aecb49f50 pulse-server: use null to clear the value
The message makes it seem that you can pass 'clear' to clear the setting
but in fact you should pass 'null'.
2026-01-27 10:17:34 +01:00
Arun Raghavan
ed59342d28 pipewire-pulse: Expose bluetooth headset autoswitch config as a message
Makes it easier for libpulse-based clients to modify this setting if
they want.
2026-01-26 14:44:30 -08:00
Arun Raghavan
703380d62d pulse-server: Fix querying after setting of mono mixdown 2026-01-26 14:37:08 -08:00
Mengshi Wu
bc53b6b343 bluez5: Remove unused bt_features variable in sco_create_socket. 2026-01-25 17:26:15 +00:00
Mengshi Wu
332e35039d doc: Fix bluez5.hw-offload-datapath property type in documentation 2026-01-25 17:26:15 +00:00
Mengshi Wu
254620676f bluez5: Use named constants for Bluetooth codec IDs
Replace magic numbers (0x02, 0x05) with named constants BT_CODEC_CVSD
and BT_CODEC_MSBC for better code readability. Also remove redundant
zero initialization of num_caps field since the buffer is already
memset to zero.
2026-01-25 17:26:15 +00:00
Mengshi Wu
78f16bc04b bluez5: Remove hw-offload feature flag check and associated quirks
The sco_offload_btcodec() function now returns void and only skips
offload setup when using the default datapath, simplifying the logic
and removing the need for explicit feature flag checks.
2026-01-25 17:26:15 +00:00
Mengshi Wu
2b5d21da5b bluez5: simplify SCO datapath parsing with spa_atou32 2026-01-25 17:26:15 +00:00
Mengshi Wu
2d6a7d2186 bluez5: fix format string in sco_offload_btcodec log message 2026-01-25 17:26:15 +00:00
Mengshi Wu
db7c74a042 bluez5/backend-native: Add HFP/HSP hardware offload datapath configuration
Add support for configuring the SCO hardware offload data path for
HFP/HSP profiles using the Bluetooth SIG-specified procedure. This
enables vendor-specific SCO offload integrations.

Changes:
- Add `bluez5.hw-offload-datapath` configuration property (default: 0)
- Implement `sco_offload_btcodec()` to set BT_CODEC socket option
- Add `SPA_BT_FEATURE_HW_OFFLOAD` quirk feature flag
- Apply offload configuration when creating SCO sockets if quirk enabled
- Document new property in pipewire-props.7.md

The datapath ID is configurable via device parameters and only applied
when the hardware offload feature flag is set in quirks, allowing
platform-specific SCO offload implementations.
2026-01-25 17:26:15 +00:00
Wim Taymans
35817c0d85 pw-cat: support some more formats
So that you can give an .oga extension and --format=opus to get an
opus ogg file.
2026-01-22 17:14:41 +01:00
Jonas Ådahl
f2c4452e8d stream: Make capability device IDs an JSON object
Also change the encoding of the byte array to use hexadecimal encoding,
i.e. the byte array [100, 200] in becomes "64c8".
2026-01-22 16:08:19 +00:00
Jonas Ådahl
8600721de0 stream: Change PW_CAPABILITY_DEVICE_ID_NEGOTIATION to a version number
This allows easier evolvement of the negotiation protocol.
2026-01-22 16:08:19 +00:00
Arun Raghavan
04793138b5 alsa-card-profiles: Add config for a couple of JBL gaming headsets
Similar to the existing SteelSeries and Logitech ones, but the order of
the playback endpoints is reversed, and only mono input is supported.
2026-01-22 15:35:05 +00:00
Wim Taymans
f978b702b1 pw-cat: also look at simple formats for extension
The simple formats contain some common mappings for other extensions such
as mp3.

Makes pw-record test.mp3 actually write an mp3 instead of a wav file.
2026-01-22 13:08:06 +01:00
Wim Taymans
f82f4945b9 filter-graph: improve min/max checks for control
Clamp the control values to their min/max.

Remove the default_control array, we can just restore the default with
the port_set_control_value(). Do this when no value has been set on the
port when we set up the graph. The avantage is that we calculate the
default, min and max correctly when they depend on the graph rate.

Fixes #5088
2026-01-21 18:35:36 +01:00
Sven Ulland
c634ef9610 pipewiresrc: Fix video crop metadata not being set
Since commit a1f33a99df changed buffer handling to create new GstBuffers
instead of reusing pool buffers, the video crop metadata was silently
lost. The code used gst_buffer_get_video_crop_meta() which returns NULL
on a fresh buffer, so the crop values from PipeWire were never applied.

Change to gst_buffer_add_video_crop_meta() to actually attach the
metadata to the buffer.

Also remove the now-obsolete call in gst_pipewire_pool_wrap_buffer.

This was discovered when using the XDG Desktop Portal's RemoteDesktop
interface: the full desktop was being delivered instead of just the
selected window, because the crop region metadata was not being
propagated to the GStreamer buffer.

Fixes: a1f33a99df ("gst: dequeue a shared buffer instead of original
pool buffer"), from merge request !1258

CC: @jameshilliard
2026-01-21 15:26:57 +00:00
Wim Taymans
56a4ab5234 filter-chain: support no input or output streams
When the graph has no inputs and the channels is set to 0, don't create
a capture stream. Likewise, don't create a playback stream when there
are no graph outputs and the output channels is 0.

You can use this to make a sine source or a null sink.
2026-01-21 16:26:16 +01:00
Wim Taymans
7f2cce1021 filter-graph: add a null plugin
It discards all input.
2026-01-21 16:26:16 +01:00
Wim Taymans
a97c4d10af filter-graph: allow 0 input and output ports
There is no reason to fail when there is no input or output port.
We can simply run the graph with what there is. Even if there is no
input or output at all, running one instance of the plugins is
possible.

Add a busy builtin plugin that has no ports and keeps the CPU IDLE or
busy for the give percent.
2026-01-21 16:26:16 +01:00
Wim Taymans
ba3e564e34 filter-graph: notify about default numner of in/out
The filter graph has, after parsing, a default number of input and
output ports. This is based on the description or the first/last element
input and output ports. Pass this information in the properties when
we emit the info.

Don't use the number of configured input/output ports as the default
number of channels in filter-chain because this is only determined after
activating the graph. Instead, use the default input/output channels.

The result is that when you load filter-chain without any channel
layout, it will default to the number of input/outputs of the graph
instead of 0. This allows for the node to be visible in the pulseaudio
API.

Fixes #5084
2026-01-21 16:26:16 +01:00
Jonas Holmberg
39dd760c60 impl-port: Free capabilities
Free capabilities when destroying port.
2026-01-20 15:21:56 +01:00
Wim Taymans
0a27618cd4 1.5.85 2026-01-19 16:21:00 +01:00
Wim Taymans
e18c8ec224 doc: fix some typos 2026-01-19 13:30:15 +01:00
Carlos Martín Nieto
a858255ea2 doc: correct a copy-paste error in the pw-loopback manpage 2026-01-17 16:43:30 +01:00
Alexander Stephens
c74acf6c25 module-rtp: Fix timestamp integer overflow 2026-01-16 11:06:21 +00:00
Alexander Stephens
dcdc19238b module-rtp: Remove device_delay from timestamp math 2026-01-16 11:06:21 +00:00
Wim Taymans
b4b057196d resample: add bool to force inter resampler
Don't rely on the out_rate vs n_phases to decide on when to use the
inter resampler because the out_rate can change when we activate the
adaptive resampler.

Instead use a boolean that we can set at the start.
2026-01-16 11:03:43 +01:00
Wim Taymans
06e4772864 channelmix: add some more channelmix positions
Implement downmix of some top and rear channels as well.
2026-01-15 16:43:43 +01:00
Wim Taymans
c28a85ab34 zeroconf: use name+mode to detect duplicates
So that there can be a sink and source tunnel even when the name is the
same.

Based on patch by @kajusnau

Fixes #5079
2026-01-15 13:08:24 +01:00
Wim Taymans
9fdbe98330 resample: limit the amount of phases
If we have in and out rates with a very small GCD, we might end up with
a lot of phases. Limit the number of phases to 1024 and switch to
interpolating mode. 1024 phases is enough to accurately interpolate
from.

Together with the MAX_TAPS limit we will never create a filter
size that overflows 32 bits.

Fixes #5073
2026-01-15 12:05:07 +01:00
Jonas Ådahl
f055cf398d examples: Make fixate examples support device ID negotation
Things are structured in a way allowing testing falling back to
implicitly assumed device by defining/not defining macros.
2026-01-14 08:15:43 +00:00
Jonas Ådahl
5481a235ed examples: Make fixate examples optionally stream DMA buffers
The video-play-fixate example will downgrade the stream to MemFd one
modifier at a time. Sometimes it's useful to test with not downgrading;
to avoid having to depend on actual DRM devices (real or virtual), fake
them by using memfd and mapping them in the sink.
2026-01-14 08:15:43 +00:00
Jonas Ådahl
fc5b43a0b0 examples: Make fixate examples start inactive
The actual formats are sent just before activating. This is done in
preparation for supporting device ID negotiation.
2026-01-14 08:15:43 +00:00
Jonas Ådahl
58b958860e stream: Add DRM device negotiation
A DMA buffer from a DRM device are typically accessed using API related
to a DRM device, e.g. Vulkan or EGL. To create such a context for using
with a PipeWire stream that passed DRM device DMA buffers applications
have so far usually guessed or made use of the same context as the
stream content will be presented. This has mostly been the Wayland
EGL/Vulkan context, and while this has most of the time worked, it's
somewhat by accident, and for reliable operation, PipeWire must be aware
of what DRM device a DMA buffer should be accessed using.

To address this, introduce device ID negotation, allowing sources and
sinks to negotiate what DRM device is supported, and what formats and
modifiers are supported by them.

This will allow applications to stop relying on luck or the windowing
system to figure out how to access the DMA buffers. It also paves the
way for being able to use multiple GPUs for different video streams,
depending on what the sources and sinks support.
2026-01-14 08:15:43 +00:00
Jonas Ådahl
e615f17573 examples/video-src-fixate: Fake DmaBuf fd with memfd
Otherwise the stream will fail due to not being able to import fd.
2026-01-14 08:15:43 +00:00
Jonas Ådahl
21217a1fad examples/video-play-fixate: Switch to RGBA
The video-src-fixate was previously changed from RGB to RGBA, so to be
able to resolve a format, switch here too.
2026-01-14 08:15:43 +00:00
Jonas Ådahl
97976e4daf examples/video-play-fixate: Split out format param handler 2026-01-14 08:15:43 +00:00
Jonas Ådahl
01627e0367 examples/video-src-fixate: Split out format param handler 2026-01-14 08:15:43 +00:00
Wim Taymans
13131706aa spa: use the right AVX2 flags
Our AVX optimizations are really AVX2 so rename the files and functions
and use the right HAVE_AVX2 and cpu flags to compile and select the
right functions.

Fixes #5072
2026-01-13 12:03:09 +01:00
Pauli Virtanen
c1f7963c2a bluez5: fix crash due to debug_mono
Enabling bluez5.debug-iso-mono was in non-ISO branch and crashes. Fix
that.
2026-01-12 19:57:56 +02:00
Pauli Virtanen
f21c44751e bluez5: iso-io: resync if controller packet completion is out of sync
Add heuristic to resync streams if controller packet completion times
for different streams differ by too much.  This likely indicates
controller has lost sync between the streams, and we have to reset
playback.

There's no way to do this properly. The ISO-over-HCI transport is badly
specified in Bluetooth Core Specification.  Many controllers have broken
implementation of the current send timestamp read command, so packets
have no identifiers which ISO interval they belong to.

Controllers try to reconstruct the right interval based on
manufacturer-specific heuristics probably based on packet arrival times.
Kernel + USB introduce timing jitter, and playback sometimes desyncs and
packet from some streams are persistently sent some multiple of the SDU
interval off from the intended timing.

Try to determine this from packet completion latencies.  This is
somewhat manufacturer specific, tested on Intel & Realtek, hopefully
works on others too.
2026-01-11 17:55:26 +02:00
Pauli Virtanen
0003d7a2d0 bluez5: iso-io: add debug option for forcing same data in all streams
When debugging desync issues, it's useful to force same data be sent on
all ISO streams. Add option for that.
2026-01-11 17:55:26 +02:00
Pauli Virtanen
d0309b4e1e bluez5: deal with missing TX timestamps
There are known controller firmware bugs that cause packet completion
reports, mainly for ISO packets, to be missing.

To avoid getting stuck e.g. in ISO queue flushing, we should consider a
packet completed if sufficient time has passed even if controller (and
kernel) don't report it completed. Take 1 s as conservative timeout, the
expected values are some ms.

These firmware bugs also cause kernel to stop sending packets if too
many are left uncompleted, but we cannot detect that.
2026-01-11 17:55:26 +02:00
Wim Taymans
0869be11f8 avb: fix compilation 2026-01-11 11:27:15 +01:00
hackerman-kl
3597e09600 milan-avb: introducing SET/GET CONTROL, first IDENTIFY 2026-01-11 10:26:10 +00:00
hackerman-kl
8912090c4d milan-avb: add default AVB AECP MTU 2026-01-11 10:26:10 +00:00
hackerman-kl
1da30b545a milan-avb: now get/set may or not have a payload 2026-01-11 10:26:10 +00:00
hackerman-kl
2f3c0bc596 milan-avb: introducing aem control value units header 2026-01-11 10:26:10 +00:00
hackerman-kl
1d485d8bbc milan-avb: get entity-id rom the server, and not from a define 2026-01-11 09:00:36 +01:00
hackerman-kl
2fe254821c module-avb: milan: aecp-aem: introducing GET/SET_SAMPLING_RATE commands 2026-01-09 13:04:19 +00:00
hackerman-kl
f70fc7ea28 milan-avb: using pull and frequency as a union 2026-01-09 13:04:19 +00:00
hackerman-kl
e7f1a8f55d milan-avb: get-set name: removing unused var 2026-01-09 13:03:02 +00:00
hackerman-kl
d3a5b352b2 milan-avb: Make sure to get payload size not the packet size 2026-01-09 09:38:06 +01:00
Timon
aec99d2b6f format: Add support for sample rate of 1.536 Mhz 2026-01-09 01:16:30 +01:00
Wim Taymans
6ed9645465 profiler: keep per node last_profile_time
So that multiple drivers can get updated per interval when
profile.interval.ms is used.

Fixes #5061
2026-01-06 17:24:55 +01:00
Pauli Virtanen
c96e58af01 bluez5: handle BAP device set volume change notifications
Update volume state on device set volume notifications.

When one device sends volume notification, CAP specifies volume on other
devices shall be synchronized too.
2026-01-06 16:14:22 +00:00
Pauli Virtanen
260e8261d5 bluez5: add bluez5.autoswitch-routes option to indicate loopback nodes
When session manager emits loopback nodes for profile autoswitch, we
need to indicate them in the Routes.

Otherwise, the port information in Pulseaudio API doesn't account for
them, and some apps (eg GNOME) misbehave, as the loopback node sometimes
doesn't have valid ports.
2026-01-05 20:43:13 +02:00
Pauli Virtanen
e91a79dab7 spa: alsa: actually set the channel map when use-chmap=true
When using channel maps, the active map should be set using
snd_pcm_set_chmap(). This has to be called when stream is in prepared
state.

Track which of the maps the selected format has set, and set it in
do_prepare().
2026-01-05 15:37:05 +00:00
Torkel Niklasson
bb0efd777f impl-node: Don't suspend when links are busy
When suspend_on_idle is set and we got to idle, there is a chance that
there is work in the work queue that is dependent on formats being set.
In suspend_node, check whether the links have a non-zero busy count before suspending and
return -EBUSY if they do.
2026-01-05 15:36:41 +00:00
Pauli Virtanen
f754741d58 bluez5: handle BAP volume change events
Handle volume changes initiated by remote also for the BAP profiles.
2026-01-05 15:35:17 +00:00
Pauli Virtanen
8a7c71694d bluez5: handle BAP initial HW volumes
Setup initial HW volumes for BAP profiles similarly as done for A2DP.
As Client, retain the remote volumes as initial values, and as Server
use our own default volumes.

Also as A2DP Source, use the remote HW volume as initial value, if
available.

In the Client / A2DP Source modes session manager usually restores its
own volumes overriding what we set here.
2026-01-05 15:35:17 +00:00
Barnabás Pőcze
aa31e814b2 spa: utils: keys: remove api.libcamera.cap.* keys
These keys have not been used for a very long time. Debian code search does
not turn up any users either. There is also no such thing as "libcamera_capability".
These were created based on the `api.v4l2.cap.*` keys, but at the moment they
are not actually applicable to libcamera. So remove them.
2026-01-05 15:34:43 +00:00
Pauli Virtanen
11389d101a bluez5: iso-io: more accurate resync after overrun
Take active rate correction properly into account when dropping data on
overrun resync.

Drop data only for the currently processed stream, after data has been
consumed from it. Make sure the rate correction factor is updated after
this for the next cycle of the stream.

Also fix buffer fill level calculation: the fill level interpolation
should use node rate corr, not clock rate diff, since the calculations
are done in system clock domain. Fix same issue in fractional delay
calculation, and take no resampler prefill into account.

Later, we maybe need some more resampler APIs to avoid such details
leaking in.

Previously, stream could have its old rate correction locked in, and its
fill level would then end up off the target on the next cycle.
2026-01-05 15:34:06 +00:00
Pauli Virtanen
b535534611 bluez5: media-source: do buffering on connection start also with PLC
Also when we are capable of PLC, it's better to buffer audio at start,
to get a buffer level close to the target initially.

Delay ISO overrun handling one cycle after buffering is complete, so
that any resamplers are filled at that point.
2026-01-05 15:34:06 +00:00
Pauli Virtanen
1d7e89e069 bluez5: media-source: improve handling of underrun with PLC
When PLC data was produced due to underrun and decode buffer has reached
target level, drop received audio if we already did PLC for that packet.
It's better to lose some packets than having to resync latency.

When about to underrun when PLC is active, update rate matching before
filling up buffer, so that rate slows down and we do not get stuck in a
continuous underrun where PLC fills data and we drop received packets.
2026-01-05 15:34:06 +00:00
Pauli Virtanen
de34ce606f bluez5: media-source: fix off-by-one cycle in rate matching
The rate matching filter assumes buffer level for cycle j+1 is

    buffer(j+1) = buffer(j) + recv(j) - corr(j+1) * duration

but what we are actually doing is instead

    buffer(j+1) = buffer(j) + recv(j) - corr(j-1) * duration

because the correction factor that is computed is not used for the next
cycle, but the one following that. Although the filter is still stable
in theory the extra lag causes oscillations to be damped less.

Fix by using the computed correction factor for the next cycle, as
there's no reason why we'd like to have more lag in rate matching.

This then changes c(j-1) -> c(j) in the assumptions, which turns out to
fix the situation. Fix the filter derivation to match.  The filter
coefficients stay as they were, and they are actually exactly correct
also for short averaging times.

In practice, it is observed that ISO RX with quantum 4096 converges to
stable rate, whereas previously the matching retained small
oscillations.
2026-01-05 15:34:06 +00:00
Pauli Virtanen
c4812af436 bluez5: decode-buffer: fix buffer level after recovery
The buffer level number includes the current quantum, so it should not
be subtracted. We do this after recovery from glitch, and this throws
rate matching off.

The level after recovery should also include the resampler delay.
2026-01-05 15:34:06 +00:00
Pauli Virtanen
11faea9dbc bluez5: media-source: don't break audio if BAP presentation delay too small
As BAP server, when we can't satisfy BAP presentation delay, just accept
a bigger latency and emit a warning, instead of having broken audio.
Also make sure it works if quantum is forced to a larger value than our
wanted node.latency.

Take other latencies into account when selecting the wanted node.latency
for BAP Server.

Fix up port.params usage vs. user flag, which is not used here.

Reset decode buffer rate matching if we need to PLC due to underrun so
it doesn't get stuck at playback at increased rate if target is too
small.
2026-01-05 15:34:06 +00:00
Pauli Virtanen
ac3ac3382b bluez5: iso-io: delay streaming start until all acquires are complete
For better start synchronization, we should wait until all ISO nodes
that are going to be started finish creating ISO io.

Add a separate ready flag for startup that is set when all Acquire
requests are complete.
2026-01-05 15:34:06 +00:00
Pauli Virtanen
8b36e2d9b7 bluez5: support specifying preferred delays as BAP Server
Add options to control advertised delays supported.

Smaller delay needs smaller node.latency be used, so use 40ms as a
reasonable minimum preferred delay.
2026-01-05 15:34:06 +00:00
Wim Taymans
ae9361bb34 support: return NULL instead of FALSE 2026-01-03 22:54:25 +01:00
Wim Taymans
4a2710c779 dbus: handle errors better
Make sure we don't crash when running out of fds and the loop functions
start to return NULL sources.
2026-01-03 11:24:08 +01:00
Dimitris Papaioannou
1f2a5d99b1 module-eq: Unload filter-chain on destruction
Make the parametric-equalizer module destroy the underlying filter-chain
module on destruction. This makes the EQ nodes get destroyed on unload.
Fixes #5045
2025-12-26 18:53:48 +00:00
Barnabás Pőcze
29bbd79830 gst: deviceprovider: unregister pw_core event hooks
A `pw_core` may be shared between multiple streams, device provider
instances, thus when the reference of the given component to the core
is dropped, the event handlers must be unregistered so as to avoid
use-after-free and similar issues.

Fixes #5030
Fixes: 2bc3e0ca10 ("gst: deviceprodiver: Use GstPipeWireCore and some cleanups")
2025-12-25 14:57:21 +01:00
Wim Taymans
3738c3fc38 tools: only print latency when we print the port
Move the latency print code after where we print the port. That way
we only get the latency when we first print the port.

Avoid -lt from printing latencies for ports without a link.
2025-12-20 18:02:36 +01:00
Robert Rosengren
165bd7b219 pipewiresrc: fix race when node suspended moving from PAUSED to PLAYING
If in PAUSED state, the node can move from idle to suspended resulting
in format cleared and state is no longer negotiated. To avoid returning
not-negotiated error upon basesrc calling create callback, wait for new
format to be provided and negotiated state is back.
2025-12-19 07:15:08 +00:00
Frédéric Danis
e15e50c5ee spa: bluez: backend-native: Prevent HSP/HFP connection in both directions
The HSP and HFP profiles expect that a device function only as an audio
gateway or as an headset, which is the normal behavior for a headset,
a hands-free car unit or a phone.

In case of a desktop, it can perform both functionalities, but there's
no interest to get them at the same time as the bidirectional audio
is already supported.
2025-12-19 07:12:46 +00:00
Frédéric Danis
f468529084 spa: bluez: backend-native: Fix audio connection policy for HSP/HFP 2025-12-19 07:12:46 +00:00
Arun Raghavan
385161b12a pulse-server: Add a message to enable/disable mono mixdown
WirePlumber recently added a mechanism to force mono mixdown on audio
outputs, which is a useful feature for accessibility. Let's also expose
that setting via libpulse for existing audio settings UIs to be able to
use.
2025-12-18 16:35:35 -08:00
Martin Geier
8c7890eb52 spa: fix missing member initialization
Signed-off-by: Martin Geier <martin.geier@streamunlimited.com>
2025-12-18 16:53:54 +01:00
Arun Raghavan
e2262617aa spa: alsa: Fix off-by-one check in ELD channel position parsing 2025-12-17 11:08:44 -08:00
Jonas Holmberg
12b2e5d67c audiotestsrc: Operate as follower too
Don't start the timer but fill buffers when following another driver.
2025-12-16 13:15:00 +01:00
Arun Raghavan
ee42b18226 spa: alsa: Guard against mismatched LPCM channel count in ELD parsing
With wonky EDID states, we want to make sure we don't overflow the
available positions.
2025-12-15 12:47:49 -08:00
Frédéric Danis
d89d1668dc spa: bluez: backend-native: Add support for AT+BLDN for PTS tests
This allows to fake Last Dial Number call by calling the first memory.

This allows to pass PTS tests HFP/AG/OCL/BV-01-C and HFP/AG/OCL/BV-02-C.
2025-12-15 08:56:03 +00:00
Frédéric Danis
9a48bbaa36 spa: bluez: modemmanager: Add support for memory dialing for PTS tests
This add a new property to to allow to fake memory dialing for PTS tests
HFP/AG/OCM/BV-01-C and HFP/AG/OCM/BV-02-C.
2025-12-15 08:56:03 +00:00
Frédéric Danis
04cf29f7cd doc: Add property documentation for bluez5.disable-dummy-call 2025-12-15 08:56:03 +00:00
Wim Taymans
bb564d5eb6 avb: fix compilation 2025-12-15 09:27:10 +01:00
Janne Grunau
f03021edd1 stream: Fix pw_time.delay calculation for rate.num > 1
Pipewire uses a rate of 256/7680 with the integrated camera of Apple
silicon Macbooks. To calculate pw_time.delay correctly in this case it
has to be divided by time->rate.num. Without this division the delay
contribution of the `((latency->min_ns + latency->max_ns) / 2)` term
ends up as 255 which are 8.5 seconds.
pipewiresrc reports the delay as latency in the gstreamer pipeline which
results in rendering a frame every 8.5 seconds.
I suspect the non-normalized rate of 256/7680 is another bug in
pipewire. The rate for an UVC webcam is reported as 1/30. Both
Video4Linux2 devices report a discrete frame interval of 0.033s (30fps).

Fixes #4957
2025-12-15 08:22:07 +00:00
Mason Remaley
c7ebc66e64 Adds explanation to reduce chance of regressing the fix 2025-12-15 08:20:24 +00:00
Mason Remaley
a6f8e209ac These two functions were marked as static, but referened by the SSE41 implementation in a separate file
I'm not 100% sure if this was breaking SSE41 builds on the official build system (I'm building Pipewire
with a different process), but I suspect it was, because you can't combine these into a single translation
unit to sidestep it without including multiple copies of resample-native-impl.h which isn't desirable.
2025-12-15 08:20:24 +00:00
hackerman-kl
6f1938d501 milan-avb: milan: adding set/get clock-source for a clock-domain 2025-12-15 08:18:30 +00:00
hackerman-kl
bb1ef8ea5e module-avb: milan: introducing full entity model for mlian v1.2 2025-12-15 08:17:50 +00:00
hackerman-kl
b22e442b10 module-avb: milan: adding get/set for configuration 2025-12-15 08:17:50 +00:00
Tyler
43bf1b8f7c module-rt: warn if setting niceness fails with rtlimit 2025-12-11 16:38:00 -08:00
hackerman-kl
ba8c6154a0 milan-avb: silent gcc warning as the variable will be used 2025-12-11 08:13:20 +01:00
Wim Taymans
548f26882f avb: fix compilation 2025-12-10 11:33:06 +01:00
hackerman-kl
63abd4e71c milan-avb: cmds-get-set-name: fix unused variable warning 2025-12-10 07:11:16 +01:00
hackerman-kl
c2ada3175e module-avb: aecp-aem: SET/GET STREAM_FORMAT answer implemented.
In the current state the GET/SET stream format can handle the commands
response however, yet, it does not take care of checking that:

 * A bound input stream cannot have it set, should reply accordingly
 * A STREAMING_STREAM output stream cannot have it set, should reply
   accordingly.
2025-12-10 07:07:24 +01:00
Wim Taymans
8153efc6ed audioconvert: refactor some code
We sync the filter graph in two places, make a function so that both
places do the same thing.

Make node_reset clear the setup flag so that we don't have to do that
twice.
2025-12-09 21:01:11 +01:00
Torkel Niklasson
40aa6fbb64 audioconvert: Sync filter graphs in setup_convert
If the the audioconvert.filter-chain.N property is set early, they will
be added to the active_graphs list but with setup = false. When the node
starts, setup_convert is called, but the graphs aren't added to
filter_graphs. Run the do_sync_filter_graph at the end of setup_convert
to add them.
2025-12-09 19:52:12 +00:00
Torkel Niklasson
34122b4bf3 audioconvert: Set this->setup to false on flush command
If flush is called, the active graphs are deactivated but this->setup
remains true.
2025-12-09 19:52:12 +00:00
hackerman-kl
d9fa0629f6 milan-avb: milan: adding set/get name command handler 2025-12-09 19:40:59 +00:00
Frédéric Danis
25a6fdcdb1 spa: bluez: device: Add SPA_PROP_params to disable dummy call state
The current implementation only send the +CIEV:<call>,<active> event
if there's an active modem in ModemManager. This may lead to headset
disconnection as in (1) if the profile is by another application than
telephony one, e.g. a conference application/website.

This commit improves dummy call status update by adding a new
"bluez5.disable-dummy-call" props param in bluez5 device, allowing
external application like WirePlumber to set it dynamically.

(1) https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/1744

Fixes: https://gitlab.freedesktop.org/pipewire/pipewire/-/merge_requests/2606
2025-12-09 16:08:31 +01:00
Wim Taymans
7aa8d8d628 spa-resample: enable clipping in sndfile
We can actually use a command to enable clipping in sndfile instead
of doing it ourselves.
2025-12-09 13:55:37 +01:00
Wim Taymans
8e6945c496 spa-resample: clamp float values
sndfile does not seem to correctly clip samples so we have to do it
ourselves.
2025-12-09 13:38:46 +01:00
Timon
e1392cec0e Fix disabling of filter chains 2025-12-08 14:19:15 +01:00
hackerman-kl
13def13f01 module-avb: milan: aecp-aem handle de/registration for unsolicited notification, and add avb info common 2025-12-07 16:08:36 +00:00
hackerman-kl
ea653a52e3 module-avb: milan: lock command handles unsolicited notifications 2025-12-07 16:08:36 +00:00
hackerman-kl
6054c1a12b module-avb: introducing unsolicited notification reply 2025-12-07 16:08:36 +00:00
hackerman-kl
b43d915e71 module-avb: milan: make lock state as part of the generic entity state structure 2025-12-07 16:08:36 +00:00
hackerman-kl
4f8f7980f0 module-avb: milan: add default CONTROL_DATA_OFFSET for aem parsing 2025-12-07 16:08:36 +00:00
hackerman-kl
a6d7e98db3 module-avb: milan: introducing controller deregister unsolicited notification 2025-12-07 16:08:36 +00:00
hackerman-kl
ad43eba25c module-avb: milan: introducing controller registered unsolicited notifications 2025-12-07 16:08:36 +00:00
hackerman-kl
a97abf10ab module-avb: state variable entity id name fixing 2025-12-07 16:08:36 +00:00
hackerman-kl
86168ab1e2 module-avb: aecp-aem: move type into their own dedicated header file 2025-12-07 16:07:56 +00:00
hackerman-kl
2f83c5dab5 module-avb: descriptors: adding control specific defines 2025-12-07 16:07:56 +00:00
hackerman-kl
63a37e4947 module-avb: descriptors: introducing control and value format used by control descriptors 2025-12-07 16:07:56 +00:00
hackerman-kl
a1a33141d7 module-avb: descriptors: introducing clock domain descriptor 2025-12-07 16:07:56 +00:00
hackerman-kl
82fe584f51 module-avb: descriptors: introducing port flags 2025-12-07 16:07:56 +00:00
hackerman-kl
b90bd2c528 module-avb: descriptors: adding mapping and audio mapping format 2025-12-07 16:07:56 +00:00
hackerman-kl
43448f147c module-avb: descriptors: adding cluster descriptor for milan and legacy AVB 2025-12-07 16:07:56 +00:00
hackerman-kl
1b39e7836d module-avb: state: fix header includes 2025-12-07 16:07:56 +00:00
filmsi
034e8683c8 Updated sl.po 2025-12-07 08:00:37 +00:00
Pauli Virtanen
2942bae034 bluez5: parse and enable configuration of TMAP / GMAP features
Parse TMAP / GMAP features from MediaEndpoint:SupportedFeatures and pass
them onto the codec in SelectProperties, so it can determine which
mandatory features the device supports.

Add configuration option for specifying which TMAP / GMAP feature bits
we advertise to remote side.

Although some of these could be determined automatically, for production
systems it's better to have explicit option to specify which ones should
be advertised as this may depend on HW capabilities.
2025-12-06 11:23:48 +00:00
Pauli Virtanen
c623886625 bluez5: fix parsing of 0-element dbus arrays
Type checking by recurse + get_arg_type is wrong for 0-element arrays.
Just check from iterator signature.

Also avoid relying on malloc(0) != NULL
2025-12-06 11:21:56 +00:00
hackerman-kl
f65d5654d3 module-avb: milan: acquiring not supported directly called 2025-12-06 08:34:19 +01:00
hackerman-kl
198f4a92f5 module-avb: milan: introducing direct reply calls 2025-12-06 08:32:32 +01:00
Wim Taymans
57e589f2e1 stream: avoid flushing invoke before state change
A flushing invoke is dangerous because the application might have queued
a destroy, which could then be executed right while we do things.

Avoid doing the flushing invoke from the state change function. This was
done because previously we would invoke a process call when we were
working in non-RT mode. Nowadays we run the process function right from
the main thread and we don't need to invoke anymore. This also means
that we can't have pending process calls to flush out when we go to
paused. We do queue other calls, like drained and trigger-done but it
should not cause problems to let those through after the state change.
If this causes problems in the future, we can check the state before
emiting them.

Do a flushing invoke right before freeing the stream. This should be ok
because we removed all signal hooks so that the pending invokes would
not get to the app.

Fixes #5010
2025-12-04 10:16:33 +01:00
Wim Taymans
e30ee9c846 tools: support filenames in raw mode
The raw mode -a only supported reading raw data from stdin/stdout and
simply ignored the filename. Make it use the filename to determine
where to read from instead.

Support stdin/stdout for sysex mode as well and close the file when we
are done.

Fixes #5012
2025-12-04 10:13:13 +01:00
Wim Taymans
b68698a086 stream: refactor the free function
In the destroy we first remove all listeners and then we clean up the
memory. Move the memory cleanup to a separate function to make it easier
to refcount later.
2025-12-02 12:37:59 +01:00
Wim Taymans
0d7cb9b39f client-node: remove the MAPPABLE flag for MemPtr data
af3ad7bf9f set the fd and MAPPABLE
flag on buffer data when it was allocated inline in shared mem, even
for MemPtr data. In the client-node however we don't pass along this
fd and so we also should not pass the MAPPABLE flag for MemPtr data.

This fixes an issue in older clients that blindly try to map the fd when
the MAPPABLE flag is set, even when there is no fd (and there is already
a data pointer).

Fixes #5003
2025-12-02 12:07:47 +01:00
Mason Remaley
c81ee31c3b Fixes parameter type that trips ubsan 2025-12-02 00:52:57 +00:00
Wim Taymans
a172bf0f55 audiomixer: only passthrough on dynamic data
When the dynamic data flag is set on the buffer data, it means the
consumer can deal with any data pointer set on the buffer and we can
simply pass the one from upstream to downstream. If the flag is not set,
we need to copy the buffer data.

See #5009
2025-12-01 16:33:50 +01:00
Wim Taymans
4f39329ca9 alsa: use graph rate for rate latency
When we recalculate the headroom we also update the latency in frames.
We should express this latency in the graph rate. This is usually the
rate that is suggested in the target_rate but when we are forcing our
own rate (mostly when using IRQ or when in DSD/IEC mode) we should
ignore that value and use our own rate that we will force instead.

Fixes #4977
2025-12-01 14:20:12 +01:00
Wim Taymans
4152c5d292 alsa: add firewire latency before we scale with rate
We first add all the latency in the rate of the pcm device and then
convert it to the rate of the graph.

See #4977
2025-12-01 12:52:37 +01:00
hackerman-kl
52f2137397 module-avb: milan: aecp-aem: introducing available command handler 2025-12-01 09:16:06 +00:00
hackerman-kl
6619aba582 module-avb: milan: aecp-aem: adding lock-entity handler 2025-12-01 09:16:06 +00:00
hackerman-kl
1aacf8d15a module-avb: milan: aecp-aem: introducing the response to the lock-entity command 2025-12-01 09:16:06 +00:00
hackerman-kl
93b59609a8 module-avb: aecp-aem: invalid helper response status return fixed 2025-12-01 09:16:06 +00:00
hackerman-kl
e7c7b5058d module-avb: milan: aecp-aem: introducing entity_lock response helper 2025-12-01 09:16:06 +00:00
hackerman-kl
986254f56f module-avb: milan: es_builder: introducing entity milan descriptor with necessary information about the state of the descriptor 2025-12-01 09:16:06 +00:00
hackerman-kl
32ceb47937 module-avb: aecp-aem: adding struct base_info. The structure provides
information about the controller that last accessed, the time when when
it actually accessed, and the expiring time if a timer has to be
implemented
2025-12-01 09:16:06 +00:00
hackerman-kl
354006a699 module-avb: aecp-aem: adding available pdu 2025-12-01 09:16:06 +00:00
hackerman-kl
17812c33cc module-avb: aecp-aem: adding entity state for legacy AVB and milan 2025-12-01 09:16:06 +00:00
hackerman-kl
2673558a52 module-avb: aecp-aem: add Milan specific aecp aem into its own header 2025-12-01 09:16:06 +00:00
hackerman-kl
a1b829997e module-avb: es_builder: setup the callback count to use only necessary
memory
2025-12-01 09:16:06 +00:00
hackerman-kl
bcf6b185d7 module-avb: es_builder: splitting avb/milan es_builder 2025-12-01 09:16:06 +00:00
hackerman-kl
8e870c809c module-avb: aecp-aem: introducing seperation between milan v1.2 and legacy milan.
This introduces the following changes:

 * Using the time at which the command was received
 * Preparation for the unsollicited notifications
 * New folder holding all the AECP_AEM commands/responses and utils
 * Improving the code-reusability by using common handlers
2025-12-01 09:16:06 +00:00
hackerman-kl
5eea411a3c module-avb: avdecc: use enum instead of a boolean for avb mode (milan vs legacy avb) 2025-12-01 09:16:06 +00:00
hackerman-kl
8e135c1015 module-avb: utils: introduce array size for static arrays 2025-12-01 09:16:06 +00:00
hackerman-kl
cdf1ebe861 module-avb: aecp-aem: moving responses status in their own file 2025-12-01 09:16:06 +00:00
hackerman-kl
99a131a91d module-avb: aecp-aem: introducing seperation between milan v1.2 and legacy milan.
This introduces the following changes:

 * Using the time at which the command was received
 * Preparation for the unsollicited notifications
 * New folder holding all the AECP_AEM commands/responses and utils
 * Improving the code-reusability by using common handlers
2025-12-01 09:15:07 +00:00
hackerman-kl
929ac1f09f module-avb: avdecc: provide stringifier of avb enum 2025-12-01 09:15:07 +00:00
hackerman-kl
f3d0642994 module-avb: avdecc: use enum instead of a boolean for avb mode (milan vs legacy avb) 2025-12-01 09:15:07 +00:00
hackerman-kl
9f1c11ac34 module-avb: aecp-aem: moving responses status in their own file 2025-12-01 09:15:07 +00:00
hackerman-kl
c48e835d0c module-avb: internal: make sure that the descriptor are modifiable at runtime 2025-12-01 09:08:26 +00:00
hackerman-kl
af62143327 module-avb: remove duplicated string 2025-12-01 09:08:26 +00:00
hackerman-kl
b60623df4d module-avb: internal: destroy internal descriptors 2025-12-01 09:08:26 +00:00
hackerman-kl
a3ce0f3e28 module-avb: avdecc: destroy stream 2025-12-01 09:08:26 +00:00
hackerman-kl
c10f869836 module-avb: mrp: cleaning allocated attribute on destroy 2025-12-01 09:08:26 +00:00
hackerman-kl
c1dbba1a31 module-avb: acmp: cleaning pending allocated resources on destroy 2025-12-01 09:08:26 +00:00
hackerman-kl
8d99bf66bd module-avb: adp: clean the allocated resources if any 2025-12-01 09:08:26 +00:00
Wim Taymans
2ff45313de resample: allow compilation with custom default quality 2025-12-01 09:52:54 +01:00
Wim Taymans
52ec847cbd filter-graph: add feedback and feedforward to delay
Add feedback and feedforward controls to the delay. This makes it
possible to make comb and allpass filters with the delay to build
custom reverb effects.
2025-11-28 17:10:55 +01:00
Wim Taymans
933ac4be43 doc: use parblock 2025-11-28 11:53:07 +01:00
Wim Taymans
8c698366b8 doc: decreasing the transition band increases ringing 2025-11-28 11:42:16 +01:00
hackerman-kl
21bb281c75 module-avb: internal: the add descriptor return the descriptor 2025-11-28 10:12:32 +00:00
hackerman-kl
831357ee88 module-avb: es_builder: fix invalid return check 2025-11-28 10:12:32 +00:00
hackerman-kl
f30a0c1864 module-avb: milan: adding mode selection 2025-11-28 10:11:07 +00:00
hackerman-kl
76f8ebb1f2 module-avb: configuration adding milan boolean 2025-11-28 10:11:07 +00:00
Wim Taymans
a13d5eeccb doc: improve resampler properties docs a little 2025-11-28 11:10:00 +01:00
hackerman-kl
875dd91bc2 module-avb: Introduce changes in the mechanisms how the stream are
built:
* es_builder: create stream with state variables and counters
* acmp: do not use the stream list, go through the descriptor to find
  the index
* stream: do not store redundant information such as the index and
  descriptor
* internal: removing the stream server and function associated to it

module-avb: internal, stream: removing server_find_stream
2025-11-27 17:47:28 +00:00
hackerman-kl
546dafa0b0 module-avb: adding state base and stream specific information 2025-11-27 17:47:28 +00:00
hackerman-kl
a88b4bfecd module-avb: descriptor may be modified 2025-11-27 17:47:28 +00:00
hackerman-kl
8593235571 module-avb: init descriptor happens after everything is set up 2025-11-27 17:47:28 +00:00
Wim Taymans
98b4693525 1.5.84 2025-11-27 13:50:37 +01:00
Wim Taymans
94c05e9e2d spa: make sure unpositioned raw audio has unknown channels
Otherwise we might end up with partial channels when code doesn't
check the unpositioned flag.  It's better to set everything to unknown
when there is a mismatch between channel count and layout.
2025-11-27 11:45:21 +01:00
Wim Taymans
172a2af982 pw-cat: move optstring closer to the longopts definition 2025-11-26 15:23:33 +01:00
lumingzh
1efa2bda30 update Chinese translation 2025-11-26 08:33:10 +00:00
Frédéric Danis
6ced56e11d spa: bluez: backend-native: Fix BIEV HF indicators support for HFP AG
The "+BIND: <a>,<state>" reply to AT+BIND? should be sent for every
supported indicator.

"AT+BIEV= <assigned number>,<value>" should only be provided for
enabled indicators. The AG shall respond with an ERROR response code
if it receives updates for disabled or unknown HF indicators or values
that are out of bounds.

This allows to pass PTs tests:
- HFP/AG/HFI/BV-02-C
  AG receives an updated HF Indicator value
- HFP/AG/HFI/BI-03-C
  AG receives invalid updated HF Indicator values
2025-11-25 16:20:02 +01:00
Wim Taymans
1408dd5245 tools: use audio_layout in pw-cat
Use the standard layout names for channel map option. Provide aliases
for the old names.

Make sure we don't have too many channels for DSD.
2025-11-25 14:01:48 +01:00
Pauli Virtanen
3b6609f13a bluez5: release transports in CIG/BIG simultaneously
When releasing multiple transports, call Release() simultaneously
instead of serializing the calls.

This operations still needs to be blocking currently, as all releases
have to finish before we do other state-modifying ops.

This works around broken firmware on Creative Zen Hybrid Pro with BAP,
whose Disable command misbehaves when shutting down sink + source CIS
otherwise. It's also anyway better to shut down everything at once.
2025-11-25 09:23:13 +00:00
Wim Taymans
2c6aa8e0d0 pulse-server: only use passive for devices
The dont-inhibit-auto-suspend flag does not do anything when using
direct-on-input-idx (capturing from a stream) in pulseaudio, so also
make it do nothing on pulse-server.

See #4991
2025-11-25 10:17:14 +01:00
Wim Taymans
fc26e6321b spa: make cutoff configurable in spa-resample 2025-11-24 16:49:25 +01:00
Wim Taymans
ed2889cecf filter-graph: improve latency reporting of convolver
The latency of the convolver depends on the IR used. It's 0 for /dirac,
len/2 for /hilbert and let's assume it is 0 for file IRs.

Fixes #4980
2025-11-24 13:56:18 +01:00
Wim Taymans
33c7d9cba5 audioconvert: move warn to debug
changing the clock rate is not uncommon and does not need a warning.
2025-11-24 11:03:22 +01:00
Wim Taymans
7d5940101b resample: tweak cutoff some more
Increase the cutoff frequency for the lower quality modes. This should
give significantly better high frequency preservation at the expense of
more (but likely inaudible) aliasing.

Reduce the cutoff for the higher qualities in blackman and exp to
compensate the the wider transition band in those windows.

Increase cutoff for kaiser because of the sharper rolloff.
2025-11-24 10:35:53 +01:00
Wim Taymans
4760fd7f52 resample: tweak kaiser cutoff a little more 2025-11-21 11:42:34 +01:00
Wim Taymans
3f292e3ce3 stream: generate PeerCapbility for old servers
Automatically make an empty PeerCapability param when we receive a
Latency event without a PeerCapability. This makes new client always
receive a PeerCapability param, even when the other side did not provide
anything or when the server is too old to collect them.
2025-11-21 10:19:36 +01:00
Wim Taymans
941fc5f51c spa: add Capability and PeerCapability
Add a new SPA_TYPE_OBJECT_ParamDict object that contains a struct with
key/value pairs. We're using something similar for Tags but this is a
more generic version.

Make a new Capability param that uses the ParamDict object. This is
meant to be used to describe capabilities with the generic key/value
struct.

Make a new PeerParam object where the keys are generic ids of the peer
objects and the values any Pod. The idea is to use this object to store
a peer objects. Make some helpers to iterate the peers and their
objects.

Add a new PeerCapability param that uses the PeerParam object with
Capability objects. This can be used to send the collection of
Capabilities from all peers to a port. This is a bit like the tags but
in a more generic way. The tags could also be implemented in this new
generic way later.

Make the PeerFormats use the same PeerParam of Format objects.

The Capability param is set on ports. impl-link will collect all
Capability objects into a PeerCapability object and send this to the
peer. The difference with the Tag param is that these Capability params
are not in any way forwared on the node automatically (like what is done
in the loopback module) because they represent the capabilities of the
ports betweem the link.
2025-11-21 10:08:46 +01:00
Pauli Virtanen
8d59ad2713 bluez5: set some BAP Context metadata value on streams
Some devices refuse to enable microphone if Streaming Context metadata
is just Unspecified.

Set some reasonable values for the stream context we create along TMAP,
and try follow CAP rules for selecting the PAC.
2025-11-21 08:33:14 +00:00
Pauli Virtanen
2010a525d3 bluez5: simplify ltv parsing 2025-11-21 08:33:14 +00:00
Pauli Virtanen
ff6db3e08e bluez5: add codec_data for codec-private configuration data
With BAP codec configuration selection goes via multiple functions,
which will need to maintain some private state.

Adjust media_codec to allow for that.

Use it for get_qos().
2025-11-21 08:33:14 +00:00
Pauli Virtanen
914e8c6c7a bluez5: parse BAP endpoint Metadata field 2025-11-21 08:33:14 +00:00
Pauli Virtanen
bf801f4f7f bluez5: clean up LTV writing
Add ltv_writer that checks bounds, and use it. Simplify code a bit.
2025-11-21 08:33:14 +00:00
Frédéric Danis
f9f08f7f5c spa: bluez: backend-native: Fix CIEV call status support for HFP AG
Based on HFP specs, the audio connection is independent of the active
call status, which should be managed by the ModemManager part of the
plugin.
But when using HFP AG without modem attached, e.g. during zoom meeting,
the connection will be closed after a while unless call status has been
forced to active,
cf. https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/1744.

Currently and for HFP AG PTS tests requesting to get an audio connection
in 3 seconds after a call activates, this prevent to start audio
connection before starting a call.

This commit prevents to force the call status during audio (dis)connection
if a modem is available.
2025-11-21 08:27:44 +00:00
Frédéric Danis
b940d9f3a1 spa: bluez: modemmanager: Fix NameOwnerChanged
Get the ModemManager interfaces when the ModemManager starts after native
HFP has started.

Also add log topic to be able to select log level for modemmanager plugin
part.
2025-11-21 08:26:40 +00:00
hackerman-kl
3372d8f102 module-avb: descriptor: moving to endstation builder (i.e es_builder) 2025-11-21 08:26:15 +00:00
hackerman-kl
55e4c7e4cb module-avb: descriptor: adding todo for simplification 2025-11-21 08:26:15 +00:00
hackerman-kl
57af462ecf modules-avb: Introducing entity builder. The entity builder is necessary
to attach ressource to the descriptors instead of having them splitted.

It is the case for the avb-streams which in a seperated list. Instead they
should be encapsulated within the descriptor itself, as one cannot leave
without the other.
2025-11-21 08:26:15 +00:00
hackerman-kl
8ea56477d9 module-avb: aecp-aem-descriptors: adding upper limit 2025-11-21 08:26:15 +00:00
hackerman-kl
f2093a3f76 module-avb: mrp: fix leavall timer issue, introducing lva state machine 2025-11-21 08:24:44 +00:00
hackerman-kl
03428f3380 module-avb: mrp: add human readable m*rp states 2025-11-21 08:24:44 +00:00
Frédéric Danis
5d39e1357e spa: bluez: backend-native: Fix +CNUM reply
PTS test HFP/AG/NUM/BV-01-C expects to receive 5 items, the last one
representing service type which could be 4 for voice or 5 for fax.
2025-11-21 08:22:55 +00:00
filmsi
99dbd109ce Replace sl.po with updated translation 2025-11-21 08:22:02 +00:00
Daniel Tang
a7735677ae systemd: Bind pipewire-pulse to pipewire
Like filter-chain.service. No point in keeping pipewire-pulse.service
unusably running when pipewire.service is masked for a non-interactive
user.
2025-11-21 08:21:22 +00:00
Barnabás Pőcze
ae7ac460b9 spa: libcamera: source: ignore frame rate
Do not parse, and do not report the negotiated frame rate. It is
completely ignored. Until support for the `FrameDurationLimits`
libcamera control is implemented, ignore any and all frame rates.
2025-11-21 08:20:30 +00:00
Barnabás Pőcze
ead1c144b2 spa: libcamera: source: remove port::memtype
This field is only used during the setup of buffers, so it can be
transformed into a local variable, but even that is not needed
because the data type can be chosen on a per-plane basis.
2025-11-21 08:20:30 +00:00
Barnabás Pőcze
207421cb7b spa: libcamera: source: remove SPA_VIDEO_FORMAT_UNKNOWN check
It is not used anywhere, so remove it.
2025-11-21 08:20:30 +00:00
Wim Taymans
f4efb37b03 doc: update docs with new resampler parameters 2025-11-20 17:44:58 +01:00
Wim Taymans
df075e6628 resample: tweak kaiser resample defaults
Ensure a 110 dB stopband. Set the alpha values accordingly.
2025-11-20 16:55:09 +01:00
Wim Taymans
faf4641625 resample: add some more options to spa-resample
Make it possible to set the params by name. Dump the resampler config
in verbose mode.
2025-11-20 14:07:26 +01:00
Wim Taymans
e8268969ea resample: update params by name
Make the params introspectable, use the names to configure them in
audioconvert.

Only use precomputed filters when using the defaults.

Tweak the kaiser filter alpha and n_taps based on values calculated from
stopband attenuation and transition bandwidth.
2025-11-20 14:07:26 +01:00
Wim Taymans
bfd26c98e3 resample: make window configurable
Add kaiser window
2025-11-20 14:07:14 +01:00
Wim Taymans
18ff08243b spa: add spa_pod_memcmp
Add a helper to memcmp two pods and use it in some places.
2025-11-13 18:13:02 +01:00
Wim Taymans
b9a895f825 adapter: don't recheck formats on driver change
We need to leave this to the converter, like we do for audio.
2025-11-13 18:10:11 +01:00
Wim Taymans
dabd2af828 modules: handle NULL stream when getting time
The stream can be destroyed when requesting the time, fall back to
MONOTONIC time then.

Fixes #4970
2025-11-10 18:33:21 +01:00
Jan Grulich
954f76d107 Remove unused <threads.h> header
This was introduced before for some threading implementation to have
definitions for 'tss_t' and 'tss_set()' and similar, but these were
later removed, while the header was still kept.

This header doesn't exist on older systems with older glibc and is the
reason for the only patch we need to have in Chromium to make PipeWire
to build.
2025-11-10 16:58:34 +00:00
Wim Taymans
fb20b96024 filter-graph: support inline convolver IRs
Refactor the IR loading code, Make some generic open/load/close
functions and handle the different filenames in the open call.

This makes it possible to reuse some of the delay and alloc code.

It also makes it easier to add new IRs loading code.

Support /IR:<rate>,<value1>,<value2>,... as an inline IR definition.
2025-11-10 16:42:29 +01:00
Wim Taymans
f322a8b159 filter-graph: support longer filenames
Support longer filenames by not assuming a max length.
2025-11-10 16:42:29 +01:00
Carlos Rafael Giani
60c47e96a8 module-rtp: Change IGMP recovery log line levels from info to debug
The associated cases where IGMP membership is silently dropped may happen
infrequently. But when they do, these log lines can occur at a large
volume. To not spam the logs in such cases, change the log level from
info to debug.

Signed-off-by: Carlos Rafael Giani <crg7475@mailbox.org>
2025-11-10 14:55:21 +01:00
Pauli Virtanen
8df58db415 bluez5: read errqueue also from media-source handler
Flush errqueue for iso-io also from the media-source handler, to avoid
dropping packet tx reports.

This applies to bidirectional streams. The sink/source handlers poll on
different fd, one being dup'd, and epoll does not trigger them in any
specific interleaved order.
2025-11-09 02:34:54 +02:00
Pauli Virtanen
878dd7a0c9 bluez5: default to FL,FR channels for BAP server
As server, it's not possible to expose all locations as supported
because BlueZ only exposes two ASE.

Default to just FL,FR
2025-11-09 01:47:17 +02:00
Pauli Virtanen
567d5181ca bluez5: iso-io: force resync after underrun
If stream underruns we should resync playback position on next cycle as
there is anyway a sound glitch.
2025-11-09 01:45:21 +02:00
Barnabás Pőcze
3413ca9617 spa: bluez: backend-native: fix libusb device leak
`libusb_free_device_list()` should be passed `true` as its second
argument to unreference every single device in the list.

Fixes: 5e0b63b149 ("bluez5: backend-native: use quirks + usb adapter caps for checking msbc")
2025-11-07 12:28:16 +00:00
Barnabás Pőcze
78b6df769b spa: bluez: telephony: reject double registration 2025-11-07 12:28:16 +00:00
Barnabás Pőcze
9a0053a501 spa: bluez: telephony: fix string leaks
`dbus_connection_register_object_path()` does not take ownership of the
path passed to it, so currently the callers `telephony_{ag,call}_register()`
leak a copy of the path string. Fix that by not making an unnecessary copy.

Fixes: b28399ac57 ("bluez5: add telephony D-Bus service implementation")
2025-11-07 12:28:16 +00:00
Barnabás Pőcze
963d10f1ac spa: bluez: mark dbus vtables static 2025-11-07 12:28:16 +00:00
Arun Raghavan
3337af64ca pulse-server: Support clearing default sink/source
The PulseAudio way of doing this is to accept @NONE@ to clear the
default setting, so let's accept that.
2025-11-06 17:23:16 -08:00
512 changed files with 44245 additions and 11483 deletions

View file

@ -410,13 +410,15 @@ build_on_fedora_html_docs:
-Dsndfile=enabled -Dsndfile=enabled
-Dsession-managers=[] -Dsession-managers=[]
before_script: before_script:
- git fetch origin 1.0 1.2 1.4 master - git fetch origin 1.0 1.2 1.4 1.6 master
- git branch -f 1.0 origin/1.0 - git branch -f 1.0 origin/1.0
- git clone -b 1.0 . branch-1.0 - git clone -b 1.0 . branch-1.0
- git branch -f 1.2 origin/1.2 - git branch -f 1.2 origin/1.2
- git clone -b 1.2 . branch-1.2 - git clone -b 1.2 . branch-1.2
- git branch -f 1.4 origin/1.4 - git branch -f 1.4 origin/1.4
- git clone -b 1.4 . branch-1.4 - git clone -b 1.4 . branch-1.4
- git branch -f 1.6 origin/1.6
- git clone -b 1.6 . branch-1.6
- git branch -f master origin/master - git branch -f master origin/master
- git clone -b master . branch-master - git clone -b master . branch-master
- !reference [.build, before_script] - !reference [.build, before_script]
@ -433,6 +435,10 @@ build_on_fedora_html_docs:
- meson setup builddir $MESON_OPTIONS - meson setup builddir $MESON_OPTIONS
- meson compile -C builddir doc/pipewire-docs - meson compile -C builddir doc/pipewire-docs
- cd .. - cd ..
- cd branch-1.6
- meson setup builddir $MESON_OPTIONS
- meson compile -C builddir doc/pipewire-docs
- cd ..
- cd branch-master - cd branch-master
- meson setup builddir $MESON_OPTIONS - meson setup builddir $MESON_OPTIONS
- meson compile -C builddir doc/pipewire-docs - meson compile -C builddir doc/pipewire-docs
@ -658,7 +664,7 @@ doccheck:
- cat pipewire_module_pages - cat pipewire_module_pages
- | - |
for page in $(cat pipewire_module_pages); do for page in $(cat pipewire_module_pages); do
git grep -q -e "\\\subpage $page" || (echo "\\page $page is missing \\subpage entry in doc/pipewire-modules.dox" && false) git grep -q -e "\\\subpage $page" || (echo "\\page $page is missing \\subpage entry in doc/dox/modules.dox" && false)
done done
check_missing_headers: check_missing_headers:
@ -682,12 +688,13 @@ pages:
- job: build_on_fedora_html_docs - job: build_on_fedora_html_docs
artifacts: true artifacts: true
script: script:
- mkdir public public/1.0 public/1.2 public/1.4 public/devel - mkdir public public/1.0 public/1.2 public/1.4 public/1.6 public/devel
- cp -R branch-1.0/builddir/doc/html/* public/1.0/ - cp -R branch-1.0/builddir/doc/html/* public/1.0/
- cp -R branch-1.2/builddir/doc/html/* public/1.2/ - cp -R branch-1.2/builddir/doc/html/* public/1.2/
- cp -R branch-1.4/builddir/doc/html/* public/1.4/ - cp -R branch-1.4/builddir/doc/html/* public/1.4/
- cp -R branch-1.6/builddir/doc/html/* public/1.6/
- cp -R branch-master/builddir/doc/html/* public/devel/ - cp -R branch-master/builddir/doc/html/* public/devel/
- (cd public && ln -s 1.4/* .) - (cd public && ln -s 1.6/* .)
artifacts: artifacts:
paths: paths:
- public - public

206
NEWS
View file

@ -1,3 +1,206 @@
# PipeWire 1.6.0 (2026-02-19)
This is the 1.6 release that is API and ABI compatible with previous
1.4.x releases.
This release contains some of the bigger changes that happened since
the 1.4 release last year, including:
* An LDAC decoder was added for bluetooth.
* SpanDSP for bluetooth packet loss concealment.
* Safe parsing and building of PODs in shared memory.
* Added support for metadata features. This is used to signal that
the sync_timeline metadata supports the RELEASE operation.
* Node commands and events can contain extra user data.
* Support for more compressed format helper functions to create
and parse formats.
* Support for compile time max channels. The max channels was
increased to 128.
* Support for audio channel layouts was added. This makes it possible
to set "audio.layout" = "5.1" instead of the more verbose
audio.position = [ FL, FR, FC, LFE, SL, SR ]
* Support for Capability Params was added. This can be used to
negotiate capabilities on a link before format and buffer
negotiation takes place.
* More HDR colortypes are added.
* Loops now have locking with priority inversion. Most code was adapted
to use the faster locks instead of epoll/eventfd to update shared state.
* Channel position are parsed from EDID data.
* Channel maps are now set on ALSA.
* The resampler now supports configurable window functions such
as blackman and kaiser windows. The phases are now also calculated
with fixed point math, which makes it more accurate.
* Many bluetooth updates and improvements.
* The filter-graph has an ffmpeg and ONNX plugin. The ffmpeg plugin
can run an audio AVFilterGraph. The ONNX plugin can run some models
such as the silero VAD.
* Many AVB updates. Work is ongoing to merge the Milan protocol.
* Support for v0 clients was removed.
* The jack-tunnel module can now autoconnect ports.
* ROC support multitrack layouts now.
* Many RTP updates.
* rlimits can now be set in the config file.
* Thread reset on fork can now be configured. JACK clients expect this
to be disabled.
* node.exclusive is now enforced.
* node.reliable enables reliable transport.
* pw-cat supports sysex and midiclip as well as some more uncompressed
formats. Options were added to set the container and codec formats
as well as list the supported containers, codecs, layouts and channel
names.
* Documentation updates.
## Highlights (since the previous 1.5.85 prerelease)
- Fix a 64 channel limit in the channel mixer.
- Fix an fd leak in pulse-server in some error cases.
- Some small fixes and improvements.
## PipeWire
- Fix Capability leaks.
- Return an error in pw-stream get-time when not STREAMING.
- Set the current time in the driver position before starting.
Some followers might look at it.
## Modules
- Improve default channel handling in module-filter-chain.
- Support source and sink only module-filter-chain.
- Tweak the filter-chain spatializer example gains.
- Handle new snapcast service type. (#5104)
- Implement socket activation without depending on libsystemd.
- Support ipv4 link-local addresses in RAOP and snapcast. (#4830)
- Forward ROC-toolkit logs to pipewire.
## SPA
- Improve default channel handling in filter-graph. (#5084)
- Clamp control values to min/max. (#5088)
- Support mode JBL gaming headsets.
- Handle some SOFA errors and add gain option.
- Really handle more than 64 channels in the channelmixer. (#5118)
- Allow removal in ALSA-udev of ignored cards.
# pulse-server
- Fix mono mixdown query.
- Expose headset autoswitch message.
- Handle EPROTO errors by disconnecting.
- Handle timeouts in play-sample streams. (#5099)
## GStreamer
- Fix crop metadata.
- Fix a race in the buffer release function.
## Tools
- Improve format support and detection in pw-cat.
- Add some more options to pw-cat to list supported containers
and formats. (#5117)
Older versions:
# PipeWire 1.5.85 (2026-01-19)
This is the fifth and hopefully last 1.6 release candidate that
is API and ABI compatible with previous 1.4.x, 1.2.x and 1.0.x
releases.
## Highlights
- Fix errors in older clients that blindly mmap buffer data.
- ALSA channel mapping should work in more cases.
- Fix an overflow in the resampler with some sample rates.
- Device ID negotiation for DMABUF was added.
- The channelmixer can now also downmix most REAR and TOP
channel positions.
- Various small fixes and improvements.
## PipeWire
- Avoid doing flushing invoke in pw-stream. (#5010)
- Fix stream time delay calculations in some cases. (#4957)
- Avoid suspending a node when the ports are busy.
## Modules
- Mode AVB module updates.
- Remove MAPPABLE flag from memptr data in client-node. This avoids
errors in older clients that blindly map buffers. (#5003)
- Don't leak the filter-chain module in module-parametric-equalizer.
(#5045)
- make it possible to run pulse tunnel sink and source with the same
name. (#5079)
## SPA
- The delay filter in filter-graph now has feedback and feedforward
options to make it possible to implement comb and allpass filters.
- The resampler can be compiled with a custom default quality.
- Firewire latency is scaled correctly now. (#4977)
- The mixer will only passthrough dynamic data.
- Fix filter-graph state in audioconvert after flush.
- Set the channel map in ALSA nodes to tell ALSA our channel layout
and avoid wrong channels.
- Fix AVX2 function, make sure they don't run on AVX-only CPUs.
(#5072)
- Fix an overflow and crash when too many phases are used in the
resampler. (#5073)
- Add some more channel downmix positions, mostly REAR and TOP
channels.
## pulse-server
- Add a message to enable mono mixdown.
## Tools
- Support filenames in raw mode in pw-cat. (#5012)
- Enable clipping in spa-resample.
## examples
- Update device ID negotiation examples.
## Docs
- Document the resampler properties better.
# PipeWire 1.5.84 (2025-11-27)
This is the fourth 1.6 release candidate that is API and ABI
compatible with previous 1.4.x, 1.2.x and 1.0.x releases.
Changes since the last pre-release:
## Highlights
- Capabilities wer added to improve negotiation over links.
- The audio resampler now has a configurable window function to better
tune the resampler quality. A kaiser and blackman window was added
and the default parameters were tuned.
- Various small fixes and improvements.
## PipeWire
- Capabilities and PeerCapabilities were added to exchange key/value
pairs between consumer and producer right after a link is made. This
can be used to detect how the negotiation of formats and buffers
should be done.
## Modules
- Avoid segfaults in RTP source. (#4970)
- The AVB module has seen some improvements.
## Pulse-server
- @NONE@ can now be used to clear the default sink/source.
## SPA
- Support longer convolver filenames and also support inline
IRs.
- The audio resampler window function is now selectable and
configurable. A kaiser window and blackman window was added
and the default qualities were tweaked to improve quality.
- The filter-graph convolver latency is now set by default to
something more sensible. (0 by default and N/2 for hilbert).
(#4980)
## Bluetooth
- Better xrun and error handling for iso streams.
- The +CNUM reply was fixed.
- The CIEC call status was fixed. (#1744)
- Add BAP context metadata to improve compatibility.
- Improve compatiblity with Creative Zen Hybrid Pro by releasing
transports simultaneously.
# PipeWire 1.5.83 (2025-11-06) # PipeWire 1.5.83 (2025-11-06)
This is the third 1.6 release candidate that is API and ABI This is the third 1.6 release candidate that is API and ABI
@ -8,9 +211,6 @@ Changes since the last pre-release:
## Highlights ## Highlights
- Include the NEWS and updated version number. - Include the NEWS and updated version number.
Older versions:
# PipeWire 1.5.82 (2025-11-06) # PipeWire 1.5.82 (2025-11-06)
This is the second 1.6 release candidate that is API and ABI This is the second 1.6 release candidate that is API and ABI

View file

@ -44,6 +44,7 @@
<tab type="usergroup" title="PipeWire Versions"> <tab type="usergroup" title="PipeWire Versions">
<tab type="user" url="https://docs.pipewire.org/1.2/" title="1.2.x"/> <tab type="user" url="https://docs.pipewire.org/1.2/" title="1.2.x"/>
<tab type="user" url="https://docs.pipewire.org/1.4/" title="1.4.x"/> <tab type="user" url="https://docs.pipewire.org/1.4/" title="1.4.x"/>
<tab type="user" url="https://docs.pipewire.org/1.6/" title="1.6.x"/>
<tab type="user" url="https://docs.pipewire.org/devel/" title="Development"/> <tab type="user" url="https://docs.pipewire.org/devel/" title="Development"/>
</tab> </tab>
</navindex> </navindex>

View file

@ -59,6 +59,14 @@ stream.properties = {
#node.autoconnect = true #node.autoconnect = true
#resample.disable = false #resample.disable = false
#resample.quality = 4 #resample.quality = 4
#resample.window = exp # blackman kaiser
#resample.cutoff = 0.0
#resample.n-taps = 0
#resample.param.exp.A = 0.0
#resample.param.blackman.alpha = 0.0
#resample.param.kaiser.alpha = 0.0
#resample.param.kaiser.stopband-attenuation = 0.0
#resample.param.kaiser.transition-bandwidth = 0.0
#monitor.channel-volumes = false #monitor.channel-volumes = false
#channelmix.disable = false #channelmix.disable = false
#channelmix.min-volume = 0.0 #channelmix.min-volume = 0.0
@ -72,6 +80,9 @@ stream.properties = {
#channelmix.fc-cutoff = 12000.0 #channelmix.fc-cutoff = 12000.0
#channelmix.rear-delay = 12.0 #channelmix.rear-delay = 12.0
#channelmix.stereo-widen = 0.0 #channelmix.stereo-widen = 0.0
#channelmix.center-level = 0.707106781
#channelmix.surround-level = 0.707106781
#channelmix.lfe-level = 0.5
#channelmix.hilbert-taps = 0 #channelmix.hilbert-taps = 0
#dither.noise = 0 #dither.noise = 0
#dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5 #dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5

View file

@ -450,11 +450,25 @@ Whether the node target may be changed using metadata.
@PAR@ node-prop node.passive = false @PAR@ node-prop node.passive = false
\parblock \parblock
This is a passive node and so it should not keep sinks/sources busy. This property makes the session manager create passive links to the sink/sources. If the node is not otherwise linked (via a non-passive link), the node and the sink it is linked to are idle (and eventually suspended). This can be used to configure the port.passive property for all ports of this node.
Possible values are:
* "out": output ports are passive, They will not make the peers active and active peers will
not make this node active.
* "in": input ports are passive, They will not make the peers active and active peers will
not make this node active.
* "true": A combination in "in" and "out", both input and output ports are passive.
* "out-follow": output ports will not make the peer active but when the peer is activated via
some other way, this node will also become active.
* "in-follow": input ports will not make the peer active but when the peer is activated via
some other way, this node will also become active.
* "follow": A combination of "in-follow" and "out-follow".
This is used for filter nodes that sit in front of sinks/sources and need to suspend together with the sink/source. This is used for filter nodes that sit in front of sinks/sources and need to suspend together with the sink/source.
\endparblock \endparblock
@PAR@ node-prop node.link-group = ID @PAR@ node-prop node.link-group = ID
Add the node to a certain link group. Nodes from the same link group are not automatically linked to each other by the session manager. And example is a coupled stream where you don't want the output to link to the input streams, making a useless loop. Add the node to a certain link group. Nodes from the same link group are not automatically linked to each other by the session manager. And example is a coupled stream where you don't want the output to link to the input streams, making a useless loop.
@ -547,17 +561,139 @@ Below is an explanation of the options that can be tuned in the sample converter
\parblock \parblock
The quality of the resampler. from 0 to 14, the default is 4. The quality of the resampler. from 0 to 14, the default is 4.
The quality of a resampler depends on multiple factors:
1. Anti-Aliasing, how well are unwanted frequencies filtered out. Poor anti-aliasing
will make the original inaudible frequencies audible as distortion and noise.
2. Cutoff frequency. At what frequency the transition band will start. This is the
frequency where the signal will start to fade out. A too low cutoff might remove too
much of the high frequencies and make the sound dull. A too high cutoff might cause
aliasing. The cutoff frequency is usually expressed as a ratio of the Nyquist
frequency, 1.0 being the Nyquist frequency (frequency/2).
3. Transition band length. How quickly the unwanted frequencies are filtered out. A
shorter transition band requires longer filters with more CPU and latency but
causes less aliasing. The transition band length is expressed as a ratio of the
Nyquist frequency.
4. Stopband attenuation. How well the unwanted frequencies are filtered out. This is
usually measured in dB. 96dB is below audible on CD quality audio, 150dB is below
the precision of floating point values.
5. CPU usage. Better anti-aliasing needs longer filters and is therefore more CPU
intensive.
6. Latency. Longer filters have a higher Latency. In real-time application the latency
should be kept as low as possible. The required latency is usually
(filter-length/2)/source-sample-rate, so a resample filter with length of 128 on a
48Khz signal has 1.3ms ((128/2)/48000) of latency.
7. Ringing. A too short transition band length might cause ringing because of how the
sinc filters work. This can sound like flutter on sharp attacks in the audio signal.
Increasing the quality will result in better cutoff and less aliasing at the expense of Increasing the quality will result in better cutoff and less aliasing at the expense of
(much) more CPU consumption. The default quality of 4 has been selected as a good compromise (much) more CPU consumption, latency and more ringing. The default quality of 4 has
between quality and performance with no artifacts that are well below the audible range. been selected as a good compromise between quality and performance with no artifact
s that are well below the audible range.
The default resampler quality for the exp window results in a cutoff of 0.87 and a
filter size of about 48 taps. It has a Stopband attenuation of about 150 dB.
See [Infinite Wave](https://src.infinitewave.ca/) for a comparison of the performance. See [Infinite Wave](https://src.infinitewave.ca/) for a comparison of the performance.
You can tune the resampler in a variaty of ways:
* Tune the cutoff frequency. Increase the cutoff to preserve more high frequencies at the
expense of more aliasing.
* Tune the transition band. Reduce the transition band to better filter out the frequencies
around the cutoff frequency and get less aliasing at the expense of more ringing, CPU and
Latency. This can be done by increasing the number of taps.
* Tune the stopband attenuation. Increase the attenuation to reduce aliasing at the expense
of a wider transition band. This can only be done on the kaiser window, the exp and
blackman window have 150dB and 96dB attenuation respecively.
\endparblock \endparblock
@PAR@ node-prop resample.disable = false @PAR@ node-prop resample.disable = false
Disable the resampler entirely. The node will only be able to negotiate with the graph Disable the resampler entirely. The node will only be able to negotiate with the graph
when the samplerates are compatible. when the samplerates are compatible.
@PAR@ node-prop resample.window = exp
\parblock
The resampler window function to use. By default an exponential window function is used
that gives a good balance between complexitiy and quality.
You can also specify a blackman or kaiser window, both with different tradeoffs. The
kaiser window has some extra tunable parameters for the specific use cases.
\endparblock
@PAR@ node-prop resample.cutoff = 0.0
\parblock
The resampler cutoff frequency. This is a value between 0.0 and 1.0. A value of 0.0 will
use a predefined value based on the resampler quality.
A higher cutoff value will preserve more high frequency content but depending on the
size of the transition band will cause more aliasing.
The default quality 4 setting for all windows is 0.87.
\endparblock
@PAR@ node-prop resample.n-taps = 0
\parblock
The resampler number of taps. A value of 0 will use a predefined value based on
the resampler quality or other window function parameters.
A higher number of taps will use more CPU, Latency and cause more ringing but will
reduce aliasing.
The default quality setting for the exp window is 48.
\endparblock
@PAR@ node-prop resample.param.exp.A = 0.0
\parblock
The A parameter for the exponential window function. A value of 0.0 will use a predefined
value based on the quality when the exp window is in use.
The default setting for the exp window is 16.97789.
\endparblock
@PAR@ node-prop resample.param.blackman.alpha = 0.0
\parblock
The alpha value of the blackman function. A value of 0.0 will use a predefined
value based on the quality when the blackman window is in use.
The default quality setting for the blackman window is 0.16.
\endparblock
@PAR@ node-prop resample.param.kaiser.stopband-attenuation = 0.0
\parblock
The kaiser window stopband attenuation parameter in dB. A default value of 0.0 will use a
predefined value based on the quality.
A higher value will filter out more of the unwanted frequencies and reduce aliasing at the
expense of a larger transition band. A value of 96dB is below the dynamic range of CD quality
audio. 150dB is the limit of the precision of the resampler.
The default quality setting for the kaiser window is 130.000000.
\endparblock
@PAR@ node-prop resample.param.kaiser.transition-bandwidth = 0.0
\parblock
The kaiser window transition bandwidth parameter. A default value of 0.0 will use a
predefined value based on the quality.
A smaller transition band will cause a steeper cutoff with less unwanted frequencies
in the final signal at the expense of more a larger filter and more CPU usage and
latency. A smaller transition band can also cause more ringing.
The default quality setting for the kaiser window is 0.177032
\endparblock
@PAR@ node-prop resample.param.kaiser.alpha = 0.0
\parblock
The kaiser window alpha parameter. A default value of 0.0 will calculate an alpha value
based on the stopband-attenuation and transition-bandwidth parameters.
This value is usually calculated from the other parameters but can be set explicitly
with this property.
The default quality setting for the kaiser window is 4.254931.
\endparblock
## Channel Mixer Parameters ## Channel Mixer Parameters
Source, sinks, capture and playback streams can apply channel mixing on the incoming signal. Source, sinks, capture and playback streams can apply channel mixing on the incoming signal.
@ -647,6 +783,15 @@ more to the center speaker and leaves the ambient sound in the stereo channels.
This is only active when up-mix is enabled and a Front Center channel is mixed. This is only active when up-mix is enabled and a Front Center channel is mixed.
\endparblock \endparblock
@PAR@ node-prop channelmix.center-level = 0.707106781
The level of the center channel when up/downmixing.
@PAR@ node-prop channelmix.surround-level = 0.707106781
The level of the surround channels when up/downmixing.
@PAR@ node-prop channelmix.lfe-level = 0.5
The level of the LFE channel when up/downmixing.
@PAR@ node-prop channelmix.hilbert-taps = 0 @PAR@ node-prop channelmix.hilbert-taps = 0
\parblock \parblock
This option will apply a 90 degree phase shift to the rear channels to improve specialization. This option will apply a 90 degree phase shift to the rear channels to improve specialization.
@ -1018,6 +1163,15 @@ HFP/HSP backend (default: native). Available values: any, none, hsphfpd, ofono,
@PAR@ monitor-prop bluez5.hfphsp-backend-native-modem # string @PAR@ monitor-prop bluez5.hfphsp-backend-native-modem # string
@PAR@ monitor-prop bluez5.hfphsp-backend-native-pts # boolean
Enable specific workarounds for Bluetooth qualification.
@PAR@ monitor-prop bluez5.disable-dummy-call # boolean
By default a call status event is sent on audio stream connection/disconnection to
workaround some headset timeout disconnection when the HFP HF is used by another
application than telephony one, e.g. a conference application/website.
This prevent to send this event.
@PAR@ monitor-prop bluez5.dummy-avrcp player # boolean @PAR@ monitor-prop bluez5.dummy-avrcp player # boolean
Register dummy AVRCP player. Some devices have wrongly functioning Register dummy AVRCP player. Some devices have wrongly functioning
volume or playback controls if this is not enabled. Default: false volume or playback controls if this is not enabled. Default: false
@ -1040,6 +1194,15 @@ in a platform-specific way. See `tests/examples/bt-pinephone.lua` in WirePlumber
Do not enable this setting if you don't know what all this means, as it won't work. Do not enable this setting if you don't know what all this means, as it won't work.
\endparblock \endparblock
@PAR@ monitor-prop bluez5.hw-offload-datapath # integer
\parblock
HFP/HSP hardware offload data path ID (default: 0).
This feature configures the SCO hardwareoffload data path for HFP/HSP using the Bluetooth
SIGspecified procedure. It is intended for advanced setups and vendor integrations. Do not
edit this unless required; incorrect values can disable SCO offload.
\endparblock
@PAR@ monitor-prop bluez5.a2dp.opus.pro.channels = 3 # integer @PAR@ monitor-prop bluez5.a2dp.opus.pro.channels = 3 # integer
PipeWire Opus Pro audio profile channel count. PipeWire Opus Pro audio profile channel count.
@ -1071,6 +1234,7 @@ PipeWire Opus Pro audio profile duplex max bitrate.
PipeWire Opus Pro audio profile duplex frame duration (1/10 ms). PipeWire Opus Pro audio profile duplex frame duration (1/10 ms).
@PAR@ monitor-prop bluez5.bcast_source.config = [] # JSON @PAR@ monitor-prop bluez5.bcast_source.config = [] # JSON
For a per-adapter configuration of multiple BIGs use an "adapter" entry in the BIG with the BD address.
\parblock \parblock
Example: Example:
``` ```
@ -1127,6 +1291,18 @@ Available sink contexts PACS bitmask of the the server.
@PAR@ monitor-prop bluez5.bap-server-capabilities.sink.supported-contexts # integer @PAR@ monitor-prop bluez5.bap-server-capabilities.sink.supported-contexts # integer
Supported sink contexts PACS bitmask of the the server. Supported sink contexts PACS bitmask of the the server.
@PAR@ monitor-prop bluez5.bap-server-capabilities.sink.delay-min # integer
Minimum presentation delay supported, in microseconds.
@PAR@ monitor-prop bluez5.bap-server-capabilities.sink.delay-max # integer
Maximum presentation delay supported, in microseconds.
@PAR@ monitor-prop bluez5.bap-server-capabilities.sink.preferred-delay-min # integer
Minimum preferred presentation delay supported, in microseconds.
@PAR@ monitor-prop bluez5.bap-server-capabilities.sink.preferred-delay-max # integer
Maximum preferred presentation delay supported, in microseconds.
@PAR@ monitor-prop bluez5.bap-server-capabilities.source.locations # JSON or integer @PAR@ monitor-prop bluez5.bap-server-capabilities.source.locations # JSON or integer
Source audio locations of the server, as channel positions or PACS bitmask. Source audio locations of the server, as channel positions or PACS bitmask.
Example: `FL,FR` Example: `FL,FR`
@ -1137,6 +1313,30 @@ Available source contexts PACS bitmask of the the server.
@PAR@ monitor-prop bluez5.bap-server-capabilities.source.supported-contexts # integer @PAR@ monitor-prop bluez5.bap-server-capabilities.source.supported-contexts # integer
Supported source contexts PACS bitmask of the the server. Supported source contexts PACS bitmask of the the server.
@PAR@ monitor-prop bluez5.bap-server-capabilities.source.delay-min # integer
Minimum presentation delay supported, in microseconds.
@PAR@ monitor-prop bluez5.bap-server-capabilities.source.delay-max # integer
Maximum presentation delay supported, in microseconds.
@PAR@ monitor-prop bluez5.bap-server-capabilities.source.preferred-delay-min # integer
Minimum preferred presentation delay supported, in microseconds.
@PAR@ monitor-prop bluez5.bap-server-capabilities.source.preferred-delay-max # integer
Maximum preferred presentation delay supported, in microseconds.
@PAR@ monitor-prop bluez5.bap-server-tmap-features = null # array of string
Override advertised TMAP service features. See TMAP specification for their meaning.
Possible values: "cg", "ct", "ums", "umr", "bms", "bmr".
Default: none.
@PAR@ monitor-prop bluez5.bap-server-gmap-features = null # array of string
Override advertised GMAP service features. See GMAP specification for their meaning.
Possible values: "ugg", "ugt", "bgs", "bgr", "ugg-multiplex", "ugg-96kbps-source", "ugg-multisink",
"ugt-source", "ugt-80kbps-source", "ugt-sink", "ugt-64kbps-sink", "ugt-multiplex", "ugt-multisink",
"ugt-multisource", "bgs-96kbps", "bgr-multisink", "bgr-multiplex".
Default: none.
## Device properties ## Device properties
@PAR@ device-prop bluez5.auto-connect # boolean @PAR@ device-prop bluez5.auto-connect # boolean
@ -1196,6 +1396,12 @@ BAP QoS framing that needs to be applied for vendor defined preset
This property is experimental. This property is experimental.
Default: as per QoS preset. Default: as per QoS preset.
@PAR@ device-prop bluez5.bap.force-target-latency = "balanced" # string
BAP QoS target latency profile forced for QoS configuration selection.
If not set or set to "balanced", both low-latency and high-reliability QoS configuration table are used.
This property is experimental.
Available: low-latency, high-reliability, balanced
## Node properties ## Node properties
@PAR@ node-prop bluez5.media-source-role # string @PAR@ node-prop bluez5.media-source-role # string
@ -1217,6 +1423,10 @@ Latency adjustment to apply on the node. Larger values add a
constant latency, but reduces timing jitter caused by Bluetooth constant latency, but reduces timing jitter caused by Bluetooth
transport. transport.
@PAR@ node-prop bluez5.debug.iso-mono = false # boolean
Debugging option for forcing ISO sinks send identical packets out for
all streams.
# PORT PROPERTIES @IDX@ props # PORT PROPERTIES @IDX@ props
Port properties are usually not directly configurable via PipeWire Port properties are usually not directly configurable via PipeWire
@ -1233,6 +1443,11 @@ them. Below are some port properties may interesting for users:
\copydoc PW_KEY_PORT_ALIAS \copydoc PW_KEY_PORT_ALIAS
\endparblock \endparblock
@PAR@ port-prop port.passive # string
\parblock
\copydoc PW_KEY_PORT_PASSIVE
\endparblock
\see pw_keys in the API documentation for a full list. \see pw_keys in the API documentation for a full list.
# LINK PROPERTIES @IDX@ props # LINK PROPERTIES @IDX@ props

View file

@ -93,6 +93,9 @@ stream.properties = {
#channelmix.fc-cutoff = 12000.0 #channelmix.fc-cutoff = 12000.0
#channelmix.rear-delay = 12.0 #channelmix.rear-delay = 12.0
#channelmix.stereo-widen = 0.0 #channelmix.stereo-widen = 0.0
#channelmix.center-level = 0.707106781
#channelmix.surround-level = 0.707106781
#channelmix.lfe-level = 0.5
#channelmix.hilbert-taps = 0 #channelmix.hilbert-taps = 0
#dither.noise = 0 #dither.noise = 0
#dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5 #dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5

View file

@ -6,31 +6,55 @@ The PipeWire server configuration file
# SYNOPSIS # SYNOPSIS
*$PIPEWIRE_CONFIG_DIR/pipewire/pipewire.conf*
*$XDG_CONFIG_HOME/pipewire/pipewire.conf* *$XDG_CONFIG_HOME/pipewire/pipewire.conf*
*$(PIPEWIRE_CONFIG_DIR)/pipewire.conf* *$(PIPEWIRE_CONFIG_DIR)/pipewire.conf*
*$(PIPEWIRE_CONFDATADIR)/pipewire.conf* *$(PIPEWIRE_CONFDATADIR)/pipewire.conf*
*$PIPEWIRE_CONFIG_DIR/pipewire/pipewire.conf.d/*
*$(PIPEWIRE_CONFDATADIR)/pipewire.conf.d/* *$(PIPEWIRE_CONFDATADIR)/pipewire.conf.d/*
*$(PIPEWIRE_CONFIG_DIR)/pipewire.conf.d/* *$(PIPEWIRE_CONFIG_DIR)/pipewire.conf.d/*
*$XDG_CONFIG_HOME/pipewire/pipewire.conf.d/* *$XDG_CONFIG_HOME/pipewire/pipewire.conf.d/*
# DESCRIPTION # DESCRIPTION
PipeWire is a service that facilitates sharing of multimedia content PipeWire is a service that facilitates sharing of multimedia content
between devices and applications. between devices and applications.
On startup, the daemon reads a main configuration file to configure On startup, the daemon reads a main configuration file to configure
itself. It executes a series of commands listed in the config file. itself. After that it will apply a set of drop-in config overrides.
The config file is looked up in the order listed in the By default the pipewire daemon will read `pipewire.conf` and looks
[SYNOPSIS](#synopsis). The environment variables `PIPEWIRE_CONFIG_DIR`, for drop-in config files in the `pipewire.conf.d` subdirectory.
`PIPEWIRE_CONFIG_PREFIX` and `PIPEWIRE_CONFIG_NAME` can be used to The environment variable `PIPEWIRE_CONFIG_NAME` can be used to specify
specify an alternative config directory, subdirectory and file an alternative config file name and the drop-in config file directory
respectively. name (by appending `.d` to the config name).
The config file and the drop-in config directories are looked up in
the order listed in the [SYNOPSIS](#synopsis).
If the environment variable `PIPEWIRE_CONFIG_DIR` is set, it is the
only place where the main config file and the drop-in config files
are looked up.
If the environment variable `PIPEWIRE_CONFIG_PREFIX` contains an
absolute path, it will be searched first for the main config file and
the drop-in config files.
If `PIPEWIRE_CONFIG_PREFIX` does not contain an absolute path, it is
used as a subdirectory to locate the main config file and the drop-in
config files.
When the environment variable `PIPEWIRE_NO_CONFIG` is set to `true`,
only the data config directory $(PIPEWIRE_CONFDATADIR) is searched.
This is useful when you want to run with the default configuration
without any system or user overrides.
Other PipeWire configuration files generally follow the same lookup Other PipeWire configuration files generally follow the same lookup
logic, replacing `pipewire.conf` with the name of the particular logic, replacing `pipewire.conf` with the name of the particular
@ -38,11 +62,19 @@ config file.
# DROP-IN CONFIGURATION FILES @IDX@ pipewire.conf # DROP-IN CONFIGURATION FILES @IDX@ pipewire.conf
The config file drop-in overrides are looked up in the order
of the `pipewire.conf.d` directories listed in the[SYNOPSIS](#synopsis).
All `*.conf` files in the `pipewire.conf.d/` directories are loaded All `*.conf` files in the `pipewire.conf.d/` directories are loaded
and merged into the configuration. Dictionary sections are merged, in alphabetical order and merged into the main configuration.
overriding properties if they already existed, and array sections are Dictionary sections are merged, overriding properties if they already
appended to. The drop-in files have same format as the main existed, and array sections are appended to. The drop-in files have
configuration file, but only contain the settings to be modified. same format as the main configuration file, but only contain the
settings to be modified.
If a config overrides with the same name exists in multiple `conf.d`
directories, the one from the last directory in the above search order
is used.
As the `pipewire.conf` configuration file contains various parts As the `pipewire.conf` configuration file contains various parts
that must be present for correct functioning, using drop-in files that must be present for correct functioning, using drop-in files
@ -256,6 +288,12 @@ Default video rate denominator
@PAR@ pipewire.conf library.name.system = support/libspa-support @PAR@ pipewire.conf library.name.system = support/libspa-support
The name of the shared library to use for the system functions for the main thread. The name of the shared library to use for the system functions for the main thread.
@PAR@ pipewire.conf library.use-fallback = true
When a plugin feature is not listed in context.spa-libs, a predefined fallback
plugin location is used by default. If this option is set to false, only plugin
features explicitly listed in context.spa-libs will be loadable. This can be used
to lock down what plugins that can be loaded.
@PAR@ pipewire.conf link.max-buffers = 64 @PAR@ pipewire.conf link.max-buffers = 64
The maximum number of buffers to negotiate between nodes. Note that version < 3 clients The maximum number of buffers to negotiate between nodes. Note that version < 3 clients
can only support 16 buffers. More buffers is almost always worse than less, latency can only support 16 buffers. More buffers is almost always worse than less, latency
@ -326,6 +364,8 @@ factory-name and the plugin where the factory can be found.
Factory names can contain a wildcard to group several related factories into one Factory names can contain a wildcard to group several related factories into one
plugin. The plugin is loaded from the first matching factory-name. plugin. The plugin is loaded from the first matching factory-name.
A special `blocked` value for the plugin disables the factory-name.
## Example ## Example
``` ```
@ -342,6 +382,7 @@ context.spa-libs = {
api.jack.* = jack/libspa-jack api.jack.* = jack/libspa-jack
support.* = support/libspa-support support.* = support/libspa-support
video.convert.* = videoconvert/libspa-videoconvert video.convert.* = videoconvert/libspa-videoconvert
#filter.graph = blocked
} }
``` ```

View file

@ -301,5 +301,118 @@ they complete processing the buffer. This can be done in the hardware as part of
the render pipeline or explicitly with drmSyncobjTimelineSignal() on the release the render pipeline or explicitly with drmSyncobjTimelineSignal() on the release
handle and the release_point of the metadata. handle and the release_point of the metadata.
# Device ID negotation
The DRM device used for accessing or allocating DMA-BUFs by default undefined. To
negotiate what DRM device should be used, and to advertise what DRM device is associated
with what combination of format and list of modifiers, device ID negotiation can be
performed.
## Capability discovery
Device ID negotiation needs explicit support by both end points of a stream, thus, the
first step of negotiation is discovering whether other peer has support for it. This is
done by advertising a \ref SPA_PARAM_Capability with the key \ref
PW_CAPABILITY_DEVICE_ID_NEGOTIATION and value `1` which corresponds to the
current negotiation API version.
```
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1")));
```
To do this, when connecting to the stream, the \ref PW_STREAM_FLAG_INACTIVE flag must be
passed, and a bare minimum video format parameter must be sent. A bare minimum video
format parameter must contain a \ref SPA_FORMAT_mediaType with \ref SPA_MEDIA_TYPE_video;
\ref SPA_FORMAT_mediaSubtype with \ref SPA_MEDIA_SUBTYPE_raw, \ref SPA_FORMAT_VIDEO_format
with something appropriate, and a \ref SPA_FORMAT_VIDEO_size with a rectangle or range of
rectangles. For example
```
spa_pod_builder_push_object(&b, &f, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(&b, SPA_FORMAT_mediaType,
SPA_POD_Id (SPA_MEDIA_TYPE_video),
0);
spa_pod_builder_add(&b, SPA_FORMAT_mediaSubtype,
SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw),
0);
spa_pod_builder_add(&b, SPA_FORMAT_VIDEO_format,
SPA_POD_Id(SPA_VIDEO_FORMAT_RGBA),
0);
spa_pod_builder_add(&b, SPA_FORMAT_VIDEO_size,
SPA_POD_CHOICE_RANGE_Rectangle(
&SPA_RECTANGLE(320, 240),
&SPA_RECTANGLE(1,1),
&SPA_RECTANGLE(8192, 8192)),
0);
params[n_params++] = spa_pod_builder_pop(&b, &f);
```
After having received the first \ref SPA_PARAM_PeerCapability param, if it contained the
\ref PW_CAPABILITY_DEVICE_ID_NEGOTIATION set to a supported API version number, the full
set of formats can be sent using \ref pw_stream_update_params following by activating the
stream usina supported API version numberstream_set_active(stream, true)`.
Note that the first \ref SPA_PARAM_Format received may be the result of the initial format
negotian with bare minimum parameters, and will be superseded by the result of the format
negotiation of the later provided format parameters.
## Device subset capability
A producer can advertise what devices it's capable of performing device ID negotiation
with. This can be used to reduce the amount of devices that are queried for format
metadata, which can be a time consuming task, if devices needs to be woken up.
To achieve this, the consumer adds another \ref SPA_PARAM_PeerCapability item with the key
\ref PW_CAPABILITY_DEVICE_IDS set to a JSON object describing what device IDs are supported.
This JSON object as of version 1 contains a single key "available-devices" that contain
a list of hexadecimal encoded `dev_t` device IDs.
```
char *device_ids = "{\"available-devices\": [\"6464000000000000\",\"c8c8000000000000\"]}";
&SPA_DICT_ITEMS(
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1"),
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_IDS, device_ids)));
```
## Device ID aware formats
When both the source and the sink advertises support for device ID negotation, format
entries must have a device ID set.
A device ID is represented as a byte array containing a `dev_t` (see `man 3type dev_t`)
device identifier.
Each format with an associated DRM device (i.e. including all but not only formats with
modifiers) must have a \ref SPA_FORMAT_VIDEO_deviceId entry with the \ref
SPA_POD_PROP_FLAG_MANDATORY set. E.g.
```
dev_t device_id;
...
spa_pod_builder_prop(&b, SPA_FORMAT_VIDEO_deviceId,
SPA_POD_PROP_FLAG_MANDATORY);
spa_pod_builder_bytes(&b, &device_id, sizeof device_id);
```
## Negotiated device ID
Once a format is finally negotiated, the resulting device ID is exposed as a \ref
SPA_FORMAT_VIDEO_deviceId and may be retrieved by e.g.
```
device_prop = spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_deviceId);
spa_pod_parse_object(format,
SPA_TYPE_OBJECT_Format, NULL,
SPA_FORMAT_VIDEO_deviceId,
SPA_POD_OPT_Bytes(&bytes, &size));
```
This device should be used for accessing the associated DMA-BUFs, by e.g. creating a
`EGLDisplay` using `EGL_PLATFORM_DEVICE_EXT`.
*/ */

View file

@ -32,8 +32,11 @@ updated as follows:
- \ref spa_io_clock::nsec : Must be set to the time (according to the monotonic - \ref spa_io_clock::nsec : Must be set to the time (according to the monotonic
system clock) when the cycle that the driver is about to trigger started. To system clock) when the cycle that the driver is about to trigger started. To
minimize jitter, it is usually a good idea to increment this by a fixed amount minimize jitter, it is usually a good idea to increment this by a computed
amount (instead of sampling a timestamp from the monotonic system clock)
except for when the driver starts and when discontinuities occur in its clock. except for when the driver starts and when discontinuities occur in its clock.
The computed amount can be fixed, or varying over time, for example due to
adjustments made by a DLL (more on that below).
- \ref spa_io_clock::rate : Set to a value that can translate samples to nanoseconds. - \ref spa_io_clock::rate : Set to a value that can translate samples to nanoseconds.
- \ref spa_io_clock::position : Current cycle position, in samples. This is the - \ref spa_io_clock::position : Current cycle position, in samples. This is the
ideal position of the graph cycle (this is explained in greater detail further below). ideal position of the graph cycle (this is explained in greater detail further below).
@ -52,7 +55,7 @@ updated as follows:
some cases, this may actually be in the past relative to nsec, for example, when some cases, this may actually be in the past relative to nsec, for example, when
some internal driver clock experienced a discontinuity. Consider setting the some internal driver clock experienced a discontinuity. Consider setting the
\ref SPA_IO_CLOCK_FLAG_DISCONT flag in such a case. Just like with nsec, to \ref SPA_IO_CLOCK_FLAG_DISCONT flag in such a case. Just like with nsec, to
minimize jitter, it is usually a good idea to increment this by a fixed amount minimize jitter, it is usually a good idea to increment this by a computed amount
except for when the driver starts and when discontinuities occur in its clock. except for when the driver starts and when discontinuities occur in its clock.
The driver node signals the start of the graph cycle by calling \ref spa_node_call_ready The driver node signals the start of the graph cycle by calling \ref spa_node_call_ready
@ -60,6 +63,11 @@ with the \ref SPA_STATUS_HAVE_DATA and \ref SPA_STATUS_NEED_DATA flags passed
to that function call. That call must happen inside the thread that runs the to that function call. That call must happen inside the thread that runs the
data loop assigned to the driver node. data loop assigned to the driver node.
Drivers must make sure that the next cycle is started at the time indicated by
the \ref spa_io_clock::next_nsec timestamp. They do not have to use the monotonic
clock itself for scheduling the next cycle. If for example the internal clock
can directly be used with \c timerfd , the next cycle can be triggered that way.
As mentioned above, the \ref spa_io_clock::position field is the _ideal_ position As mentioned above, the \ref spa_io_clock::position field is the _ideal_ position
of the graph cycle, in samples. This contrasts with \ref spa_io_clock::nsec, which of the graph cycle, in samples. This contrasts with \ref spa_io_clock::nsec, which
is the moment in monotonic clock time when the cycle _actually_ happens. This is an is the moment in monotonic clock time when the cycle _actually_ happens. This is an
@ -103,11 +111,12 @@ expected position (in samples) and the actual position (derived from the current
of the driver's internal clock), passes the delta between these two quantities into the of the driver's internal clock), passes the delta between these two quantities into the
DLL, and the DLL computes a correction factor (2.0 in the above example) which is used DLL, and the DLL computes a correction factor (2.0 in the above example) which is used
for scaling durations between \c timerfd timeouts. This forms a control loop, since the for scaling durations between \c timerfd timeouts. This forms a control loop, since the
correction factor causes the durations between the timeouts to be adjusted such that the correction factor causes the \ref spa_io_clock::next_nsec increments (that is, the
difference between the expected position and the actual position reaches zero. Keep in durations between the timerfd timeouts) to be adjusted such that, over time, the difference
mind the notes above about \ref spa_io_clock::position being the ideal position of the between the expected position and the actual position reaches zero. Keep in mind the
graph cycle, meaning that even in this case, the duration it is incremented by is notes above about \ref spa_io_clock::position being the ideal position of the graph
_not_ scaled by the correction factor; the duration in samples remains unchanged. cycle, meaning that even in this case, the duration it is incremented by is _not_
scaled by the correction factor; the duration in samples remains unchanged.
(Other popular control loop mechanisms that are suitable alternatives to the DLL are (Other popular control loop mechanisms that are suitable alternatives to the DLL are
PID controllers and Kalman filters.) PID controllers and Kalman filters.)

View file

@ -10,6 +10,7 @@
- \subpage page_objects_design - \subpage page_objects_design
- \subpage page_library - \subpage page_library
- \subpage page_dma_buf - \subpage page_dma_buf
- \subpage page_running
- \subpage page_scheduling - \subpage page_scheduling
- \subpage page_driver - \subpage page_driver
- \subpage page_latency - \subpage page_latency

View file

@ -103,7 +103,7 @@ down and upstream.
# Async nodes # Async nodes
When a node has the node.async property set to true, it will be considered an async When a node has the node.async property set to true, it will be considered an async
node and will be scheduled differently, see scheduling.dox. node and will be scheduled differently, see \ref page_scheduling .
A link between a port of an async node and another port (async or not) is called an A link between a port of an async node and another port (async or not) is called an
async link and will have the link.async=true property. async link and will have the link.async=true property.

View file

@ -62,6 +62,13 @@ As of 1.4, SPA_CONTROL_UMP (Universal Midi Packet) is the prefered format
for the MIDI 1.0 and 2.0 messages in the \ref spa_pod_sequence. Conversion for the MIDI 1.0 and 2.0 messages in the \ref spa_pod_sequence. Conversion
to SPA_CONTROL_Midi is performed for legacy applications. to SPA_CONTROL_Midi is performed for legacy applications.
As of 1.7 the prefered format is Midi1 again because most devices and
applications are still Midi1 and conversions between Midi1 and UMP are not
completely transparent in ALSA and PipeWire. UMP in the ALSA sequencer
and consumers must be enabled explicitly. UMP in producers is supported
still and will be converted to Midi1 by all consumers that did not explicitly
enable UMP support.
## The PipeWire Daemon ## The PipeWire Daemon
Nothing special is implemented for MIDI. Negotiation of formats Nothing special is implemented for MIDI. Negotiation of formats
@ -104,13 +111,14 @@ filtering out the \ref SPA_CONTROL_Midi, \ref SPA_CONTROL_OSC and
\ref SPA_CONTROL_UMP types. On output ports, the JACK event stream is \ref SPA_CONTROL_UMP types. On output ports, the JACK event stream is
converted to control messages in a similar way. converted to control messages in a similar way.
Normally, all MIDI and UMP messages are converted to MIDI1 jack events unless Normally, all MIDI and UMP input messages are converted to MIDI1 jack
the JACK port was created with an explcit "32 bit raw UMP" format or with events unless the JACK port was created with an explcit "32 bit raw UMP"
the JackPortIsMIDI2 flag, in which case the raw UMP is passed to the JACK format or with the JackPortIsMIDI2 flag, in which case the messages are
application directly. For output ports, converted to UMP or passed on directly.
the JACK events are assumed to be MIDI1 and converted to UMP unless the port
has the "32 bit raw UMP" format or the JackPortIsMIDI2 flag, in which case For output ports, the JACK events are assumed to be
the UMP messages are simply passed on. MIDI1 unless the port has the "32 bit raw UMP" format or the JackPortIsMIDI2
flag, in which case the control messages are assumed to be UMP.
There is a 1 to 1 mapping between the JACK events and control There is a 1 to 1 mapping between the JACK events and control
messages so there is no information loss or need for complicated messages so there is no information loss or need for complicated

View file

@ -1130,6 +1130,8 @@ follows:
|<----------------------------------------| |<----------------------------------------|
| ClientNode::PortSetMixInfo | mixer inputs for each linked port | ClientNode::PortSetMixInfo | mixer inputs for each linked port
|<----------------------------------------| |<----------------------------------------|
| ClientNode::PortSetParam | PeerCapability of the ports
|<----------------------------------------|
| ClientNode::PortSetParam | Latency of the ports | ClientNode::PortSetParam | Latency of the ports
|<----------------------------------------| |<----------------------------------------|
| ClientNode::SetActivation | activation of port peers | ClientNode::SetActivation | activation of port peers

View file

@ -0,0 +1,393 @@
/** \page page_running Node running
This document tries to explain how the Nodes in a PipeWire graph become
runnable so that they can be scheduled later.
It also describes how nodes are grouped together and scheduled together.
# Runnable Nodes
A runnable node is a node that will participate in the dataflow in the
PipeWire graph.
Not all nodes should participate by default. For example, filters or device
nodes that are not linked to any runnable nodes should not do useless
processing.
Nodes form one or more groups depending on properties and how they are
linked. For each group, one driver is selected to run the group. Inside the
group, all runnable nodes are scheduled by the group driver. If there are no
runnable nodes in a group, the driver is not started.
PipeWire provides mechanisms to precisely describe how and when nodes should
be scheduled and grouped using:
- port properties to control how port links control runnable state
- node properties to control processing
- grouping of nodes and internal links between nodes
# Port passive modes
A Port has 4 passive modes, this depends on the value of the `port.passive` property:
- `false`, the port will make the peer active and an active peer will make this port
active.
- `true`, the port will not make the peer active and an active peer will not make this
port active.
- `follow`, the port will not make the peer active but an active peer will make this
port active.
- `follow-suspend`, the port will only make another follow-suspend peer active but any
active peer will make this port active.
The combination of these 4 modes on the output and input ports of a link results in a
wide range of use cases.
# Node passive modes
A Node can have 10 passive modes, `node.passive` can be set to a comma separated list
of the following values:
- `false`, both input and output ports have `port.passive = false`
- `in`, input ports have `port.passive = true`
- `out`, output ports have `port.passive = true`
- `true`, both input and output ports have `port.passive = true`. This is the same
as `in,out`.
- `in-follow`, input ports have `port.passive = follow`
- `out-follow`, output ports have `port.passive = follow`
- `follow`, input and output ports have `port.passive = follow`. This is the same
as `in-follow,out-follow`.
- `in-follow-suspend`, input ports have `port.passive = follow-suspend`
- `out-follow-suspend`, output ports have `port.passive = follow-suspend`
- `follow-suspend`, input and output ports have `port.passive = follow-suspend`.
This is the same as `in-follow-suspend,out-follow-suspend`.
Nodes by default have the `false` mode but nodes with the `media.class` property
containing `Sink`, `Source` or `Duplex` receive the `follow-suspend` mode by default.
Unless explicitly configured, ports inherit the mode from their parent node.
# Updating the node runnable state
We iterate all nodes A in the graph and look at its peers B.
Based on the port passive modes of the port links we can decide if the nodes are
runnable or not. A link will always make both nodes runnable or none.
The following table decides the runnability of the 2 nodes based on the port.passive
mode of the link between the 2 ports:
```
B-false B-true B-follow B-follow-suspend
A-false X X X X
A-true
A-follow
A-follow-suspend X
Table 1
```
When a node is made runnable, the port passive mode will then decide if the peer ports
should become active as well with the following table.
```
B-false B-true B-follow B-follow-suspend
A-false X X X
A-true X X X
A-follow X X X
A-follow-suspend X X X
Table 2
```
So when A is runnable, all peers are activated except those with `port.passive=true`.
When A is runnable, all the nodes that share the same group or link-group will also
be made runnable.
# Use cases
Let's check some cases that we want to solve with these node and port properties.
## Device nodes
```
+--------+ +--------+
| ALSA | | ALSA |
| Source | | Sink |
| FL FL |
| FR FR |
+--------+ +--------+
```
Unlinked device nodes are supposed to stay suspended when nothing is linked to
them.
```
+----------+ +--------+
| playback | | ALSA |
| | | Sink |
| FL ------ FL |
| FR ------ FR |
+----------+ +--------+
```
An (active) player node linked to a device node should make both nodes runnable.
Device nodes have the `port.passive = follow-suspend` property by default. The
playback node has the `port.passive = false` by default.
If we look at the playback node as A and the sink as B, both nodes will be made
runnable according to Table 1.
The two runnable nodes form a group and will be scheduled together. One of the
nodes of a group with the `node.driver = true` property is selected as the
driver. In the above case, that will be the ALSA Sink.
Likewise, a capture node linked to an ALSA Source should make both nodes runnable.
```
+--------+ +---------+
| ALSA | | capture |
| Source | | |
| FL ------ FL |
| FR ------ FR |
+--------+ +---------+
```
The ALSA Source is now the driver.
Also, linking 2 device nodes together should make them runnable:
```
+--------+ +--------+
| ALSA | | ALSA |
| Source | | Sink |
| FL ----------------------- FL |
| FR ----------------------- FR |
+--------+ +--------+
```
This is the case because in Table 1, the two `port.passive = follow-suspend` ports
from the Source and Sink activate each other.
## Filter nodes
When there is a filter in front of the ALSA Sink, it should not make the filter and
sink runnable.
```
+--------+ +--------+
| filter | | ALSA |
| | | Sink |
FL FL ------ FL |
FR FR ------ FR |
+--------+ +--------+
```
The links between the filter and ALSA Sink are `port.passive = true` and don't make
the nodes runnable.
The filter needs to be made runnable via some other means to also make the ALSA
Sink runnable, for example by linking a playback node:
```
+----------+ +--------+ +--------+
| playback | | filter | | ALSA |
| | | | | Sink |
| FL ------ FL FL ------ FL |
| FR ------ FR FR ------ FR |
+----------+ +--------+ +--------+
```
The input port of the filter is `port.passive = follow-suspend' and so it can be
activated by the playback node.
Likewise, if the ALSA Sink is runnable, it should not automatically make the
filter runnable. For example:
```
+--------+ +--------+
| filter | | ALSA |
| | | Sink |
FL FL ---+-- FL |
FR FR ---|+- FR |
+--------+ || +--------+
||
+----------+ ||
| playback | ||
| | ||
| FL ---+|
| FR ----+
+----------+
```
Here the playback node makes the ALSA Sink runnable but the filter
stays not-runnable because the output port is `port.passive = true`.
## Device node monitor
Consider the case where we have an ALSA Sink and a monitor stream
connected to the sink monitor ports.
```
+-------+ +--------++
| ALSA | | monitor |
| Sink | | |
FL FL ------ FL |
FR FR ------ FR |
+-------+ +---------+
```
We would like to keep the monitor stream and the ALSA sink suspended
unless something else activates the ALSA Sink:
```
+----------+ +-------+ +---------+
| playback | | ALSA | | monitor |
| | | Sink | | |
| FL ------ FL FL ------ FL |
| FR ------ FR FR ------ FR |
+----------+ +-------+ +---------+
```
We can do this by making the monitor stream input ports `port.passive = follow`
and leave the ALSA Sink monitor output ports as `port.passive = follow-suspend`.
According to Table 1, both nodes will not activate each other but when ALSA Sink
becomes runnable because of playback, according to Table 2, the monitor will
become runnable as well.
Note how we need the distinction between `follow` and `follow-suspend` for this
use case.
## Node groups
Normally when an application makes a capture and playback node, both nodes will
be scheduled in different groups, consider:
```
+--------+ +---------+
| ALSA | | capture |
| Source | | |
| FL ------ FL |
| FR ------ FR |
+--------+ +---------+
+----------+ +--------+
| playback | | ALSA |
| | | Sink |
| FL ------ FL |
| FR ------ FR |
+----------+ +--------+
```
Here we see 2 groups with the ALSA Source and ALSA Sink respectively as the
drivers. Depending on the clocks of the nodes, the capture and playback will not
be in sync. They will each run in their own time domain depending on the rate of
the drivers.
When we place a node.group property with the same value on the capture and playback
nodes, they will be grouped together and this whole graph becomes one single group.
Because there are 2 potential drivers in the group, the one with the highest
`priority.driver` property is selected as the driver in the group. The other nodes
in the group (including the other driver) become followers in the group.
When a node becomes runnable, all other nodes with the same node.group property
become runnable as well.
## Node link groups
When we have a filter that is constructed from two nodes, an input and an output
node, we could use the `node.group` property to make sure they are both scheduled
and made runnable together.
```
+--------+ +-------+ +--------+ +-------+
| ALSA | | input | | output | | ALSA |
| Source | | | | | | Sink |
| FL ------ FL -- processing-- FL ------ FL |
| FR ------ FR | | FR ------ FR |
+--------+ +-------+ +--------+ +-------+
```
This would work fine but it does not describe that there is an implicit internal
link between the input and output node. This information is important for the
session manager to avoid linking the output node to the input node and make a
loop.
The `node.link-group` property can be used to both group the nodes together and
descibe that they are internally linked together.
When a node becomes runnable, all other nodes with the same node.link-group property
become runnable as well.
For the 2 node filters, like loopback and filter-chain, the same `port.passive`
property rules apply as for the filter nodes. Note that for the virtual devices,
the Source/Sink nodes will be `follow-suspend` by default and the other node should
be set to `node.passive = true` to make the ports passive.
## Want driver
When there is no driver node in the group, nothing should be scheduled. This can
happen when a playback node is linked to a capture node:
```
+--------+ +---------+
| player | | capture |
| | | |
| FL ----------- FL |
| FR ----------- FR |
+--------+ +---------+
```
None of these nodes is a driver so there is no driver in the group and nothing
will be scheduled.
When one of the nodes has `node.want-driver = true` they are grouped and
scheduled with a random driver node. This is often the driver node with the
highest priority (usually the Dummy-Driver) or otherwise a driver that is already
scheduling some other nodes.
## Always process nodes
A simple node, unlinked to anything should normally not run.
```
+--------+
| player |
| |
| FL
| FR
+--------+
```
When the `node.always-process = true` property is set, the node will however be
made runnable even if unlinked. This is done by adding the node to a random driver.
`node.always-process = true` implies the `node.want-driver = true` property.
## Sync groups
In some cases, you only want to group nodes together depending on some condition.
For example, when the JACK transport is activated, all nodes in the graph should share
the same driver node, regardless of the grouping or linking of the nodes.
This is done by setting the same node.sync-group property on all nodes (by default all
nodes have `node.sync-group = group.sync.0`). When a node sets `node.sync = true` all
the other nodes with the same `node.sync-group` property are grouped together.
This can be used to implement the JACK transport. When the transport is started, the
`node.sync=true` property is set and all nodes join one group with a shared driver
and timing information.
*/

View file

@ -23,6 +23,9 @@ node is scheduled to run.
This document describes the processing that happens in the data processing This document describes the processing that happens in the data processing
thread after the main thread has configured it. thread after the main thread has configured it.
Before scheduling of the node happens, the scheduler will collect a list of
nodes that are runnable, see \ref page_running
# Nodes # Nodes
Nodes are objects with 0 or more input and output ports. Nodes are objects with 0 or more input and output ports.

View file

@ -54,11 +54,11 @@ in the graph. Output Tag objects on output ports are propagated to linked input
ports and input Tag objects on input ports are propagated to linked output ports. ports and input Tag objects on input ports are propagated to linked output ports.
If a port has links with multiple other ports, the Tag objects are merged by If a port has links with multiple other ports, the Tag objects are merged by
appending the dictionaties to the Tag param. Intermediate nodes or sinks are allowed appending the dictionaries to the Tag param. Intermediate nodes or sinks are allowed
to take the multiple dictionaries in a Tag and combine them into one dictionary if to take the multiple dictionaries in a Tag and combine them into one dictionary if
they would like to do so. they would like to do so.
This way, Output Tag always describes the aggragated total upstream metadata of This way, Output Tag always describes the aggregated total upstream metadata of the
signal up to the port and Input tag describes the aggregated downstream metadata signal up to the port and Input tag describes the aggregated downstream metadata
of the signal from the port. of the signal from the port.

View file

@ -81,6 +81,7 @@ List of known modules:
- \subpage page_module_raop_discover - \subpage page_module_raop_discover
- \subpage page_module_roc_sink - \subpage page_module_roc_sink
- \subpage page_module_roc_source - \subpage page_module_roc_source
- \subpage page_module_scheduler_v1
- \subpage page_module_rtp_sap - \subpage page_module_rtp_sap
- \subpage page_module_rtp_sink - \subpage page_module_rtp_sink
- \subpage page_module_rtp_source - \subpage page_module_rtp_source
@ -90,6 +91,8 @@ List of known modules:
- \subpage page_module_spa_node_factory - \subpage page_module_spa_node_factory
- \subpage page_module_spa_device - \subpage page_module_spa_device
- \subpage page_module_spa_device_factory - \subpage page_module_spa_device_factory
- \subpage page_module_sendspin_recv
- \subpage page_module_sendspin_send
- \subpage page_module_session_manager - \subpage page_module_session_manager
- \subpage page_module_snapcast_discover - \subpage page_module_snapcast_discover
- \subpage page_module_vban_recv - \subpage page_module_vban_recv

View file

@ -77,7 +77,7 @@ Certain properties are, by convention, expected for specific object types.
Each object type has a list of methods that it needs to implement. Each object type has a list of methods that it needs to implement.
The session manager is responsible for defining the list of permissions each client has. Each permission entry is an object ID and four flags. The four flags are: The session manager is responsible for defining the list of permissions each client has. Each permission entry is an object ID and five flags. The five flags are:
- Read: the object can be seen and events can be received; - Read: the object can be seen and events can be received;
- Write: the object can be modified, usually through methods (which requires the execute flag) - Write: the object can be modified, usually through methods (which requires the execute flag)
@ -109,7 +109,7 @@ Modules in PipeWire can only be loaded in their own process. A client, for examp
Nodes are the core data processing entities in PipeWire. Nodes are the core data processing entities in PipeWire.
They may produce data (capture devices, signal generators, ...), consume data (playback devices, network endpoints, ...) or both (filters). They may produce data (capture devices, signal generators, ...), consume data (playback devices, network endpoints, ...) or both (filters).
Notes have a method `process`, which eats up data from input ports and provides data for each output port. Nodes have a method `process`, which eats up data from input ports and provides data for each output port.
#### Ports #### Ports

View file

@ -4,6 +4,7 @@ Manual pages:
- \subpage page_man_pipewire_1 - \subpage page_man_pipewire_1
- \subpage page_man_pipewire-pulse_1 - \subpage page_man_pipewire-pulse_1
- \subpage page_man_pw-audioconvert_1
- \subpage page_man_pw-cat_1 - \subpage page_man_pw-cat_1
- \subpage page_man_pw-cli_1 - \subpage page_man_pw-cli_1
- \subpage page_man_pw-config_1 - \subpage page_man_pw-config_1

View file

@ -0,0 +1,62 @@
\page page_man_pw-audioconvert_1 pw-audioconvert
The PipeWire audioconvert utility
# SYNOPSIS
**pw-audioconvert** \[*OPTIONS*\] *INFILE* *OUTFILE*
# DESCRIPTION
Use the PipeWire audioconvert to convert input file to output file,
following the given options.
This is useful only for doing audio conversion but also apply effects
on the audio using a filter-graph.
It understands all audio file formats supported by `libsndfile` for input
and output. The filename extension is used to guess the output file
container and format with the WAV file format as the default.
# OPTIONS
\par -r RATE | \--rate=RATE
Output sample rate. Default the same as the input sample rate.
\par -f FORMAT | \--format=FORMAT
Output sample format (s8 | s16 | s32 | f32 | f64). Default the same
as the input format.
\par -b BLOCKSIZE | \--blocksize=BLOCKSIZE
Number of samples per iteration (default 4096)
\par -P PROPERTIES | \--properties=PROPERTIES
Set extra stream properties as a JSON object. One can also use @filename to
read the JSON object with properties from filename.
\par -c CHANNELS | \--channels=CHANNELS
The number of output channels, default the same as the input.
\par \--channel-map=VALUE
The channelmap. Possible values include are either a predefined channel layout
such as **Mono**, **Stereo**, **2.1**, **Quad**, **2.2**, **5.1**,
or comma separated array of channel names such as **FL,FR**.
\par -h
Show help.
\par -v
Verbose operation.
# EXAMPLES
**pw-audioconvert** -r 48000 -f s32 in.wav out.wav
# AUTHORS
The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
PipeWire is available from <$(PACKAGE_URL)>
# SEE ALSO
\ref page_man_pipewire_1 "pipewire(1)"

View file

@ -24,9 +24,9 @@ Play and record media with PipeWire
**pw-cat** is a simple tool for playing back or capturing raw or encoded **pw-cat** is a simple tool for playing back or capturing raw or encoded
media files on a PipeWire server. It understands all audio file formats media files on a PipeWire server. It understands all audio file formats
supported by `libsndfile` for PCM capture and playback. When capturing supported by `libsndfile` for PCM capture and playback. When no container
PCM, the filename extension is used to guess the file format with the is specified for capturing PCM, the filename extension is used to guess
WAV file format as the default. the file format with the WAV file format as the default.
It understands standard MIDI files and MIDI 2.0 clip files for playback It understands standard MIDI files and MIDI 2.0 clip files for playback
and recording. This tool will not render MIDI files, it will simply make and recording. This tool will not render MIDI files, it will simply make
@ -37,8 +37,15 @@ DSD playback is supported with the DSF file format. This tool will only
work with native DSD capable hardware and will produce an error when no work with native DSD capable hardware and will produce an error when no
such hardware was found. such hardware was found.
When the *FILE* is - input and output will be raw data from STDIN and When the *FILE* is - input will be from STDIN. If no format is specified,
STDOUT respectively. libsndfile will attempt to parse and stream the format from STDIN. For
some formats, this is not possible and libsndfile will give an error.
Raw, MIDI and DSD formats are all streamable from STDIN.
When the *FILE* is - output will be to STDOUT. If no format is specified,
libsndfile is instructed to output the .au format, which is streamble and
preserves the format, rate and channels.
Raw and DSD formats are all streamable to STDOUT.
# OPTIONS # OPTIONS
@ -87,6 +94,11 @@ DSD mode. *FILE* is a DSF file. If the tool is called under the name
render the DSD audio. You need a DSD capable device to play DSD content render the DSD audio. You need a DSD capable device to play DSD content
or this program will exit with an error. or this program will exit with an error.
\par -s | \--sysex
SysEx mode. *FILE* is a File that contains a raw SysEx MIDI message.
If the tool is called under the name **pw-sysex** this is the default.
The File is read and sent as a MIDI control message into the graph.
\par \--media-type=VALUE \par \--media-type=VALUE
Set the media type property (default Audio/Midi depending on mode). The Set the media type property (default Audio/Midi depending on mode). The
media type is used by the session manager to select a suitable target to media type is used by the session manager to select a suitable target to
@ -112,6 +124,9 @@ Set a node target (default auto). The value can be:
- <b>\<id\></b>: The object.serial or the node.name of a target node - <b>\<id\></b>: The object.serial or the node.name of a target node
\endparblock \endparblock
\par -C | \--monitor
In recording mode, record from monitor ports.
\par \--latency=VALUE\[*units*\] \par \--latency=VALUE\[*units*\]
\parblock \parblock
Set the node latency (default 100ms) Set the node latency (default 100ms)
@ -138,6 +153,17 @@ does not match the samplerate of the server, the data will be resampled.
Higher quality uses more CPU. Values between 0 and 15 are allowed, the Higher quality uses more CPU. Values between 0 and 15 are allowed, the
default quality is 4. default quality is 4.
\par -a | \--raw
Raw samples will be read or written. The \--rate, \--format, \--channels
and \--channelmap can be used to specify the raw format.
\par -M | \--force-midi
Force midi format, one of "midi" or "ump", (default ump).
When reading or writing midi, for one of midi or UMP.
\par -n | \--sample-count=COUNT
Stop after COUNT samples.
\par \--rate=VALUE \par \--rate=VALUE
The sample rate, default 48000. The sample rate, default 48000.
@ -145,19 +171,38 @@ The sample rate, default 48000.
The number of channels, default 2. The number of channels, default 2.
\par \--channel-map=VALUE \par \--channel-map=VALUE
The channelmap. Possible values include: **mono**, **stereo**, The channelmap. Possible values include are either a channel layout
such as **mono**, **stereo**,
**surround-21**, **quad**, **surround-22**, **surround-40**, **surround-21**, **quad**, **surround-22**, **surround-40**,
**surround-31**, **surround-41**, **surround-50**, **surround-51**, or comma separated array of channel names such as **FL,FR**.
**surround-51r**, **surround-70**, **surround-71** or a comma separated See \--list-layouts and \--list-channel-names to get a complete
list of channel names: **FL**, **FR**, **FC**, **LFE**, **SL**, **SR**, list of possible values.
**FLC**, **FRC**, **RC**, **RL**, **RR**, **TC**, **TFL**, **TFC**,
**TFR**, **TRL**, **TRC**, **TRR**, **RLC**, **RRC**, **FLW**, **FRW**, \par \--list-layouts
**LFE2**, **FLH**, **FCH**, **FRH**, **TFLC**, **TFRC**, **TSL**, List all known channel layouts. One of these can be used as the
**TSR**, **LLFR**, **RLFE**, **BC**, **BLC**, **BRC** \--channel-map value.
\par \--list-channel-names
List all known channel names. An array of these can be used as the
\--channel-map value.
\par \--format=VALUE \par \--format=VALUE
The sample format to use. One of: **u8**, **s8**, **s16** (default), The sample format to use. Some possible values include: **u8**, **s8**,
**s24**, **s32**, **f32**, **f64**. **s16** (default), **s24**, **s32**, **f32**, **f64**. See
\--list-formats to get a complete list of values.
\par \--list-formats
List all known format values.
\par \--container=VALUE
Specify the container to use when saving. This is usually guessed from
the filename extension but can be specified explicitly. When using
STDOUT and no container is specified, the AU container will be used.
Then using a filename and the container was not specified and it could
not be derived from the filename, the WAV container is used.
\par \--list-containers
List all known container values.
\par \--volume=VALUE \par \--volume=VALUE
The stream volume, default 1.000. Depending on the locale you have The stream volume, default 1.000. Depending on the locale you have

View file

@ -49,7 +49,7 @@ Target device to play to
Wanted properties of capture node (in JSON) Wanted properties of capture node (in JSON)
\par -o | \--playback-props=PROPS \par -o | \--playback-props=PROPS
Wanted properties of capture node (in JSON) Wanted properties of playback node (in JSON)
# AUTHORS # AUTHORS

View file

@ -13,7 +13,7 @@ node and device statistics.
A hierarchical view is shown of Driver nodes and follower nodes. The A hierarchical view is shown of Driver nodes and follower nodes. The
Driver nodes are actively using a timer to schedule dataflow in the Driver nodes are actively using a timer to schedule dataflow in the
followers. The followers of a driver node as shown below their driver followers. The followers of a driver node are shown below their driver
with a + sign (or = for async nodes) in a tree-like representation. with a + sign (or = for async nodes) in a tree-like representation.
The columns presented are as follows: The columns presented are as follows:
@ -188,6 +188,11 @@ Quit
Clear the ERR counters. This does *not* clear the counters globally, Clear the ERR counters. This does *not* clear the counters globally,
it will only reset the counters in this instance of *pw-top*. it will only reset the counters in this instance of *pw-top*.
\par [f|F]
Cycle through filter presets. If any nodes are filtered from view,
the current preset will be indicated in the header bar. Only nodes
with the indicated state or higher, will be printed.
# OPTIONS # OPTIONS
\par -h | \--help \par -h | \--help
@ -199,6 +204,9 @@ Run in non-interactive batch mode, similar to top\'s batch mode.
\par -n | \--iterations=NUMBER \par -n | \--iterations=NUMBER
Exit after NUMBER of batch iterations. Only used in batch mode. Exit after NUMBER of batch iterations. Only used in batch mode.
\par -f | \--filter=NUMBER
Start with filter preset NUMBER selected.
\par -r | \--remote=NAME \par -r | \--remote=NAME
The name the *remote* instance to monitor. If left unspecified, a The name the *remote* instance to monitor. If left unspecified, a
connection is made to the default PipeWire instance. connection is made to the default PipeWire instance.

23
doc/examples/meson.build Normal file
View file

@ -0,0 +1,23 @@
tutorials = [
'tutorial1',
'tutorial2',
'tutorial3',
'tutorial4',
'tutorial5',
'tutorial6',
'tutorial7',
]
if not get_option('examples').allowed()
subdir_done()
endif
foreach c : tutorials
executable(
c,
sources: c + '.c',
dependencies: [ pipewire_dep, mathlib ],
install: installed_tests_enabled,
install_dir: installed_tests_execdir / 'examples',
)
endforeach

View file

@ -56,7 +56,7 @@ static void on_process(void *userdata)
* Another common method to convert a double to * Another common method to convert a double to
* 16 bits is to multiple by 32768.0 and then clamp to * 16 bits is to multiple by 32768.0 and then clamp to
* [-32768 32767] to get the full 16 bits range. */ * [-32768 32767] to get the full 16 bits range. */
val = sin(data->accumulator) * DEFAULT_VOLUME * 32767.0; val = (int16_t) (sin(data->accumulator) * DEFAULT_VOLUME * 32767.0);
for (c = 0; c < DEFAULT_CHANNELS; c++) for (c = 0; c < DEFAULT_CHANNELS; c++)
*dst++ = val; *dst++ = val;
} }

View file

@ -67,6 +67,7 @@ extra_docs = [
'dox/internals/session-manager.dox', 'dox/internals/session-manager.dox',
'dox/internals/objects.dox', 'dox/internals/objects.dox',
'dox/internals/audio.dox', 'dox/internals/audio.dox',
'dox/internals/running.dox',
'dox/internals/scheduling.dox', 'dox/internals/scheduling.dox',
'dox/internals/driver.dox', 'dox/internals/driver.dox',
'dox/internals/protocol.dox', 'dox/internals/protocol.dox',
@ -99,6 +100,7 @@ manpage_docs = [
'dox/config/libpipewire-modules.7.md', 'dox/config/libpipewire-modules.7.md',
'dox/programs/pipewire-pulse.1.md', 'dox/programs/pipewire-pulse.1.md',
'dox/programs/pipewire.1.md', 'dox/programs/pipewire.1.md',
'dox/programs/pw-audioconvert.1.md',
'dox/programs/pw-cat.1.md', 'dox/programs/pw-cat.1.md',
'dox/programs/pw-cli.1.md', 'dox/programs/pw-cli.1.md',
'dox/programs/pw-config.1.md', 'dox/programs/pw-config.1.md',
@ -167,18 +169,14 @@ cssfiles = [
] ]
# Example files (in order from simple to esoteric) # Example files (in order from simple to esoteric)
example_files = [ example_files = []
'tutorial1.c',
'tutorial2.c',
'tutorial3.c',
'tutorial4.c',
'tutorial5.c',
'tutorial6.c',
'tutorial7.c',
]
example_dep_files = [] example_dep_files = []
foreach h : example_files
example_dep_files += ['examples/' + h] subdir('examples')
foreach h : tutorials
example_files += [h + '.c']
example_dep_files += ['examples/' + h + '.c']
endforeach endforeach
foreach h : examples foreach h : examples
example_files += [h + '.c'] example_files += [h + '.c']

View file

@ -1,5 +1,5 @@
project('pipewire', ['c' ], project('pipewire', ['c' ],
version : '1.5.83', version : '1.7.0',
license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ], license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
meson_version : '>= 0.61.1', meson_version : '>= 0.61.1',
default_options : [ 'warning_level=3', default_options : [ 'warning_level=3',
@ -82,6 +82,7 @@ common_flags = [
'-fvisibility=hidden', '-fvisibility=hidden',
'-fno-strict-aliasing', '-fno-strict-aliasing',
'-fno-strict-overflow', '-fno-strict-overflow',
'-DSPA_AUDIO_MAX_CHANNELS=128u',
'-Werror=suggest-attribute=format', '-Werror=suggest-attribute=format',
'-Wsign-compare', '-Wsign-compare',
'-Wpointer-arith', '-Wpointer-arith',
@ -115,7 +116,7 @@ cc_flags = common_flags + [
'-Werror=old-style-definition', '-Werror=old-style-definition',
'-Werror=missing-parameter-type', '-Werror=missing-parameter-type',
'-Werror=strict-prototypes', '-Werror=strict-prototypes',
'-DSPA_AUDIO_MAX_CHANNELS=128u', '-Werror=discarded-qualifiers',
] ]
add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c') add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c')
add_project_arguments(cc_native.get_supported_arguments(cc_flags), add_project_arguments(cc_native.get_supported_arguments(cc_flags),
@ -367,7 +368,7 @@ cdata.set('HAVE_OPUS', opus_dep.found())
summary({'readline (for pw-cli)': readline_dep.found()}, bool_yn: true, section: 'Misc dependencies') summary({'readline (for pw-cli)': readline_dep.found()}, bool_yn: true, section: 'Misc dependencies')
cdata.set('HAVE_READLINE', readline_dep.found()) cdata.set('HAVE_READLINE', readline_dep.found())
ncurses_dep = dependency('ncursesw', required : false) ncurses_dep = dependency('ncursesw', required : false)
sndfile_dep = dependency('sndfile', version : '>= 1.0.20', required : get_option('sndfile')) sndfile_dep = dependency('sndfile', version : '>= 1.1.0', required : get_option('sndfile'))
summary({'sndfile': sndfile_dep.found()}, bool_yn: true, section: 'pw-cat/pw-play/pw-dump/filter-chain') summary({'sndfile': sndfile_dep.found()}, bool_yn: true, section: 'pw-cat/pw-play/pw-dump/filter-chain')
cdata.set('HAVE_SNDFILE', sndfile_dep.found()) cdata.set('HAVE_SNDFILE', sndfile_dep.found())
pulseaudio_dep = dependency('libpulse', required : get_option('libpulse')) pulseaudio_dep = dependency('libpulse', required : get_option('libpulse'))
@ -411,7 +412,7 @@ gst_deps_def = {
'gio-unix-2.0': {}, 'gio-unix-2.0': {},
'gstreamer-1.0': {'version': '>= 1.10.0'}, 'gstreamer-1.0': {'version': '>= 1.10.0'},
'gstreamer-base-1.0': {}, 'gstreamer-base-1.0': {},
'gstreamer-video-1.0': {}, 'gstreamer-video-1.0': {'version': '>= 1.22.0'},
'gstreamer-audio-1.0': {}, 'gstreamer-audio-1.0': {},
'gstreamer-allocators-1.0': {}, 'gstreamer-allocators-1.0': {},
} }

View file

@ -294,6 +294,10 @@ option('legacy-rtkit',
description: 'Build legacy rtkit module', description: 'Build legacy rtkit module',
type: 'boolean', type: 'boolean',
value: true) value: true)
option('avb-virtual',
description: 'Enable AVB Virtual code for testing',
type: 'feature',
value: 'disabled')
option('avb', option('avb',
description: 'Enable AVB code', description: 'Enable AVB code',
type: 'feature', type: 'feature',

View file

@ -1248,6 +1248,7 @@ SND_CTL_PLUGIN_DEFINE_FUNC(pipewire)
int err; int err;
const char *str; const char *str;
snd_ctl_pipewire_t *ctl; snd_ctl_pipewire_t *ctl;
struct pw_properties *props2;
struct pw_loop *loop; struct pw_loop *loop;
pw_init(NULL, NULL); pw_init(NULL, NULL);
@ -1392,20 +1393,20 @@ SND_CTL_PLUGIN_DEFINE_FUNC(pipewire)
goto error; goto error;
pw_thread_loop_lock(ctl->mainloop); pw_thread_loop_lock(ctl->mainloop);
ctl->core = pw_context_connect(ctl->context, pw_properties_copy(ctl->props), 0); if ((props2 = pw_properties_copy(ctl->props)) == NULL)
if (ctl->core == NULL) { goto error_unlock_errno;
err = -errno;
goto error_unlock; ctl->core = pw_context_connect(ctl->context, props2, 0);
} if (ctl->core == NULL)
goto error_unlock_errno;
pw_core_add_listener(ctl->core, pw_core_add_listener(ctl->core,
&ctl->core_listener, &ctl->core_listener,
&core_events, ctl); &core_events, ctl);
ctl->registry = pw_core_get_registry(ctl->core, PW_VERSION_REGISTRY, 0); ctl->registry = pw_core_get_registry(ctl->core, PW_VERSION_REGISTRY, 0);
if (ctl->registry == NULL) { if (ctl->registry == NULL)
err = -errno; goto error_unlock_errno;
goto error_unlock;
}
pw_registry_add_listener(ctl->registry, pw_registry_add_listener(ctl->registry,
&ctl->registry_listener, &ctl->registry_listener,
@ -1437,6 +1438,8 @@ SND_CTL_PLUGIN_DEFINE_FUNC(pipewire)
return 0; return 0;
error_unlock_errno:
err = -errno;
error_unlock: error_unlock:
pw_thread_loop_unlock(ctl->mainloop); pw_thread_loop_unlock(ctl->mainloop);
error: error:

View file

@ -951,6 +951,8 @@ static snd_pcm_chmap_t * snd_pcm_pipewire_get_chmap(snd_pcm_ioplug_t * io)
map = calloc(1, sizeof(snd_pcm_chmap_t) + map = calloc(1, sizeof(snd_pcm_chmap_t) +
channels * sizeof(unsigned int)); channels * sizeof(unsigned int));
if (map == NULL)
return NULL;
map->channels = channels; map->channels = channels;
for (i = 0; i < channels; i++) for (i = 0; i < channels; i++)
map->pos[i] = channel_to_chmap(position[i]); map->pos[i] = channel_to_chmap(position[i]);
@ -958,33 +960,39 @@ static snd_pcm_chmap_t * snd_pcm_pipewire_get_chmap(snd_pcm_ioplug_t * io)
return map; return map;
} }
static void make_map(snd_pcm_chmap_query_t **maps, int index, int channels, ...) static void make_map(snd_pcm_chmap_query_t **maps, int *index, int channels, ...)
{ {
va_list args; va_list args;
int i; int i, idx = *index;
maps[index] = malloc(sizeof(snd_pcm_chmap_query_t) + (channels * sizeof(unsigned int))); maps[idx] = malloc(sizeof(snd_pcm_chmap_query_t) + (channels * sizeof(unsigned int)));
maps[index]->type = SND_CHMAP_TYPE_FIXED; if (maps[idx] == NULL)
maps[index]->map.channels = channels; return;
maps[idx]->type = SND_CHMAP_TYPE_FIXED;
maps[idx]->map.channels = channels;
va_start(args, channels); va_start(args, channels);
for (i = 0; i < channels; i++) for (i = 0; i < channels; i++)
maps[index]->map.pos[i] = va_arg(args, int); maps[idx]->map.pos[i] = va_arg(args, int);
va_end(args); va_end(args);
(*index)++;
} }
static snd_pcm_chmap_query_t **snd_pcm_pipewire_query_chmaps(snd_pcm_ioplug_t *io) static snd_pcm_chmap_query_t **snd_pcm_pipewire_query_chmaps(snd_pcm_ioplug_t *io)
{ {
snd_pcm_chmap_query_t **maps; snd_pcm_chmap_query_t **maps;
int idx = 0;
maps = calloc(7, sizeof(*maps)); maps = calloc(7, sizeof(*maps));
make_map(maps, 0, 1, SND_CHMAP_MONO); if (maps == NULL)
make_map(maps, 1, 2, SND_CHMAP_FL, SND_CHMAP_FR); return NULL;
make_map(maps, 2, 4, SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR); make_map(maps, &idx, 1, SND_CHMAP_MONO);
make_map(maps, 3, 5, SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, make_map(maps, &idx, 2, SND_CHMAP_FL, SND_CHMAP_FR);
make_map(maps, &idx, 4, SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR);
make_map(maps, &idx, 5, SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR,
SND_CHMAP_FC); SND_CHMAP_FC);
make_map(maps, 4, 6, SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, make_map(maps, &idx, 6, SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR,
SND_CHMAP_FC, SND_CHMAP_LFE); SND_CHMAP_FC, SND_CHMAP_LFE);
make_map(maps, 5, 8, SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, make_map(maps, &idx, 8, SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR,
SND_CHMAP_FC, SND_CHMAP_LFE, SND_CHMAP_SL, SND_CHMAP_SR); SND_CHMAP_FC, SND_CHMAP_LFE, SND_CHMAP_SL, SND_CHMAP_SR);
return maps; return maps;
@ -1244,7 +1252,7 @@ static ssize_t log_write(void *cookie, const char *buf, size_t size)
return size; return size;
} }
static cookie_io_functions_t io_funcs = { static const cookie_io_functions_t io_funcs = {
.write = log_write, .write = log_write,
}; };
@ -1261,6 +1269,7 @@ static int snd_pcm_pipewire_open(snd_pcm_t **pcmp,
struct pw_properties *props, snd_pcm_stream_t stream, int mode) struct pw_properties *props, snd_pcm_stream_t stream, int mode)
{ {
snd_pcm_pipewire_t *pw; snd_pcm_pipewire_t *pw;
struct pw_properties *props2;
int err; int err;
const char *str, *node_name = NULL; const char *str, *node_name = NULL;
struct pw_loop *loop; struct pw_loop *loop;
@ -1276,8 +1285,7 @@ static int snd_pcm_pipewire_open(snd_pcm_t **pcmp,
pw->log_file = fopencookie(pw, "w", io_funcs); pw->log_file = fopencookie(pw, "w", io_funcs);
if (pw->log_file == NULL) { if (pw->log_file == NULL) {
pw_log_error("can't create log file: %m"); pw_log_error("can't create log file: %m");
err = -errno; goto error_errno;
goto error;
} }
if ((err = snd_output_stdio_attach(&pw->output, pw->log_file, 0)) < 0) { if ((err = snd_output_stdio_attach(&pw->output, pw->log_file, 0)) < 0) {
pw_log_error("can't attach log file: %s", snd_strerror(err)); pw_log_error("can't attach log file: %s", snd_strerror(err));
@ -1285,20 +1293,17 @@ static int snd_pcm_pipewire_open(snd_pcm_t **pcmp,
} }
pw->main_loop = pw_thread_loop_new("alsa-pipewire", NULL); pw->main_loop = pw_thread_loop_new("alsa-pipewire", NULL);
if (pw->main_loop == NULL) { if (pw->main_loop == NULL)
err = -errno; goto error_errno;
goto error;
}
loop = pw_thread_loop_get_loop(pw->main_loop); loop = pw_thread_loop_get_loop(pw->main_loop);
pw->system = loop->system; pw->system = loop->system;
if ((pw->context = pw_context_new(loop, if ((pw->context = pw_context_new(loop,
pw_properties_new( pw_properties_new(
PW_KEY_CLIENT_API, "alsa", PW_KEY_CLIENT_API, "alsa",
NULL), NULL),
0)) == NULL) { 0)) == NULL)
err = -errno; goto error_errno;
goto error;
}
pw_context_conf_update_props(pw->context, "alsa.properties", pw->props); pw_context_conf_update_props(pw->context, "alsa.properties", pw->props);
@ -1348,12 +1353,14 @@ static int snd_pcm_pipewire_open(snd_pcm_t **pcmp,
goto error; goto error;
pw_thread_loop_lock(pw->main_loop); pw_thread_loop_lock(pw->main_loop);
pw->core = pw_context_connect(pw->context, pw_properties_copy(pw->props), 0); props2 = pw_properties_copy(pw->props);
if (pw->core == NULL) { if (props2 == NULL)
err = -errno; goto error_unlock_errno;
pw_thread_loop_unlock(pw->main_loop);
goto error; pw->core = pw_context_connect(pw->context, props2, 0);
} if (pw->core == NULL)
goto error_unlock_errno;
pw_core_add_listener(pw->core, &pw->core_listener, &core_events, pw); pw_core_add_listener(pw->core, &pw->core_listener, &core_events, pw);
pw_thread_loop_unlock(pw->main_loop); pw_thread_loop_unlock(pw->main_loop);
@ -1391,6 +1398,12 @@ static int snd_pcm_pipewire_open(snd_pcm_t **pcmp,
return 0; return 0;
error_unlock_errno:
err = -errno;
pw_thread_loop_unlock(pw->main_loop);
goto error;
error_errno:
err = -errno;
error: error:
pw_log_debug("%p: failed to open %s :%s", pw, node_name, spa_strerror(err)); pw_log_debug("%p: failed to open %s :%s", pw, node_name, spa_strerror(err));
snd_pcm_pipewire_free(pw); snd_pcm_pipewire_free(pw);

View file

@ -55,7 +55,7 @@ pipewire_jackserver = shared_library('jackserver',
pipewire_jackserver_sources, pipewire_jackserver_sources,
soversion : soversion, soversion : soversion,
version : libjackversion, version : libjackversion,
c_args : pipewire_jack_c_args, c_args : pipewire_jack_c_args + '-DLIBJACKSERVER',
include_directories : [configinc, jack_inc], include_directories : [configinc, jack_inc],
dependencies : [pipewire_dep, mathlib], dependencies : [pipewire_dep, mathlib],
install : true, install : true,

View file

@ -26,13 +26,6 @@ static jack_description_t *find_description(jack_uuid_t subject)
return NULL; return NULL;
} }
static void set_property(jack_property_t *prop, const char *key, const char *value, const char *type)
{
prop->key = strdup(key);
prop->data = strdup(value);
prop->type = strdup(type);
}
static void clear_property(jack_property_t *prop) static void clear_property(jack_property_t *prop)
{ {
free((char*)prop->key); free((char*)prop->key);
@ -40,16 +33,41 @@ static void clear_property(jack_property_t *prop)
free((char*)prop->type); free((char*)prop->type);
} }
static int set_property(jack_property_t *prop, const char *key, const char *value, const char *type)
{
prop->key = strdup(key);
prop->data = strdup(value);
prop->type = strdup(type);
if (prop->key == NULL || prop->data == NULL || prop->type == NULL) {
int err = errno;
clear_property(prop);
spa_zero(*prop);
return -err;
}
return 0;
}
static jack_property_t *copy_properties(jack_property_t *src, uint32_t cnt) static jack_property_t *copy_properties(jack_property_t *src, uint32_t cnt)
{ {
jack_property_t *dst; jack_property_t *dst;
uint32_t i; uint32_t i;
dst = malloc(sizeof(jack_property_t) * cnt);
if (dst != NULL) { dst = pw_reallocarray(NULL, cnt, sizeof(jack_property_t));
for (i = 0; i < cnt; i++) if (dst == NULL)
set_property(&dst[i], src[i].key, src[i].data, src[i].type); return NULL;
for (i = 0; i < cnt; i++) {
if (set_property(&dst[i], src[i].key, src[i].data, src[i].type) < 0)
goto cleanup;
} }
return dst; return dst;
cleanup:
while (i > 0)
clear_property(&dst[--i]);
free(dst);
return NULL;
} }
static int copy_description(jack_description_t *dst, jack_description_t *src) static int copy_description(jack_description_t *dst, jack_description_t *src)
@ -106,8 +124,10 @@ static jack_property_t *add_property(jack_description_t *desc, const char *key,
desc->property_size = ns; desc->property_size = ns;
desc->properties = np; desc->properties = np;
} }
prop = &desc->properties[desc->property_cnt++]; prop = &desc->properties[desc->property_cnt];
set_property(prop, key, value, type); if (set_property(prop, key, value, type) < 0)
return NULL;
desc->property_cnt++;
return prop; return prop;
} }
@ -127,13 +147,19 @@ static int change_property(jack_property_t *prop, const char *value, const char
{ {
int changed = 0; int changed = 0;
if (!spa_streq(prop->data, value)) { if (!spa_streq(prop->data, value)) {
char *v = strdup(value);
if (v == NULL)
return -errno;
free((char*)prop->data); free((char*)prop->data);
prop->data = strdup(value); prop->data = v;
changed++; changed++;
} }
if (!spa_streq(prop->type, type)) { if (!spa_streq(prop->type, type)) {
char *t = strdup(type);
if (t == NULL)
return -errno;
free((char*)prop->type); free((char*)prop->type);
prop->type = strdup(type); prop->type = t;
changed++; changed++;
} }
return changed; return changed;
@ -245,26 +271,38 @@ int jack_get_property(jack_uuid_t subject,
{ {
jack_description_t *desc; jack_description_t *desc;
jack_property_t *prop; jack_property_t *prop;
int res = -1; char *v, *t;
pthread_mutex_lock(&globals.lock); pthread_mutex_lock(&globals.lock);
desc = find_description(subject); desc = find_description(subject);
if (desc == NULL) if (desc == NULL)
goto done; goto error;
prop = find_property(desc, key); prop = find_property(desc, key);
if (prop == NULL) if (prop == NULL)
goto done; goto error;
*value = strdup(prop->data); v = strdup(prop->data);
*type = strdup(prop->type); t = strdup(prop->type);
res = 0; if (v == NULL || t == NULL)
goto cleanup;
pthread_mutex_unlock(&globals.lock);
*value = v;
*type = t;
pw_log_debug("subject:%"PRIu64" key:'%s' value:'%s' type:'%s'", pw_log_debug("subject:%"PRIu64" key:'%s' value:'%s' type:'%s'",
subject, key, *value, *type); subject, key, v, t);
done:
return 0;
cleanup:
free(v);
free(t);
error:
pthread_mutex_unlock(&globals.lock); pthread_mutex_unlock(&globals.lock);
return res; return -1;
} }
SPA_EXPORT SPA_EXPORT
@ -312,12 +350,26 @@ int jack_get_all_properties (jack_description_t** result)
len = pw_array_get_len(descriptions, jack_description_t); len = pw_array_get_len(descriptions, jack_description_t);
src = descriptions->data; src = descriptions->data;
dst = malloc(descriptions->size); dst = malloc(descriptions->size);
for (i = 0; i < len; i++) if (dst == NULL)
copy_description(&dst[i], &src[i]); goto error;
for (i = 0; i < len; i++) {
if (copy_description(&dst[i], &src[i]) < 0)
goto cleanup;
}
*result = dst; *result = dst;
pthread_mutex_unlock(&globals.lock); pthread_mutex_unlock(&globals.lock);
return len; return len;
cleanup:
while (i > 0)
jack_free_description(&dst[--i], false);
free(dst);
error:
*result = NULL;
pthread_mutex_unlock(&globals.lock);
return 0;
} }
SPA_EXPORT SPA_EXPORT

View file

@ -73,7 +73,6 @@ PW_LOG_TOPIC_STATIC(jack_log_topic, "jack");
#define TYPE_ID_IS_EVENT(t) ((t) >= TYPE_ID_MIDI && (t) <= TYPE_ID_UMP) #define TYPE_ID_IS_EVENT(t) ((t) >= TYPE_ID_MIDI && (t) <= TYPE_ID_UMP)
#define TYPE_ID_CAN_OSC(t) ((t) == TYPE_ID_MIDI || (t) == TYPE_ID_OSC) #define TYPE_ID_CAN_OSC(t) ((t) == TYPE_ID_MIDI || (t) == TYPE_ID_OSC)
#define TYPE_ID_IS_HIDDEN(t) ((t) >= TYPE_ID_OTHER)
#define TYPE_ID_IS_COMPATIBLE(a,b)(((a) == (b)) || (TYPE_ID_IS_EVENT(a) && TYPE_ID_IS_EVENT(b))) #define TYPE_ID_IS_COMPATIBLE(a,b)(((a) == (b)) || (TYPE_ID_IS_EVENT(a) && TYPE_ID_IS_EVENT(b)))
#define SELF_CONNECT_ALLOW 0 #define SELF_CONNECT_ALLOW 0
@ -86,7 +85,7 @@ PW_LOG_TOPIC_STATIC(jack_log_topic, "jack");
#define OTHER_CONNECT_FAIL -1 #define OTHER_CONNECT_FAIL -1
#define OTHER_CONNECT_IGNORE 0 #define OTHER_CONNECT_IGNORE 0
#define NOTIFY_BUFFER_SIZE (1u<<13) #define NOTIFY_BUFFER_SIZE (1u<<16)
#define NOTIFY_BUFFER_MASK (NOTIFY_BUFFER_SIZE-1) #define NOTIFY_BUFFER_MASK (NOTIFY_BUFFER_SIZE-1)
struct notify { struct notify {
@ -104,8 +103,8 @@ struct notify {
#define NOTIFY_TYPE_TOTAL_LATENCY ((9<<4)|NOTIFY_ACTIVE_FLAG) #define NOTIFY_TYPE_TOTAL_LATENCY ((9<<4)|NOTIFY_ACTIVE_FLAG)
#define NOTIFY_TYPE_PORT_RENAME ((10<<4)|NOTIFY_ACTIVE_FLAG) #define NOTIFY_TYPE_PORT_RENAME ((10<<4)|NOTIFY_ACTIVE_FLAG)
int type; int type;
struct object *object;
int arg1; int arg1;
struct object *object;
const char *msg; const char *msg;
}; };
@ -492,6 +491,8 @@ struct client {
jack_position_t jack_position; jack_position_t jack_position;
jack_transport_state_t jack_state; jack_transport_state_t jack_state;
struct frame_times jack_times; struct frame_times jack_times;
struct object dummy_port;
}; };
#define return_val_if_fail(expr, val) \ #define return_val_if_fail(expr, val) \
@ -1446,8 +1447,9 @@ static size_t convert_from_event(void *midi, void *buffer, size_t size, uint32_t
switch (type) { switch (type) {
case TYPE_ID_MIDI: case TYPE_ID_MIDI:
event_type = SPA_CONTROL_Midi;
break;
case TYPE_ID_OSC: case TYPE_ID_OSC:
/* we handle MIDI as OSC, check below */
event_type = SPA_CONTROL_OSC; event_type = SPA_CONTROL_OSC;
break; break;
case TYPE_ID_UMP: case TYPE_ID_UMP:
@ -1464,27 +1466,15 @@ static size_t convert_from_event(void *midi, void *buffer, size_t size, uint32_t
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
jack_midi_event_t ev; jack_midi_event_t ev;
jack_midi_event_get(&ev, midi, i); jack_midi_event_get(&ev, midi, i);
uint32_t ev_type;
if (type != TYPE_ID_MIDI || is_osc(&ev)) { if (type == TYPE_ID_MIDI && is_osc(&ev))
/* no midi port or it's OSC */ ev_type = SPA_CONTROL_OSC;
spa_pod_builder_control(&b, ev.time, event_type); else
spa_pod_builder_bytes(&b, ev.buffer, ev.size); ev_type = event_type;
} else {
/* midi port and it's not OSC, convert to UMP */
uint8_t *data = ev.buffer;
size_t size = ev.size;
uint64_t state = 0;
while (size > 0) { spa_pod_builder_control(&b, ev.time, ev_type);
uint32_t ump[4]; spa_pod_builder_bytes(&b, ev.buffer, ev.size);
int ump_size = spa_ump_from_midi(&data, &size,
ump, sizeof(ump), 0, &state);
if (ump_size <= 0)
break;
spa_pod_builder_control(&b, ev.time, SPA_CONTROL_UMP);
spa_pod_builder_bytes(&b, ump, ump_size);
}
}
} }
spa_pod_builder_pop(&b, &f); spa_pod_builder_pop(&b, &f);
return b.state.offset; return b.state.offset;
@ -1579,6 +1569,8 @@ static inline int midi_event_append(void *port_buffer, const jack_midi_data_t *d
size_t old_size; size_t old_size;
uint8_t *old, *buf; uint8_t *old, *buf;
if (mb->event_count == 0)
return -ENOBUFS;
ev = &events[--mb->event_count]; ev = &events[--mb->event_count];
mb->write_pos -= ev->size; mb->write_pos -= ev->size;
old_size = ev->size; old_size = ev->size;
@ -1776,7 +1768,7 @@ static inline void process_empty(struct port *p, uint32_t frames)
case TYPE_ID_AUDIO: case TYPE_ID_AUDIO:
ptr = get_buffer_output(p, frames, sizeof(float), NULL); ptr = get_buffer_output(p, frames, sizeof(float), NULL);
if (SPA_LIKELY(ptr != NULL)) if (SPA_LIKELY(ptr != NULL))
memcpy(ptr, src, frames * sizeof(float)); spa_memcpy(ptr, src, frames * sizeof(float));
break; break;
case TYPE_ID_MIDI: case TYPE_ID_MIDI:
case TYPE_ID_OSC: case TYPE_ID_OSC:
@ -1790,7 +1782,7 @@ static inline void process_empty(struct port *p, uint32_t frames)
* to do this concurrently */ * to do this concurrently */
b->datas[0].chunk->size = convert_from_event(src, midi_scratch, b->datas[0].chunk->size = convert_from_event(src, midi_scratch,
MIDI_SCRATCH_FRAMES * sizeof(float), type); MIDI_SCRATCH_FRAMES * sizeof(float), type);
memcpy(ptr, midi_scratch, b->datas[0].chunk->size); spa_memcpy(ptr, midi_scratch, b->datas[0].chunk->size);
} }
break; break;
} }
@ -2208,7 +2200,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask)
} }
} else if (SPA_LIKELY(mask & SPA_IO_IN)) { } else if (SPA_LIKELY(mask & SPA_IO_IN)) {
uint32_t buffer_frames; uint32_t buffer_frames;
int status = 0; int status = -EBUSY;
buffer_frames = cycle_run(c); buffer_frames = cycle_run(c);
@ -3724,8 +3716,8 @@ static int update_port_name(struct object *o, const char *name)
if (spa_streq(port_name, o->port.name)) if (spa_streq(port_name, o->port.name))
return 0; return 0;
strcpy(o->port.old_name, o->port.name); snprintf(o->port.old_name, sizeof(o->port.old_name), "%s", o->port.name);
strcpy(o->port.name, port_name); snprintf(o->port.name, sizeof(o->port.name), "%s", port_name);
return 1; return 1;
} }
@ -3798,7 +3790,8 @@ static void registry_event_global(void *data, uint32_t id,
if ((str = spa_dict_lookup(props, PW_KEY_SEC_PID)) != NULL) { if ((str = spa_dict_lookup(props, PW_KEY_SEC_PID)) != NULL) {
pw_log_debug("%p: pid of \"%s\" is \"%s\"", c, app, str); pw_log_debug("%p: pid of \"%s\" is \"%s\"", c, app, str);
} else { } else {
pw_log_debug("%p: pid of \"%s\" is unknown", c, app); pw_log_warn("%p: pid of \"%s\" is unknown", c, app);
str = "0";
} }
o = alloc_object(c, INTERFACE_Client); o = alloc_object(c, INTERFACE_Client);
@ -3903,8 +3896,9 @@ static void registry_event_global(void *data, uint32_t id,
const char *name; const char *name;
if ((str = spa_dict_lookup(props, PW_KEY_FORMAT_DSP)) == NULL) if ((str = spa_dict_lookup(props, PW_KEY_FORMAT_DSP)) == NULL)
str = "other"; goto exit;
if ((type_id = string_to_type(str)) == SPA_ID_INVALID) if ((type_id = string_to_type(str)) == SPA_ID_INVALID ||
!type_is_dsp(type_id))
goto exit; goto exit;
if ((str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL) if ((str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL)
@ -4386,13 +4380,17 @@ jack_client_t * jack_client_open (const char *client_name,
if (client->core == NULL) if (client->core == NULL)
goto server_failed; goto server_failed;
client->pool = pw_core_get_mempool(client->core);
pw_core_add_listener(client->core, pw_core_add_listener(client->core,
&client->core_listener, &client->core_listener,
&core_events, client); &core_events, client);
client->pool = pw_core_get_mempool(client->core);
client->registry = pw_core_get_registry(client->core, client->registry = pw_core_get_registry(client->core,
PW_VERSION_REGISTRY, 0); PW_VERSION_REGISTRY, 0);
if (client->registry == NULL)
goto init_failed;
pw_registry_add_listener(client->registry, pw_registry_add_listener(client->registry,
&client->registry_listener, &client->registry_listener,
&registry_events, client); &registry_events, client);
@ -4468,6 +4466,11 @@ jack_client_t * jack_client_open (const char *client_name,
0, NULL, &client->info); 0, NULL, &client->info);
client->info.change_mask = 0; client->info.change_mask = 0;
client->dummy_port.type = INTERFACE_Port;
snprintf(client->dummy_port.port.name, sizeof(client->dummy_port.port.name), "%s:dummy", client_name);
snprintf(client->dummy_port.port.alias1, sizeof(client->dummy_port.port.alias1), "%s:dummy", client_name);
snprintf(client->dummy_port.port.alias2, sizeof(client->dummy_port.port.alias2), "%s:dummy", client_name);
client->show_monitor = pw_properties_get_bool(client->props, "jack.show-monitor", true); client->show_monitor = pw_properties_get_bool(client->props, "jack.show-monitor", true);
client->show_midi = pw_properties_get_bool(client->props, "jack.show-midi", true); client->show_midi = pw_properties_get_bool(client->props, "jack.show-midi", true);
client->merge_monitor = pw_properties_get_bool(client->props, "jack.merge-monitor", true); client->merge_monitor = pw_properties_get_bool(client->props, "jack.merge-monitor", true);
@ -4868,7 +4871,7 @@ int jack_activate (jack_client_t *client)
freeze_callbacks(c); freeze_callbacks(c);
/* reemit buffer_frames */ /* reemit buffer_frames */
c->buffer_frames = 0; c->buffer_frames = (uint32_t)-1;
pw_data_loop_start(c->loop); pw_data_loop_start(c->loop);
c->active = true; c->active = true;
@ -4880,9 +4883,21 @@ int jack_activate (jack_client_t *client)
c->activation->pending_sync = true; c->activation->pending_sync = true;
spa_list_for_each(o, &c->context.objects, link) { spa_list_for_each(o, &c->context.objects, link) {
#if !defined(LIBJACKSERVER)
if (o->type != INTERFACE_Port || o->port.port == NULL || if (o->type != INTERFACE_Port || o->port.port == NULL ||
o->port.port->client != c || !o->port.port->valid) o->port.port->client != c || !o->port.port->valid)
continue; continue;
#else
/* emits all foreign active ports, skips own (already announced via jack_port_register) */
if (o->type != INTERFACE_Port || o->removed)
continue;
/* own ports are handled by jack_port_register */
if (o->port.port != NULL && o->port.port->client == c)
continue;
/* only announce ports whose node is active */
if (o->port.node != NULL && !node_is_active(c, o->port.node))
continue;
#endif
o->registered = 0; o->registered = 0;
queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 1, NULL); queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 1, NULL);
} }
@ -5318,7 +5333,7 @@ int jack_set_freewheel(jack_client_t* client, int onoff)
pw_thread_loop_lock(c->context.loop); pw_thread_loop_lock(c->context.loop);
str = pw_properties_get(c->props, PW_KEY_NODE_GROUP); str = pw_properties_get(c->props, PW_KEY_NODE_GROUP);
if (str != NULL) { if (str != NULL) {
char *p = strstr(str, ",pipewire.freewheel"); const char *p = strstr(str, ",pipewire.freewheel");
if (p == NULL) if (p == NULL)
p = strstr(str, "pipewire.freewheel"); p = strstr(str, "pipewire.freewheel");
if (p == NULL && onoff) if (p == NULL && onoff)
@ -5437,7 +5452,7 @@ SPA_EXPORT
jack_nframes_t jack_get_buffer_size (jack_client_t *client) jack_nframes_t jack_get_buffer_size (jack_client_t *client)
{ {
struct client *c = (struct client *) client; struct client *c = (struct client *) client;
jack_nframes_t res = -1; uint32_t res = -1;
return_val_if_fail(c != NULL, 0); return_val_if_fail(c != NULL, 0);
@ -5454,7 +5469,7 @@ jack_nframes_t jack_get_buffer_size (jack_client_t *client)
} }
c->buffer_frames = res; c->buffer_frames = res;
pw_log_debug("buffer_frames: %u", res); pw_log_debug("buffer_frames: %u", res);
return res; return (jack_nframes_t)res;
} }
SPA_EXPORT SPA_EXPORT
@ -5523,7 +5538,8 @@ jack_port_t * jack_port_register (jack_client_t *client,
return NULL; return NULL;
} }
if ((type_id = string_to_type(port_type)) == SPA_ID_INVALID) { if ((type_id = string_to_type(port_type)) == SPA_ID_INVALID ||
!type_is_dsp(type_id)) {
pw_log_warn("unknown port type %s", port_type); pw_log_warn("unknown port type %s", port_type);
return NULL; return NULL;
} }
@ -5551,7 +5567,7 @@ jack_port_t * jack_port_register (jack_client_t *client,
o = p->object; o = p->object;
o->port.flags = flags; o->port.flags = flags;
strcpy(o->port.name, name); snprintf(o->port.name, sizeof(o->port.name), "%s", name);
o->port.type_id = type_id; o->port.type_id = type_id;
init_buffer(p, c->max_frames); init_buffer(p, c->max_frames);
@ -5842,11 +5858,11 @@ static void *get_buffer_input_midi(struct port *p, jack_nframes_t frames)
* the per port buffer. This makes it possible to call this function concurrently * the per port buffer. This makes it possible to call this function concurrently
* but also have different pointers per port */ * but also have different pointers per port */
convert_to_event(mix_info, n_mix_info, mb, p->client->fix_midi_events, p->object->port.type_id); convert_to_event(mix_info, n_mix_info, mb, p->client->fix_midi_events, p->object->port.type_id);
memcpy(ptr, mb, sizeof(struct midi_buffer) + (mb->event_count spa_memcpy(ptr, mb, sizeof(struct midi_buffer) + (mb->event_count
* sizeof(struct midi_event))); * sizeof(struct midi_event)));
if (mb->write_pos > 0) { if (mb->write_pos > 0) {
size_t offs = mb->buffer_size - mb->write_pos; size_t offs = mb->buffer_size - mb->write_pos;
memcpy(SPA_PTROFF(ptr, offs, void), SPA_PTROFF(mb, offs, void), mb->write_pos); spa_memcpy(SPA_PTROFF(ptr, offs, void), SPA_PTROFF(mb, offs, void), mb->write_pos);
} }
return ptr; return ptr;
} }
@ -5951,9 +5967,7 @@ static const char *port_name(struct object *o)
{ {
const char *name; const char *name;
struct client *c = o->client; struct client *c = o->client;
if (c == NULL) if (c != NULL && c->default_as_system && is_port_default(c, o))
return NULL;
if (c->default_as_system && is_port_default(c, o))
name = o->port.system; name = o->port.system;
else else
name = o->port.name; name = o->port.name;
@ -5977,7 +5991,8 @@ const char * jack_port_short_name (const jack_port_t *port)
return_val_if_fail(o != NULL, NULL); return_val_if_fail(o != NULL, NULL);
if (o->type != INTERFACE_Port) if (o->type != INTERFACE_Port)
return NULL; return NULL;
return strchr(port_name(o), ':') + 1; const char *p = strchr(port_name(o), ':');
return p ? p + 1 : port_name(o);
} }
SPA_EXPORT SPA_EXPORT
@ -6007,7 +6022,16 @@ jack_port_type_id_t jack_port_type_id (const jack_port_t *port)
return_val_if_fail(o != NULL, 0); return_val_if_fail(o != NULL, 0);
if (o->type != INTERFACE_Port) if (o->type != INTERFACE_Port)
return TYPE_ID_OTHER; return TYPE_ID_OTHER;
return o->port.type_id;
/* map internal type IDs to jack1/jack2 compatible public values */
switch (o->port.type_id) {
case TYPE_ID_AUDIO: return 0;
case TYPE_ID_MIDI:
case TYPE_ID_OSC:
case TYPE_ID_UMP: return 1; /* all MIDI variants map to 1 */
case TYPE_ID_VIDEO: return 3; /* video maps to 3 */
default: return o->port.type_id;
}
} }
SPA_EXPORT SPA_EXPORT
@ -6498,10 +6522,10 @@ int jack_connect (jack_client_t *client,
if ((res = check_connect(c, src, dst)) != 1) if ((res = check_connect(c, src, dst)) != 1)
goto exit; goto exit;
snprintf(val[0], sizeof(val[0]), "%d", src->port.node_id); snprintf(val[0], sizeof(val[0]), "%u", src->port.node_id);
snprintf(val[1], sizeof(val[1]), "%d", src->id); snprintf(val[1], sizeof(val[1]), "%u", src->id);
snprintf(val[2], sizeof(val[2]), "%d", dst->port.node_id); snprintf(val[2], sizeof(val[2]), "%u", dst->port.node_id);
snprintf(val[3], sizeof(val[3]), "%d", dst->id); snprintf(val[3], sizeof(val[3]), "%u", dst->id);
props = SPA_DICT_INIT(items, 0); props = SPA_DICT_INIT(items, 0);
items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_OUTPUT_NODE, val[0]); items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_OUTPUT_NODE, val[0]);
@ -6564,8 +6588,6 @@ int jack_disconnect (jack_client_t *client,
src = find_port_by_name(c, source_port); src = find_port_by_name(c, source_port);
dst = find_port_by_name(c, destination_port); dst = find_port_by_name(c, destination_port);
pw_log_debug("%p: %d %d", client, src->id, dst->id);
if (src == NULL || dst == NULL || if (src == NULL || dst == NULL ||
!(src->port.flags & JackPortIsOutput) || !(src->port.flags & JackPortIsOutput) ||
!(dst->port.flags & JackPortIsInput)) { !(dst->port.flags & JackPortIsInput)) {
@ -6920,8 +6942,6 @@ const char ** jack_get_ports (jack_client_t *client,
continue; continue;
pw_log_debug("%p: check port type:%d flags:%08lx name:\"%s\"", c, pw_log_debug("%p: check port type:%d flags:%08lx name:\"%s\"", c,
o->port.type_id, o->port.flags, o->port.name); o->port.type_id, o->port.flags, o->port.name);
if (TYPE_ID_IS_HIDDEN(o->port.type_id))
continue;
if (!SPA_FLAG_IS_SET(o->port.flags, flags)) if (!SPA_FLAG_IS_SET(o->port.flags, flags))
continue; continue;
if (str != NULL && o->port.node != NULL) { if (str != NULL && o->port.node != NULL) {
@ -6999,13 +7019,11 @@ jack_port_t * jack_port_by_id (jack_client_t *client,
pthread_mutex_lock(&c->context.lock); pthread_mutex_lock(&c->context.lock);
res = find_by_serial(c, port_id); res = find_by_serial(c, port_id);
if (res && res->type != INTERFACE_Port)
res = NULL;
pw_log_debug("%p: port %d -> %p", c, port_id, res);
pthread_mutex_unlock(&c->context.lock); pthread_mutex_unlock(&c->context.lock);
if (res == NULL || res->type != INTERFACE_Port)
res = &c->dummy_port;
if (res == NULL) pw_log_debug("%p: port %d -> %p", c, port_id, res);
pw_log_info("%p: port %d not found", c, port_id);
return object_to_port(res); return object_to_port(res);
} }

View file

@ -132,10 +132,10 @@ size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt)
n2 = 0; n2 = 0;
} }
memcpy (dest, &(rb->buf[rb->read_ptr]), n1); spa_memcpy (dest, &(rb->buf[rb->read_ptr]), n1);
rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask; rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask;
if (n2) { if (n2) {
memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); spa_memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2);
rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask; rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask;
} }
return to_read; return to_read;
@ -167,11 +167,11 @@ size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt)
n2 = 0; n2 = 0;
} }
memcpy (dest, &(rb->buf[tmp_read_ptr]), n1); spa_memcpy (dest, &(rb->buf[tmp_read_ptr]), n1);
tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask; tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask;
if (n2) if (n2)
memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2); spa_memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2);
return to_read; return to_read;
} }
@ -249,10 +249,10 @@ size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src,
n2 = 0; n2 = 0;
} }
memcpy (&(rb->buf[rb->write_ptr]), src, n1); spa_memcpy (&(rb->buf[rb->write_ptr]), src, n1);
rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask; rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask;
if (n2) { if (n2) {
memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); spa_memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2);
rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask; rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask;
} }
return to_write; return to_write;

View file

@ -2570,7 +2570,10 @@ static void *v4l2_mmap(void *addr, size_t length, int prot,
buf = &file->buffers[id]; buf = &file->buffers[id];
data = &buf->buf->buffer->datas[0]; data = &buf->buf->buffer->datas[0];
pw_map_range_init(&range, data->mapoffset, data->maxsize, 1024); if (pw_map_range_init(&range, data->mapoffset, data->maxsize, 1024) < 0) {
res = MAP_FAILED;
goto error_unlock;
}
if (!SPA_FLAG_IS_SET(data->flags, SPA_DATA_FLAG_READABLE)) if (!SPA_FLAG_IS_SET(data->flags, SPA_DATA_FLAG_READABLE))
prot &= ~PROT_READ; prot &= ~PROT_READ;

View file

@ -7,9 +7,10 @@ bn_IN
ca ca
cs cs
da da
de_CH
de de
de_CH
el el
eo
es es
fi fi
fr fr
@ -36,14 +37,15 @@ oc
or or
pa pa
pl pl
pt_BR
pt pt
pt_BR
ro ro
ru ru
si
sk sk
sl sl
sr@latin
sr sr
sr@latin
sv sv
ta ta
te te
@ -51,5 +53,3 @@ tr
uk uk
zh_CN zh_CN
zh_TW zh_TW
eo
si

573
po/kk.po
View file

@ -1,15 +1,14 @@
# Kazakh translation of pipewire. # Kazakh translation of pipewire.
# Copyright (C) 2020 The pipewire authors. # Copyright (C) 2020 The pipewire authors.
# This file is distributed under the same license as the pipewire package. # This file is distributed under the same license as the pipewire package.
# Baurzhan Muftakhidinov <baurthefirst@gmail.com>, 2020. # Baurzhan Muftakhidinov <baurthefirst@gmail.com>, 2020-2026.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues\n"
"issues/new\n" "POT-Creation-Date: 2026-03-09 12:19+0000\n"
"POT-Creation-Date: 2021-04-18 16:54+0800\n" "PO-Revision-Date: 2026-03-17 00:04+0500\n"
"PO-Revision-Date: 2020-06-30 08:04+0500\n"
"Last-Translator: Baurzhan Muftakhidinov <baurthefirst@gmail.com>\n" "Last-Translator: Baurzhan Muftakhidinov <baurthefirst@gmail.com>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: kk\n" "Language: kk\n"
@ -17,96 +16,199 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.3.1\n" "X-Generator: Poedit 3.9\n"
#: src/daemon/pipewire.c:43 #: src/daemon/pipewire.c:29
#, c-format #, c-format
msgid "" msgid ""
"%s [options]\n" "%s [options]\n"
" -h, --help Show this help\n" " -h, --help Show this help\n"
" -v, --verbose Increase verbosity by one level\n"
" --version Show version\n" " --version Show version\n"
" -c, --config Load config (Default %s)\n" " -c, --config Load config (Default %s)\n"
" -P --properties Set context properties\n"
msgstr "" msgstr ""
"%s [опциялар]\n"
" -h, --help Осы көмекті көрсету\n"
" -v, --verbose Ақпараттылығын бір деңгейге арттыру\n"
" --version Нұсқасын көрсету\n"
" -c, --config Конфигурацияны жүктеу (Бастапқы %s)\n"
" -P --properties Контекст қасиеттерін орнату\n"
#: src/daemon/pipewire.desktop.in:3
msgid "PipeWire Media System"
msgstr "PipeWire медиа жүйесі"
#: src/daemon/pipewire.desktop.in:4 #: src/daemon/pipewire.desktop.in:4
msgid "PipeWire Media System"
msgstr ""
#: src/daemon/pipewire.desktop.in:5
msgid "Start the PipeWire Media System" msgid "Start the PipeWire Media System"
msgstr "" msgstr "PipeWire медиа жүйесін іске қосу"
#: src/examples/media-session/alsa-monitor.c:526 #: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
#: spa/plugins/alsa/acp/compat.c:187 #: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
msgid "Built-in Audio" #, c-format
msgstr "Құрамындағы аудио" msgid "Tunnel to %s%s%s"
msgstr "%s%s%s бағытына туннель"
#: src/examples/media-session/alsa-monitor.c:530 #: src/modules/module-fallback-sink.c:40
#: spa/plugins/alsa/acp/compat.c:192 msgid "Dummy Output"
msgid "Modem" msgstr "Жалған шығыс"
msgstr "Модем"
#: src/examples/media-session/alsa-monitor.c:539 #: src/modules/module-pulse-tunnel.c:761
#, c-format
msgid "Tunnel for %s@%s"
msgstr "%s@%s үшін туннель"
#: src/modules/module-zeroconf-discover.c:290
msgid "Unknown device" msgid "Unknown device"
msgstr "" msgstr "Белгісіз құрылғы"
#: src/tools/pw-cat.c:991 #: src/modules/module-zeroconf-discover.c:302
#, c-format
msgid "%s on %s@%s"
msgstr "%s, %s@%s ішінде"
#: src/modules/module-zeroconf-discover.c:306
#, c-format
msgid "%s on %s"
msgstr "%s, %s ішінде"
#: src/tools/pw-cat.c:269
#, c-format
msgid "Supported formats:\n"
msgstr "Қолдау көрсетілетін пішімдер:\n"
#: src/tools/pw-cat.c:754
#, c-format
msgid "Supported channel layouts:\n"
msgstr "Қолдау көрсетілетін арна жаймалары:\n"
#: src/tools/pw-cat.c:764
#, c-format
msgid "Supported channel layout aliases:\n"
msgstr "Қолдау көрсетілетін арна жаймаларының алиастары:\n"
#: src/tools/pw-cat.c:766
#, c-format
msgid " %s -> %s\n"
msgstr " %s -> %s\n"
#: src/tools/pw-cat.c:771
#, c-format
msgid "Supported channel names:\n"
msgstr "Қолдау көрсетілетін арна атаулары:\n"
#: src/tools/pw-cat.c:1182
#, c-format #, c-format
msgid "" msgid ""
"%s [options] <file>\n" "%s [options] [<file>|-]\n"
" -h, --help Show this help\n" " -h, --help Show this help\n"
" --version Show version\n" " --version Show version\n"
" -v, --verbose Enable verbose operations\n" " -v, --verbose Enable verbose operations\n"
"\n" "\n"
msgstr "" msgstr ""
"%s [опциялар] [<файл>|-]\n"
" -h, --help Осы көмекті көрсету\n"
" --version Нұсқасын көрсету\n"
" -v, --verbose Толық ақпаратты іске қосу\n"
"\n"
#: src/tools/pw-cat.c:998 #: src/tools/pw-cat.c:1189
#, c-format #, c-format
msgid "" msgid ""
" -R, --remote Remote daemon name\n" " -R, --remote Remote daemon name\n"
" --media-type Set media type (default %s)\n" " --media-type Set media type (default %s)\n"
" --media-category Set media category (default %s)\n" " --media-category Set media category (default %s)\n"
" --media-role Set media role (default %s)\n" " --media-role Set media role (default %s)\n"
" --target Set node target (default %s)\n" " --target Set node target serial or name (default %s)\n"
" 0 means don't link\n" " 0 means don't link\n"
" --latency Set node latency (default %s)\n" " --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n" " Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n" " or direct samples (256)\n"
" the rate is the one of the source " " the rate is the one of the source file\n"
"file\n" " -P --properties Set node properties\n"
" --list-targets List available targets for --target\n"
"\n" "\n"
msgstr "" msgstr ""
" -R, --remote Қашықтағы қызмет атауы\n"
" --media-type Медиа түрін орнату (бастапқы %s)\n"
" --media-category Медиа категориясын орнату (бастапқы %s)\n"
" --media-role Медиа рөлін орнату (бастапқы %s)\n"
" --target Түйін мақсатының сериялық нөмірін немесе атын орнату (бастапқы "
"%s)\n"
" 0 байланыстырмауды білдіреді\n"
" --latency Түйін кідірісін орнату (бастапқы %s)\n"
" Xюнит (юнит = с, мс, мкс, нс)\n"
" немесе тікелей үлгілер (256)\n"
" жиілік бастапқы файлдың жиілігі болып табылады\n"
" -P --properties Түйін қасиеттерін орнату\n"
"\n"
#: src/tools/pw-cat.c:1016 #: src/tools/pw-cat.c:1207
#, c-format #, c-format
msgid "" msgid ""
" --rate Sample rate (req. for rec) (default " " --rate Sample rate (default %u)\n"
"%u)\n" " --channels Number of channels (default %u)\n"
" --channels Number of channels (req. for rec) "
"(default %u)\n"
" --channel-map Channel map\n" " --channel-map Channel map\n"
" one of: \"stereo\", " " a channel layout: \"Stereo\", \"5.1\",... or\n"
"\"surround-51\",... or\n" " comma separated list of channel names: eg. \"FL,FR\"\n"
" comma separated list of channel " " --list-layouts List supported channel layouts\n"
"names: eg. \"FL,FR\"\n" " --list-channel-names List supported channel maps\n"
" --format Sample format %s (req. for rec) " " --format Sample format (default %s)\n"
"(default %s)\n" " --list-formats List supported sample formats\n"
" --container Container format\n"
" --list-containers List supported containers and extensions\n"
" --volume Stream volume 0-1.0 (default %.3f)\n" " --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default " " -q --quality Resampler quality (0 - 15) (default %d)\n"
"%d)\n" " -a, --raw RAW mode\n"
" -M, --force-midi Force midi format, one of \"midi\" or \"ump\", (default ump)\n"
" -n, --sample-count COUNT Stop after COUNT samples\n"
"\n" "\n"
msgstr "" msgstr ""
" --rate Дискреттеу жиілігі (бастапқы %u)\n"
" --channels Арналар саны (бастапқы %u)\n"
" --channel-map Арналар картасы\n"
" арна жаймасы: «Stereo», «5.1»,... немесе\n"
" үтірмен ажыратылған арна атауларының тізімі: мысалы, "
"«FL,FR»\n"
" --list-layouts Қолдау көрсетілетін арна жаймаларын тізімдеу\n"
" --list-channel-names Қолдау көрсетілетін арналар картасын тізімдеу\n"
" --format Дискреттеу пішімі (бастапқы %s)\n"
" --list-formats Қолдау көрсетілетін дискреттеу пішімдерін тізімдеу\n"
" --container Контейнер пішімі\n"
" --list-containers Қолдау көрсетілетін контейнерлер мен кеңейтулерді тізімдеу\n"
" --volume Ағын дыбыс деңгейі 0-1.0 (бастапқы %.3f)\n"
" -q --quality Қайта дискреттеу сапасы (0 - 15) (бастапқы %d)\n"
" -a, --raw RAW режимі\n"
" -M, --force-midi Midi пішімін мәжбүрлеу, «midi» немесе «ump» біреуі, (бастапқы "
"ump)\n"
" -n, --sample-count COUNT COUNT үлгісінен кейін тоқтату\n"
"\n"
#: src/tools/pw-cat.c:1033 #: src/tools/pw-cat.c:1232
msgid "" msgid ""
" -p, --playback Playback mode\n" " -p, --playback Playback mode\n"
" -r, --record Recording mode\n" " -r, --record Recording mode\n"
" -m, --midi Midi mode\n" " -m, --midi Midi mode\n"
" -d, --dsd DSD mode\n"
" -o, --encoded Encoded mode\n"
" -s, --sysex SysEx mode\n"
" -c, --midi-clip MIDI clip mode\n"
"\n" "\n"
msgstr "" msgstr ""
" -p, --playback Ойнату режимі\n"
" -r, --record Жазу режимі\n"
" -m, --midi Midi режимі\n"
" -d, --dsd DSD режимі\n"
" -o, --encoded Шифрленген режим\n"
" -s, --sysex SysEx режимі\n"
" -c, --midi-clip MIDI clip режимі\n"
"\n"
#: src/tools/pw-cli.c:2932 #: src/tools/pw-cat.c:1837
#, c-format
msgid "Supported containers and extensions:\n"
msgstr "Қолдау көрсетілетін контейнерлер мен кеңейтулер:\n"
#: src/tools/pw-cli.c:2386
#, c-format #, c-format
msgid "" msgid ""
"%s [options] [command]\n" "%s [options] [command]\n"
@ -114,465 +216,506 @@ msgid ""
" --version Show version\n" " --version Show version\n"
" -d, --daemon Start as daemon (Default false)\n" " -d, --daemon Start as daemon (Default false)\n"
" -r, --remote Remote daemon name\n" " -r, --remote Remote daemon name\n"
" -m, --monitor Monitor activity\n"
"\n" "\n"
msgstr "" msgstr ""
"%s [опциялар] [команда]\n"
" -h, --help Осы көмекті көрсету\n"
" --version Нұсқасын көрсету\n"
" -d, --daemon Қызмет ретінде іске қосу (Бастапқы false)\n"
" -r, --remote Қашықтағы қызмет атауы\n"
" -m, --monitor Белсенділікті бақылау\n"
"\n"
#: spa/plugins/alsa/acp/acp.c:290 #: spa/plugins/alsa/acp/acp.c:361
msgid "Pro Audio" msgid "Pro Audio"
msgstr "" msgstr "Кәсіби аудио"
#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704 #: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:1000 #: spa/plugins/bluez5/bluez5-device.c:2021
msgid "Off" msgid "Off"
msgstr "Сөнд." msgstr "Сөнд."
#: spa/plugins/alsa/acp/channelmap.h:466 #: spa/plugins/alsa/acp/acp.c:618
msgid "(invalid)" #, c-format
msgstr "(жарамсыз)" msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM қатесі]"
#: spa/plugins/alsa/acp/alsa-mixer.c:2709 #: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Input" msgid "Input"
msgstr "Кіріс" msgstr "Кіріс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2710 #: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "Docking Station Input" msgid "Docking Station Input"
msgstr "Док-станция кірісі" msgstr "Док-станция кірісі"
#: spa/plugins/alsa/acp/alsa-mixer.c:2711 #: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Docking Station Microphone" msgid "Docking Station Microphone"
msgstr "Док-станция микрофоны" msgstr "Док-станция микрофоны"
#: spa/plugins/alsa/acp/alsa-mixer.c:2712 #: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "Docking Station Line In" msgid "Docking Station Line In"
msgstr "Док-станцияның сызықтық кірісі" msgstr "Док-станцияның сызықтық кірісі"
#: spa/plugins/alsa/acp/alsa-mixer.c:2713 #: spa/plugins/alsa/acp/alsa-mixer.c:2725 spa/plugins/alsa/acp/alsa-mixer.c:2816
#: spa/plugins/alsa/acp/alsa-mixer.c:2804
msgid "Line In" msgid "Line In"
msgstr "Сызықтық кіріс" msgstr "Сызықтық кіріс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2714 #: spa/plugins/alsa/acp/alsa-mixer.c:2726 spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/alsa/acp/alsa-mixer.c:2798 #: spa/plugins/bluez5/bluez5-device.c:2422
#: spa/plugins/bluez5/bluez5-device.c:1145
msgid "Microphone" msgid "Microphone"
msgstr "Микрофон" msgstr "Микрофон"
#: spa/plugins/alsa/acp/alsa-mixer.c:2715 #: spa/plugins/alsa/acp/alsa-mixer.c:2727 spa/plugins/alsa/acp/alsa-mixer.c:2811
#: spa/plugins/alsa/acp/alsa-mixer.c:2799
msgid "Front Microphone" msgid "Front Microphone"
msgstr "Алдыңғы микрофон" msgstr "Алдыңғы микрофон"
#: spa/plugins/alsa/acp/alsa-mixer.c:2716 #: spa/plugins/alsa/acp/alsa-mixer.c:2728 spa/plugins/alsa/acp/alsa-mixer.c:2812
#: spa/plugins/alsa/acp/alsa-mixer.c:2800
msgid "Rear Microphone" msgid "Rear Microphone"
msgstr "Артқы микрофон" msgstr "Артқы микрофон"
#: spa/plugins/alsa/acp/alsa-mixer.c:2717 #: spa/plugins/alsa/acp/alsa-mixer.c:2729
msgid "External Microphone" msgid "External Microphone"
msgstr "Сыртқы микрофон" msgstr "Сыртқы микрофон"
#: spa/plugins/alsa/acp/alsa-mixer.c:2718 #: spa/plugins/alsa/acp/alsa-mixer.c:2730 spa/plugins/alsa/acp/alsa-mixer.c:2814
#: spa/plugins/alsa/acp/alsa-mixer.c:2802
msgid "Internal Microphone" msgid "Internal Microphone"
msgstr "Ішкі микрофон" msgstr "Ішкі микрофон"
#: spa/plugins/alsa/acp/alsa-mixer.c:2719 #: spa/plugins/alsa/acp/alsa-mixer.c:2731 spa/plugins/alsa/acp/alsa-mixer.c:2817
#: spa/plugins/alsa/acp/alsa-mixer.c:2805
msgid "Radio" msgid "Radio"
msgstr "Радио" msgstr "Радио"
#: spa/plugins/alsa/acp/alsa-mixer.c:2720 #: spa/plugins/alsa/acp/alsa-mixer.c:2732 spa/plugins/alsa/acp/alsa-mixer.c:2818
#: spa/plugins/alsa/acp/alsa-mixer.c:2806
msgid "Video" msgid "Video"
msgstr "Видео" msgstr "Видео"
#: spa/plugins/alsa/acp/alsa-mixer.c:2721 #: spa/plugins/alsa/acp/alsa-mixer.c:2733
msgid "Automatic Gain Control" msgid "Automatic Gain Control"
msgstr "Күшейтуді автореттеу" msgstr "Күшейтуді автореттеу"
#: spa/plugins/alsa/acp/alsa-mixer.c:2722 #: spa/plugins/alsa/acp/alsa-mixer.c:2734
msgid "No Automatic Gain Control" msgid "No Automatic Gain Control"
msgstr "Күшейтуді автореттеу жоқ" msgstr "Күшейтуді автореттеу жоқ"
#: spa/plugins/alsa/acp/alsa-mixer.c:2723 #: spa/plugins/alsa/acp/alsa-mixer.c:2735
msgid "Boost" msgid "Boost"
msgstr "Күшейту" msgstr "Күшейту"
#: spa/plugins/alsa/acp/alsa-mixer.c:2724 #: spa/plugins/alsa/acp/alsa-mixer.c:2736
msgid "No Boost" msgid "No Boost"
msgstr "Күшейту жоқ" msgstr "Күшейту жоқ"
#: spa/plugins/alsa/acp/alsa-mixer.c:2725 #: spa/plugins/alsa/acp/alsa-mixer.c:2737
msgid "Amplifier" msgid "Amplifier"
msgstr "Күшейткіш" msgstr "Күшейткіш"
#: spa/plugins/alsa/acp/alsa-mixer.c:2726 #: spa/plugins/alsa/acp/alsa-mixer.c:2738
msgid "No Amplifier" msgid "No Amplifier"
msgstr "Күшейткіш жоқ" msgstr "Күшейткіш жоқ"
#: spa/plugins/alsa/acp/alsa-mixer.c:2727 #: spa/plugins/alsa/acp/alsa-mixer.c:2739
msgid "Bass Boost" msgid "Bass Boost"
msgstr "Бас күшейту" msgstr "Бас күшейту"
#: spa/plugins/alsa/acp/alsa-mixer.c:2728 #: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "No Bass Boost" msgid "No Bass Boost"
msgstr "Бас күшейту жоқ" msgstr "Бас күшейту жоқ"
#: spa/plugins/alsa/acp/alsa-mixer.c:2729 #: spa/plugins/alsa/acp/alsa-mixer.c:2741 spa/plugins/bluez5/bluez5-device.c:2428
#: spa/plugins/bluez5/bluez5-device.c:1150
msgid "Speaker" msgid "Speaker"
msgstr "Динамик" msgstr "Динамик"
#: spa/plugins/alsa/acp/alsa-mixer.c:2730 #. Don't call it "headset", the HF one has the mic
#: spa/plugins/alsa/acp/alsa-mixer.c:2808 #: spa/plugins/alsa/acp/alsa-mixer.c:2742 spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/bluez5/bluez5-device.c:2434 spa/plugins/bluez5/bluez5-device.c:2501
msgid "Headphones" msgid "Headphones"
msgstr "Құлаққаптар" msgstr "Құлаққаптар"
#: spa/plugins/alsa/acp/alsa-mixer.c:2797 #: spa/plugins/alsa/acp/alsa-mixer.c:2809
msgid "Analog Input" msgid "Analog Input"
msgstr "Аналогтық кіріс" msgstr "Аналогтық кіріс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2801 #: spa/plugins/alsa/acp/alsa-mixer.c:2813
msgid "Dock Microphone" msgid "Dock Microphone"
msgstr "Док-станция микрофоны" msgstr "Док-станция микрофоны"
#: spa/plugins/alsa/acp/alsa-mixer.c:2803 #: spa/plugins/alsa/acp/alsa-mixer.c:2815
msgid "Headset Microphone" msgid "Headset Microphone"
msgstr "Гарнитура микрофоны" msgstr "Гарнитура микрофоны"
#: spa/plugins/alsa/acp/alsa-mixer.c:2807 #: spa/plugins/alsa/acp/alsa-mixer.c:2819
msgid "Analog Output" msgid "Analog Output"
msgstr "Аналогтық шығыс" msgstr "Аналогтық шығыс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2809 #: spa/plugins/alsa/acp/alsa-mixer.c:2821
#, fuzzy
msgid "Headphones 2" msgid "Headphones 2"
msgstr "Құлаққаптар" msgstr "Құлаққап 2"
#: spa/plugins/alsa/acp/alsa-mixer.c:2810 #: spa/plugins/alsa/acp/alsa-mixer.c:2822
msgid "Headphones Mono Output" msgid "Headphones Mono Output"
msgstr "Құлаққаптардың моно шығысы" msgstr "Құлаққаптардың моно шығысы"
#: spa/plugins/alsa/acp/alsa-mixer.c:2811 #: spa/plugins/alsa/acp/alsa-mixer.c:2823
msgid "Line Out" msgid "Line Out"
msgstr "Сызықтық шығыс" msgstr "Сызықтық шығыс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2812 #: spa/plugins/alsa/acp/alsa-mixer.c:2824
msgid "Analog Mono Output" msgid "Analog Mono Output"
msgstr "Аналогтық моно шығысы" msgstr "Аналогтық моно шығысы"
#: spa/plugins/alsa/acp/alsa-mixer.c:2813 #: spa/plugins/alsa/acp/alsa-mixer.c:2825
msgid "Speakers" msgid "Speakers"
msgstr "Динамиктер" msgstr "Динамиктер"
#: spa/plugins/alsa/acp/alsa-mixer.c:2814 #: spa/plugins/alsa/acp/alsa-mixer.c:2826
msgid "HDMI / DisplayPort" msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort" msgstr "HDMI / DisplayPort"
#: spa/plugins/alsa/acp/alsa-mixer.c:2815 #: spa/plugins/alsa/acp/alsa-mixer.c:2827
msgid "Digital Output (S/PDIF)" msgid "Digital Output (S/PDIF)"
msgstr "Цифрлық шығыс (S/PDIF)" msgstr "Цифрлық шығыс (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2816 #: spa/plugins/alsa/acp/alsa-mixer.c:2828
msgid "Digital Input (S/PDIF)" msgid "Digital Input (S/PDIF)"
msgstr "Цифрлық кіріс (S/PDIF)" msgstr "Цифрлық кіріс (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2817 #: spa/plugins/alsa/acp/alsa-mixer.c:2829
msgid "Multichannel Input" msgid "Multichannel Input"
msgstr "Көпарналы кіріс" msgstr "Көпарналы кіріс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2818 #: spa/plugins/alsa/acp/alsa-mixer.c:2830
msgid "Multichannel Output" msgid "Multichannel Output"
msgstr "Көпарналы шығыс" msgstr "Көпарналы шығыс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2819 #: spa/plugins/alsa/acp/alsa-mixer.c:2831
msgid "Game Output" msgid "Game Output"
msgstr "Ойын шығысы" msgstr "Ойын шығысы"
#: spa/plugins/alsa/acp/alsa-mixer.c:2820 #: spa/plugins/alsa/acp/alsa-mixer.c:2832 spa/plugins/alsa/acp/alsa-mixer.c:2833
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
msgid "Chat Output" msgid "Chat Output"
msgstr "Чат шығысы" msgstr "Чат шығысы"
#: spa/plugins/alsa/acp/alsa-mixer.c:2822 #: spa/plugins/alsa/acp/alsa-mixer.c:2834
#, fuzzy
msgid "Chat Input" msgid "Chat Input"
msgstr "Чат шығысы" msgstr "Чат кірісі"
#: spa/plugins/alsa/acp/alsa-mixer.c:2823 #: spa/plugins/alsa/acp/alsa-mixer.c:2835
#, fuzzy
msgid "Virtual Surround 7.1" msgid "Virtual Surround 7.1"
msgstr "Виртуалды көлемді аудиоқабылдағыш" msgstr "Виртуалды көлемді дыбыс 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4527 #: spa/plugins/alsa/acp/alsa-mixer.c:4522
msgid "Analog Mono" msgid "Analog Mono"
msgstr "Аналогтық моно" msgstr "Аналогтық моно"
#: spa/plugins/alsa/acp/alsa-mixer.c:4528 #: spa/plugins/alsa/acp/alsa-mixer.c:4523
#, fuzzy
msgid "Analog Mono (Left)" msgid "Analog Mono (Left)"
msgstr "Аналогтық моно" msgstr "Аналогты моно (Сол жақ)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4529 #: spa/plugins/alsa/acp/alsa-mixer.c:4524
#, fuzzy
msgid "Analog Mono (Right)" msgid "Analog Mono (Right)"
msgstr "Аналогтық моно" msgstr "Аналогты моно (Оң жақ)"
#. Note: Not translated to "Analog Stereo Input", because the source #. Note: Not translated to "Analog Stereo Input", because the source
#. * name gets "Input" appended to it automatically, so adding "Input" #. * name gets "Input" appended to it automatically, so adding "Input"
#. * here would lead to the source name to become "Analog Stereo Input #. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output, #. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output. #. * multichannel-input and multichannel-output.
#: spa/plugins/alsa/acp/alsa-mixer.c:4530 #: spa/plugins/alsa/acp/alsa-mixer.c:4525 spa/plugins/alsa/acp/alsa-mixer.c:4533
#: spa/plugins/alsa/acp/alsa-mixer.c:4538 #: spa/plugins/alsa/acp/alsa-mixer.c:4534
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Stereo" msgid "Analog Stereo"
msgstr "Аналогтық стерео" msgstr "Аналогтық стерео"
#: spa/plugins/alsa/acp/alsa-mixer.c:4531 #: spa/plugins/alsa/acp/alsa-mixer.c:4526
msgid "Mono" msgid "Mono"
msgstr "Моно" msgstr "Моно"
#: spa/plugins/alsa/acp/alsa-mixer.c:4532 #: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Stereo" msgid "Stereo"
msgstr "Стерео" msgstr "Стерео"
#: spa/plugins/alsa/acp/alsa-mixer.c:4540 #: spa/plugins/alsa/acp/alsa-mixer.c:4535 spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/alsa/acp/alsa-mixer.c:4698 #: spa/plugins/bluez5/bluez5-device.c:2410
#: spa/plugins/bluez5/bluez5-device.c:1135
msgid "Headset" msgid "Headset"
msgstr "Гарнитура" msgstr "Гарнитура"
#: spa/plugins/alsa/acp/alsa-mixer.c:4541 #: spa/plugins/alsa/acp/alsa-mixer.c:4536 spa/plugins/alsa/acp/alsa-mixer.c:4694
#: spa/plugins/alsa/acp/alsa-mixer.c:4699
#, fuzzy
msgid "Speakerphone" msgid "Speakerphone"
msgstr "Динамик" msgstr "Динамик"
#: spa/plugins/alsa/acp/alsa-mixer.c:4542 #: spa/plugins/alsa/acp/alsa-mixer.c:4537 spa/plugins/alsa/acp/alsa-mixer.c:4538
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Multichannel" msgid "Multichannel"
msgstr "Көпарналы" msgstr "Көпарналы"
#: spa/plugins/alsa/acp/alsa-mixer.c:4544 #: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Surround 2.1" msgid "Analog Surround 2.1"
msgstr "Аналогтық көлемді 2.1" msgstr "Аналогтық көлемді 2.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4545 #: spa/plugins/alsa/acp/alsa-mixer.c:4540
msgid "Analog Surround 3.0" msgid "Analog Surround 3.0"
msgstr "Аналогтық көлемді 3.0" msgstr "Аналогтық көлемді 3.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4546 #: spa/plugins/alsa/acp/alsa-mixer.c:4541
msgid "Analog Surround 3.1" msgid "Analog Surround 3.1"
msgstr "Аналогтық көлемді 3.1" msgstr "Аналогтық көлемді 3.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4547 #: spa/plugins/alsa/acp/alsa-mixer.c:4542
msgid "Analog Surround 4.0" msgid "Analog Surround 4.0"
msgstr "Аналогтық көлемді 4.0" msgstr "Аналогтық көлемді 4.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4548 #: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Analog Surround 4.1" msgid "Analog Surround 4.1"
msgstr "Аналогтық көлемді 4.1" msgstr "Аналогтық көлемді 4.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4549 #: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 5.0" msgid "Analog Surround 5.0"
msgstr "Аналогтық көлемді 5.0" msgstr "Аналогтық көлемді 5.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4550 #: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 5.1" msgid "Analog Surround 5.1"
msgstr "Аналогтық көлемді 5.1" msgstr "Аналогтық көлемді 5.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4551 #: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 6.0" msgid "Analog Surround 6.0"
msgstr "Аналогтық көлемді 6.0" msgstr "Аналогтық көлемді 6.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4552 #: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 6.1" msgid "Analog Surround 6.1"
msgstr "Аналогтық көлемді 6.1" msgstr "Аналогтық көлемді 6.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4553 #: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 7.0" msgid "Analog Surround 7.0"
msgstr "Аналогтық көлемді 7.0" msgstr "Аналогтық көлемді 7.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4554 #: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 7.1" msgid "Analog Surround 7.1"
msgstr "Аналогтық көлемді 7.1" msgstr "Аналогтық көлемді 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4555 #: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Digital Stereo (IEC958)" msgid "Digital Stereo (IEC958)"
msgstr "Цифрлық стерео (IEC958)" msgstr "Цифрлық стерео (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4556 #: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Digital Surround 4.0 (IEC958/AC3)" msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Цифрлық көлемді 4.0 (IEC958/AC3)" msgstr "Цифрлық көлемді 4.0 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4557 #: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Digital Surround 5.1 (IEC958/AC3)" msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Цифрлық көлемді 5.1 (IEC958/AC3)" msgstr "Цифрлық көлемді 5.1 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4558 #: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Digital Surround 5.1 (IEC958/DTS)" msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "Цифрлық көлемді 5.1 (IEC958/DTS)" msgstr "Цифрлық көлемді 5.1 (IEC958/DTS)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4559 #: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Digital Stereo (HDMI)" msgid "Digital Stereo (HDMI)"
msgstr "Цифрлық стерео (HDMI)" msgstr "Цифрлық стерео (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4560 #: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Surround 5.1 (HDMI)" msgid "Digital Surround 5.1 (HDMI)"
msgstr "Цифрлық көлемді 5.1 (HDMI)" msgstr "Цифрлық көлемді 5.1 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4561 #: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Chat" msgid "Chat"
msgstr "" msgstr "Чат"
#: spa/plugins/alsa/acp/alsa-mixer.c:4562 #: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Game" msgid "Game"
msgstr "" msgstr "Ойын"
#: spa/plugins/alsa/acp/alsa-mixer.c:4696 #: spa/plugins/alsa/acp/alsa-mixer.c:4691
msgid "Analog Mono Duplex" msgid "Analog Mono Duplex"
msgstr "Аналогтық моно дуплекс" msgstr "Аналогтық моно дуплекс"
#: spa/plugins/alsa/acp/alsa-mixer.c:4697 #: spa/plugins/alsa/acp/alsa-mixer.c:4692
msgid "Analog Stereo Duplex" msgid "Analog Stereo Duplex"
msgstr "Аналогтық стерео дуплекс" msgstr "Аналогтық стерео дуплекс"
#: spa/plugins/alsa/acp/alsa-mixer.c:4700 #: spa/plugins/alsa/acp/alsa-mixer.c:4695
msgid "Digital Stereo Duplex (IEC958)" msgid "Digital Stereo Duplex (IEC958)"
msgstr "Цифрлық стерео дуплекс (IEC958)" msgstr "Цифрлық стерео дуплекс (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4701 #: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Multichannel Duplex" msgid "Multichannel Duplex"
msgstr "Көпарналы дуплекс" msgstr "Көпарналы дуплекс"
#: spa/plugins/alsa/acp/alsa-mixer.c:4702 #: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Stereo Duplex" msgid "Stereo Duplex"
msgstr "Стерео дуплекс" msgstr "Стерео дуплекс"
#: spa/plugins/alsa/acp/alsa-mixer.c:4703 #: spa/plugins/alsa/acp/alsa-mixer.c:4698
msgid "Mono Chat + 7.1 Surround" msgid "Mono Chat + 7.1 Surround"
msgstr "" msgstr "Моно чат + 7.1 көлемді дыбыс"
#: spa/plugins/alsa/acp/alsa-mixer.c:4806 #: spa/plugins/alsa/acp/alsa-mixer.c:4799
#, c-format #, c-format
msgid "%s Output" msgid "%s Output"
msgstr "%s шығысы" msgstr "%s шығысы"
#: spa/plugins/alsa/acp/alsa-mixer.c:4813 #: spa/plugins/alsa/acp/alsa-mixer.c:4807
#, c-format #, c-format
msgid "%s Input" msgid "%s Input"
msgstr "%s кірісі" msgstr "%s кірісі"
#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269 #: spa/plugins/alsa/acp/alsa-util.c:1233 spa/plugins/alsa/acp/alsa-util.c:1327
#, c-format #, c-format
msgid "" msgid ""
"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu " "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu ms).\n"
"ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgid_plural "" msgid_plural ""
"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu " "snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
"ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] "" msgstr[0] ""
"snd_pcm_avail() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
msgstr[1] "" msgstr[1] ""
"snd_pcm_avail() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
#: spa/plugins/alsa/acp/alsa-util.c:1241 #: spa/plugins/alsa/acp/alsa-util.c:1299
#, c-format #, c-format
msgid "" msgid ""
"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s" "snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s%lu ms).\n"
"%lu ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgid_plural "" msgid_plural ""
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s" "snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s%lu ms).\n"
"%lu ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] "" msgstr[0] ""
"snd_pcm_delay() өте үлкен мән қайтарды: %li байт (%s%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
msgstr[1] "" msgstr[1] ""
"snd_pcm_delay() өте үлкен мән қайтарды: %li байт (%s%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
#: spa/plugins/alsa/acp/alsa-util.c:1288 #: spa/plugins/alsa/acp/alsa-util.c:1346
#, c-format #, c-format
msgid "" msgid ""
"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail " "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail %lu.\n"
"%lu.\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr "" msgstr ""
"snd_pcm_avail_delay() оғаш мәндер қайтарды: кідіріс %lu мәні қолжетімді %lu мәнінен аз.\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
#: spa/plugins/alsa/acp/alsa-util.c:1331 #: spa/plugins/alsa/acp/alsa-util.c:1389
#, c-format #, c-format
msgid "" msgid ""
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte " "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte (%lu ms).\n"
"(%lu ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgid_plural "" msgid_plural ""
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes " "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
"(%lu ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] "" msgstr[0] ""
"snd_pcm_mmap_begin() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
msgstr[1] "" msgstr[1] ""
"snd_pcm_mmap_begin() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
#: spa/plugins/bluez5/bluez5-device.c:1010 #: spa/plugins/alsa/acp/channelmap.h:460
msgid "(invalid)"
msgstr "(жарамсыз)"
#: spa/plugins/alsa/acp/compat.c:194
msgid "Built-in Audio"
msgstr "Құрамындағы аудио"
#: spa/plugins/alsa/acp/compat.c:199
msgid "Modem"
msgstr "Модем"
#: spa/plugins/bluez5/bluez5-device.c:2032
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "" msgstr "Аудио шлюзі (A2DP бастапқы көзі және HSP/HFP AG)"
#: spa/plugins/bluez5/bluez5-device.c:1033 #: spa/plugins/bluez5/bluez5-device.c:2061
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "Есту аппараттарына арналған аудио ағыны (ASHA қабылдағышы)"
#: spa/plugins/bluez5/bluez5-device.c:2104
#, c-format #, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr "" msgstr "Жоғары сапалы ойнату (A2DP қабылдағышы, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:1035 #: spa/plugins/bluez5/bluez5-device.c:2107
#, c-format #, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr "" msgstr "Жоғары сапалы дуплекс (A2DP бастапқы көзі/қабылдағышы, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:1041 #: spa/plugins/bluez5/bluez5-device.c:2115
msgid "High Fidelity Playback (A2DP Sink)" msgid "High Fidelity Playback (A2DP Sink)"
msgstr "" msgstr "Жоғары сапалы ойнату (A2DP қабылдағышы)"
#: spa/plugins/bluez5/bluez5-device.c:1043 #: spa/plugins/bluez5/bluez5-device.c:2117
msgid "High Fidelity Duplex (A2DP Source/Sink)" msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "" msgstr "Жоғары сапалы дуплекс (A2DP бастапқы көзі/қабылдағышы)"
#: spa/plugins/bluez5/bluez5-device.c:1070 #: spa/plugins/bluez5/bluez5-device.c:2194
#, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "Жоғары сапалы ойнату (BAP қабылдағышы, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:2199
#, c-format
msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "Жоғары сапалы кіріс (BAP бастапқы көзі, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:2203
#, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "Жоғары сапалы дуплекс (BAP бастапқы көзі/қабылдағышы, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:2212
msgid "High Fidelity Playback (BAP Sink)"
msgstr "Жоғары сапалы ойнату (BAP қабылдағышы)"
#: spa/plugins/bluez5/bluez5-device.c:2216
msgid "High Fidelity Input (BAP Source)"
msgstr "Жоғары сапалы кіріс (BAP бастапқы көзі)"
#: spa/plugins/bluez5/bluez5-device.c:2219
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "Жоғары сапалы дуплекс (BAP бастапқы көзі/қабылдағышы)"
#: spa/plugins/bluez5/bluez5-device.c:2259
#, c-format #, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)" msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr "" msgstr "Гарнитура (HSP/HFP, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:1074 #: spa/plugins/bluez5/bluez5-device.c:2411 spa/plugins/bluez5/bluez5-device.c:2416
msgid "Headset Head Unit (HSP/HFP)" #: spa/plugins/bluez5/bluez5-device.c:2423 spa/plugins/bluez5/bluez5-device.c:2429
msgstr "" #: spa/plugins/bluez5/bluez5-device.c:2435 spa/plugins/bluez5/bluez5-device.c:2441
#: spa/plugins/bluez5/bluez5-device.c:2447 spa/plugins/bluez5/bluez5-device.c:2453
#: spa/plugins/bluez5/bluez5-device.c:1140 #: spa/plugins/bluez5/bluez5-device.c:2459
msgid "Handsfree" msgid "Handsfree"
msgstr "Хендс-фри" msgstr "Хендс-фри"
#: spa/plugins/bluez5/bluez5-device.c:1155 #: spa/plugins/bluez5/bluez5-device.c:2417
msgid "Headphone" msgid "Handsfree (HFP)"
msgstr "Құлаққап" msgstr "Гарнитура (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:1160 #: spa/plugins/bluez5/bluez5-device.c:2440
msgid "Portable" msgid "Portable"
msgstr "Портативті динамик" msgstr "Портативті динамик"
#: spa/plugins/bluez5/bluez5-device.c:1165 #: spa/plugins/bluez5/bluez5-device.c:2446
msgid "Car" msgid "Car"
msgstr "Автомобильдік динамик" msgstr "Автомобильдік динамик"
#: spa/plugins/bluez5/bluez5-device.c:1170 #: spa/plugins/bluez5/bluez5-device.c:2452
msgid "HiFi" msgid "HiFi"
msgstr "HiFi" msgstr "HiFi"
#: spa/plugins/bluez5/bluez5-device.c:1175 #: spa/plugins/bluez5/bluez5-device.c:2458
msgid "Phone" msgid "Phone"
msgstr "Телефон" msgstr "Телефон"
#: spa/plugins/bluez5/bluez5-device.c:1181 #: spa/plugins/bluez5/bluez5-device.c:2465
msgid "Bluetooth" msgid "Bluetooth"
msgstr "Bluetooth" msgstr "Bluetooth"
#: spa/plugins/bluez5/bluez5-device.c:2466
msgid "Bluetooth Handsfree"
msgstr "Bluetooth гарнитурасы"
#~ msgid "Headphone"
#~ msgstr "Құлаққап"

371
po/sl.po
View file

@ -2,23 +2,21 @@
# Copyright (C) 2024 PipeWire's COPYRIGHT HOLDER # Copyright (C) 2024 PipeWire's COPYRIGHT HOLDER
# This file is distributed under the same license as the PipeWire package. # This file is distributed under the same license as the PipeWire package.
# #
# Martin <miles@filmsi.net>, 2024, 2025. # Martin <miles@filmsi.net>, 2024, 2025, 2026.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PipeWire master\n" "Project-Id-Version: PipeWire master\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues\n"
"issues\n" "POT-Creation-Date: 2026-02-09 12:55+0000\n"
"POT-Creation-Date: 2025-09-11 03:34+0000\n" "PO-Revision-Date: 2026-02-15 16:18+0100\n"
"PO-Revision-Date: 2025-09-11 11:47+0200\n"
"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n" "Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
"Language-Team: Slovenian <gnome-si@googlegroups.com>\n" "Language-Team: Slovenian <gnome-si@googlegroups.com>\n"
"Language: sl\n" "Language: sl\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && " "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n%100<=4 ? 2 : 3);\n"
"n%100<=4 ? 2 : 3);\n"
"X-Generator: Poedit 2.2.1\n" "X-Generator: Poedit 2.2.1\n"
#: src/daemon/pipewire.c:29 #: src/daemon/pipewire.c:29
@ -35,8 +33,7 @@ msgstr ""
" -h, --help Pokaži to pomoč\n" " -h, --help Pokaži to pomoč\n"
" -v, --verbose Povečaj opisnost za eno raven\n" " -v, --verbose Povečaj opisnost za eno raven\n"
" --version Pokaži različico\n" " --version Pokaži različico\n"
" -c, --config Naloži prilagoditev config (privzeto " " -c, --config Naloži prilagoditev config (privzeto %s)\n"
"%s)\n"
" -P --properties Določi lastnosti konteksta\n" " -P --properties Določi lastnosti konteksta\n"
#: src/daemon/pipewire.desktop.in:3 #: src/daemon/pipewire.desktop.in:3
@ -57,26 +54,51 @@ msgstr "Prehod do %s%s%s"
msgid "Dummy Output" msgid "Dummy Output"
msgstr "Lažni izhod" msgstr "Lažni izhod"
#: src/modules/module-pulse-tunnel.c:760 #: src/modules/module-pulse-tunnel.c:761
#, c-format #, c-format
msgid "Tunnel for %s@%s" msgid "Tunnel for %s@%s"
msgstr "Prehod za %s@%s" msgstr "Prehod za %s@%s"
#: src/modules/module-zeroconf-discover.c:320 #: src/modules/module-zeroconf-discover.c:326
msgid "Unknown device" msgid "Unknown device"
msgstr "Neznana naprava" msgstr "Neznana naprava"
#: src/modules/module-zeroconf-discover.c:332 #: src/modules/module-zeroconf-discover.c:338
#, c-format #, c-format
msgid "%s on %s@%s" msgid "%s on %s@%s"
msgstr "%s na %s@%s" msgstr "%s na %s@%s"
#: src/modules/module-zeroconf-discover.c:336 #: src/modules/module-zeroconf-discover.c:342
#, c-format #, c-format
msgid "%s on %s" msgid "%s on %s"
msgstr "%s na %s" msgstr "%s na %s"
#: src/tools/pw-cat.c:1084 #: src/tools/pw-cat.c:264
#, c-format
msgid "Supported formats:\n"
msgstr "Podprti zapisi:\n"
#: src/tools/pw-cat.c:749
#, c-format
msgid "Supported channel layouts:\n"
msgstr "Podprte postavitve kanalov:\n"
#: src/tools/pw-cat.c:759
#, c-format
msgid "Supported channel layout aliases:\n"
msgstr "Podprti vzdevki postavitev kanalov:\n"
#: src/tools/pw-cat.c:761
#, c-format
msgid " %s -> %s\n"
msgstr " %s -> %s\n"
#: src/tools/pw-cat.c:766
#, c-format
msgid "Supported channel names:\n"
msgstr "Podprta imena kanalov:\n"
#: src/tools/pw-cat.c:1177
#, c-format #, c-format
msgid "" msgid ""
"%s [options] [<file>|-]\n" "%s [options] [<file>|-]\n"
@ -92,7 +114,7 @@ msgstr ""
"\n" "\n"
"</file>\n" "</file>\n"
#: src/tools/pw-cat.c:1091 #: src/tools/pw-cat.c:1184
#, c-format #, c-format
msgid "" msgid ""
" -R, --remote Remote daemon name\n" " -R, --remote Remote daemon name\n"
@ -129,20 +151,23 @@ msgstr ""
" -P --properties Nastavi lastnosti vozlišča\n" " -P --properties Nastavi lastnosti vozlišča\n"
"\n" "\n"
#: src/tools/pw-cat.c:1109 #: src/tools/pw-cat.c:1202
#, c-format #, c-format
msgid "" msgid ""
" --rate Sample rate (req. for rec) (default " " --rate Sample rate (default %u)\n"
"%u)\n" " --channels Number of channels (default %u)\n"
" --channels Number of channels (req. for rec) "
"(default %u)\n"
" --channel-map Channel map\n" " --channel-map Channel map\n"
" one of: \"stereo\", " " a channel layout: \"Stereo\", "
"\"surround-51\",... or\n" "\"5.1\",... or\n"
" comma separated list of channel " " comma separated list of channel "
"names: eg. \"FL,FR\"\n" "names: eg. \"FL,FR\"\n"
" --format Sample format %s (req. for rec) " " --list-layouts List supported channel layouts\n"
"(default %s)\n" " --list-channel-names List supported channel maps\n"
" --format Sample format (default %s)\n"
" --list-formats List supported sample formats\n"
" --container Container format\n"
" --list-containers List supported containers and "
"extensions\n"
" --volume Stream volume 0-1.0 (default %.3f)\n" " --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default " " -q --quality Resampler quality (0 - 15) (default "
"%d)\n" "%d)\n"
@ -157,22 +182,27 @@ msgstr ""
" --channels Število kanalov (zaht. za snemanje) " " --channels Število kanalov (zaht. za snemanje) "
"(privzeto %u)\n" "(privzeto %u)\n"
" --channel-map Preslikava kanalov\n" " --channel-map Preslikava kanalov\n"
" Ena izmed: \"stereo\", " " Ena izmed: \"Stereo\", "
"\"surround-51\",... ali\n" "\"5.1\",... ali\n"
" seznam imen kanalov, ločen z " " seznam imen kanalov, ločen z "
"vejico: npr. \"FL,FR\"\n" "vejico: npr. \"FL,FR\"\n"
" --format Vzorčne oblike zapisa %s (zahtevano " " —list-layouts Izpiše podprte postavitve kanalov\n"
"za rec) (privzeto %s)\n" " —list-channel-names Izpiše podprte preslikave kanalov\n"
" --format Oblika zapisa vzorcev (privzeto %s)\n"
" —list-formats Izpiše podprte zapise vzorcev\n"
" —container Oblika vsebnika\n"
" —list-containers Seznam podprtih vsebnikov in "
"razširitev\n"
" --volume Glasnost toka 0-1.0 (privzeto %.3f)\n" " --volume Glasnost toka 0-1.0 (privzeto %.3f)\n"
" -q --quality Kakovost prevzorčenja (0 - 15) " " -q --quality Kakovost prevzorčenja (0 - 15) "
"(privzeto %d)\n" "(privzeto %d)\n"
" -a, --raw neobdelan način (RAW)\n" " -a, --raw neobdelan način (RAW)\n"
" -M, --force-midi Vsili zapis midi, eden izmed " " -M, --force-midi Vsili zapis midi, eden izmed \"midi"
"\"midi\" ali \"ump\" (privzeto ump)\n" "\" ali \"ump\" (privzeto ump)\n"
" -n, --sample-count ŠTEVEC Ustavi po ŠTEVEC vzorcih\n" " -n, --sample-count ŠTEVEC Ustavi po ŠTEVEC vzorcih\n"
"\n" "\n"
#: src/tools/pw-cat.c:1129 #: src/tools/pw-cat.c:1227
msgid "" msgid ""
" -p, --playback Playback mode\n" " -p, --playback Playback mode\n"
" -r, --record Recording mode\n" " -r, --record Recording mode\n"
@ -192,6 +222,11 @@ msgstr ""
" -c, --midi-clip Način posnetka MIDI\n" " -c, --midi-clip Način posnetka MIDI\n"
"\n" "\n"
#: src/tools/pw-cat.c:1827
#, c-format
msgid "Supported containers and extensions:\n"
msgstr "Podprti vsebniki in razširitve:\n"
#: src/tools/pw-cli.c:2386 #: src/tools/pw-cli.c:2386
#, c-format #, c-format
msgid "" msgid ""
@ -212,203 +247,203 @@ msgstr ""
" -m, --monitor Spremljaj dejavnosti\n" " -m, --monitor Spremljaj dejavnosti\n"
"\n" "\n"
#: spa/plugins/alsa/acp/acp.c:351 #: spa/plugins/alsa/acp/acp.c:361
msgid "Pro Audio" msgid "Pro Audio"
msgstr "Profesionalni zvok" msgstr "Profesionalni zvok"
#: spa/plugins/alsa/acp/acp.c:527 spa/plugins/alsa/acp/alsa-mixer.c:4635 #: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:1974 #: spa/plugins/bluez5/bluez5-device.c:2021
msgid "Off" msgid "Off"
msgstr "Izklopljeno" msgstr "Izklopljeno"
#: spa/plugins/alsa/acp/acp.c:610 #: spa/plugins/alsa/acp/acp.c:620
#, c-format #, c-format
msgid "%s [ALSA UCM error]" msgid "%s [ALSA UCM error]"
msgstr "%s [napaka ALSA UCM]" msgstr "%s [napaka ALSA UCM]"
#: spa/plugins/alsa/acp/alsa-mixer.c:2652 #: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Input" msgid "Input"
msgstr "Vhod" msgstr "Vhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2653 #: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "Docking Station Input" msgid "Docking Station Input"
msgstr "Vhod priklopne postaje" msgstr "Vhod priklopne postaje"
#: spa/plugins/alsa/acp/alsa-mixer.c:2654 #: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Docking Station Microphone" msgid "Docking Station Microphone"
msgstr "Mikrofon priklopne postaje" msgstr "Mikrofon priklopne postaje"
#: spa/plugins/alsa/acp/alsa-mixer.c:2655 #: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "Docking Station Line In" msgid "Docking Station Line In"
msgstr "Linijski vhod priklopne postaje" msgstr "Linijski vhod priklopne postaje"
#: spa/plugins/alsa/acp/alsa-mixer.c:2656 #: spa/plugins/alsa/acp/alsa-mixer.c:2725
#: spa/plugins/alsa/acp/alsa-mixer.c:2747 #: spa/plugins/alsa/acp/alsa-mixer.c:2816
msgid "Line In" msgid "Line In"
msgstr "Linijski vhod" msgstr "Linijski vhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2657 #: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2741 #: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:2372 #: spa/plugins/bluez5/bluez5-device.c:2422
msgid "Microphone" msgid "Microphone"
msgstr "Mikrofon" msgstr "Mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2658 #: spa/plugins/alsa/acp/alsa-mixer.c:2727
#: spa/plugins/alsa/acp/alsa-mixer.c:2742 #: spa/plugins/alsa/acp/alsa-mixer.c:2811
msgid "Front Microphone" msgid "Front Microphone"
msgstr "Sprednji mikrofon" msgstr "Sprednji mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2659 #: spa/plugins/alsa/acp/alsa-mixer.c:2728
#: spa/plugins/alsa/acp/alsa-mixer.c:2743 #: spa/plugins/alsa/acp/alsa-mixer.c:2812
msgid "Rear Microphone" msgid "Rear Microphone"
msgstr "Zadnji mikrofon" msgstr "Zadnji mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2660 #: spa/plugins/alsa/acp/alsa-mixer.c:2729
msgid "External Microphone" msgid "External Microphone"
msgstr "Zunanji mikrofon" msgstr "Zunanji mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2661 #: spa/plugins/alsa/acp/alsa-mixer.c:2730
#: spa/plugins/alsa/acp/alsa-mixer.c:2745 #: spa/plugins/alsa/acp/alsa-mixer.c:2814
msgid "Internal Microphone" msgid "Internal Microphone"
msgstr "Notranji mikrofon" msgstr "Notranji mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2662 #: spa/plugins/alsa/acp/alsa-mixer.c:2731
#: spa/plugins/alsa/acp/alsa-mixer.c:2748 #: spa/plugins/alsa/acp/alsa-mixer.c:2817
msgid "Radio" msgid "Radio"
msgstr "Radio" msgstr "Radio"
#: spa/plugins/alsa/acp/alsa-mixer.c:2663 #: spa/plugins/alsa/acp/alsa-mixer.c:2732
#: spa/plugins/alsa/acp/alsa-mixer.c:2749 #: spa/plugins/alsa/acp/alsa-mixer.c:2818
msgid "Video" msgid "Video"
msgstr "Video" msgstr "Video"
#: spa/plugins/alsa/acp/alsa-mixer.c:2664 #: spa/plugins/alsa/acp/alsa-mixer.c:2733
msgid "Automatic Gain Control" msgid "Automatic Gain Control"
msgstr "Samodejni nadzor ojačanja" msgstr "Samodejni nadzor ojačanja"
#: spa/plugins/alsa/acp/alsa-mixer.c:2665 #: spa/plugins/alsa/acp/alsa-mixer.c:2734
msgid "No Automatic Gain Control" msgid "No Automatic Gain Control"
msgstr "Brez samodejnega nadzora ojačanja" msgstr "Brez samodejnega nadzora ojačanja"
#: spa/plugins/alsa/acp/alsa-mixer.c:2666 #: spa/plugins/alsa/acp/alsa-mixer.c:2735
msgid "Boost" msgid "Boost"
msgstr "Ojačitev" msgstr "Ojačitev"
#: spa/plugins/alsa/acp/alsa-mixer.c:2667 #: spa/plugins/alsa/acp/alsa-mixer.c:2736
msgid "No Boost" msgid "No Boost"
msgstr "Brez ojačitve" msgstr "Brez ojačitve"
#: spa/plugins/alsa/acp/alsa-mixer.c:2668 #: spa/plugins/alsa/acp/alsa-mixer.c:2737
msgid "Amplifier" msgid "Amplifier"
msgstr "Ojačevalnik" msgstr "Ojačevalnik"
#: spa/plugins/alsa/acp/alsa-mixer.c:2669 #: spa/plugins/alsa/acp/alsa-mixer.c:2738
msgid "No Amplifier" msgid "No Amplifier"
msgstr "Brez ojačevalnika" msgstr "Brez ojačevalnika"
#: spa/plugins/alsa/acp/alsa-mixer.c:2670 #: spa/plugins/alsa/acp/alsa-mixer.c:2739
msgid "Bass Boost" msgid "Bass Boost"
msgstr "Ojačitev nizkih tonov" msgstr "Ojačitev nizkih tonov"
#: spa/plugins/alsa/acp/alsa-mixer.c:2671 #: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "No Bass Boost" msgid "No Bass Boost"
msgstr "Brez ojačitve nizkih tonov" msgstr "Brez ojačitve nizkih tonov"
#: spa/plugins/alsa/acp/alsa-mixer.c:2672 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2378 #: spa/plugins/bluez5/bluez5-device.c:2428
msgid "Speaker" msgid "Speaker"
msgstr "Zvočnik" msgstr "Zvočnik"
#. Don't call it "headset", the HF one has the mic #. Don't call it "headset", the HF one has the mic
#: spa/plugins/alsa/acp/alsa-mixer.c:2673 #: spa/plugins/alsa/acp/alsa-mixer.c:2742
#: spa/plugins/alsa/acp/alsa-mixer.c:2751 #: spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/bluez5/bluez5-device.c:2384 #: spa/plugins/bluez5/bluez5-device.c:2434
#: spa/plugins/bluez5/bluez5-device.c:2451 #: spa/plugins/bluez5/bluez5-device.c:2501
msgid "Headphones" msgid "Headphones"
msgstr "Slušalke" msgstr "Slušalke"
#: spa/plugins/alsa/acp/alsa-mixer.c:2740 #: spa/plugins/alsa/acp/alsa-mixer.c:2809
msgid "Analog Input" msgid "Analog Input"
msgstr "Analogni vhod" msgstr "Analogni vhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2744 #: spa/plugins/alsa/acp/alsa-mixer.c:2813
msgid "Dock Microphone" msgid "Dock Microphone"
msgstr "Priklopni mikrofon" msgstr "Priklopni mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2746 #: spa/plugins/alsa/acp/alsa-mixer.c:2815
msgid "Headset Microphone" msgid "Headset Microphone"
msgstr "Mikrofon s slušalkami" msgstr "Mikrofon s slušalkami"
#: spa/plugins/alsa/acp/alsa-mixer.c:2750 #: spa/plugins/alsa/acp/alsa-mixer.c:2819
msgid "Analog Output" msgid "Analog Output"
msgstr "Analogni izhod" msgstr "Analogni izhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2752 #: spa/plugins/alsa/acp/alsa-mixer.c:2821
msgid "Headphones 2" msgid "Headphones 2"
msgstr "Slušalke 2" msgstr "Slušalke 2"
#: spa/plugins/alsa/acp/alsa-mixer.c:2753 #: spa/plugins/alsa/acp/alsa-mixer.c:2822
msgid "Headphones Mono Output" msgid "Headphones Mono Output"
msgstr "Mono izhod slušalk" msgstr "Mono izhod slušalk"
#: spa/plugins/alsa/acp/alsa-mixer.c:2754 #: spa/plugins/alsa/acp/alsa-mixer.c:2823
msgid "Line Out" msgid "Line Out"
msgstr "Linijsk izhod" msgstr "Linijski izhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2755 #: spa/plugins/alsa/acp/alsa-mixer.c:2824
msgid "Analog Mono Output" msgid "Analog Mono Output"
msgstr "Analogni mono izhod" msgstr "Analogni mono izhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2756 #: spa/plugins/alsa/acp/alsa-mixer.c:2825
msgid "Speakers" msgid "Speakers"
msgstr "Govorniki" msgstr "Zvočniki"
#: spa/plugins/alsa/acp/alsa-mixer.c:2757 #: spa/plugins/alsa/acp/alsa-mixer.c:2826
msgid "HDMI / DisplayPort" msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort" msgstr "HDMI / DisplayPort"
#: spa/plugins/alsa/acp/alsa-mixer.c:2758 #: spa/plugins/alsa/acp/alsa-mixer.c:2827
msgid "Digital Output (S/PDIF)" msgid "Digital Output (S/PDIF)"
msgstr "Digitalni izhod (S/PDIF)" msgstr "Digitalni izhod (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2759 #: spa/plugins/alsa/acp/alsa-mixer.c:2828
msgid "Digital Input (S/PDIF)" msgid "Digital Input (S/PDIF)"
msgstr "Digitalni vhod (S/PDIF)" msgstr "Digitalni vhod (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2760 #: spa/plugins/alsa/acp/alsa-mixer.c:2829
msgid "Multichannel Input" msgid "Multichannel Input"
msgstr "Večkanalni vhod" msgstr "Večkanalni vhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2761 #: spa/plugins/alsa/acp/alsa-mixer.c:2830
msgid "Multichannel Output" msgid "Multichannel Output"
msgstr "Večkanalni izhod" msgstr "Večkanalni izhod"
#: spa/plugins/alsa/acp/alsa-mixer.c:2762 #: spa/plugins/alsa/acp/alsa-mixer.c:2831
msgid "Game Output" msgid "Game Output"
msgstr "Vhod igre" msgstr "Vhod igre"
#: spa/plugins/alsa/acp/alsa-mixer.c:2763 #: spa/plugins/alsa/acp/alsa-mixer.c:2832
#: spa/plugins/alsa/acp/alsa-mixer.c:2764 #: spa/plugins/alsa/acp/alsa-mixer.c:2833
msgid "Chat Output" msgid "Chat Output"
msgstr "Izhod klepeta" msgstr "Izhod klepeta"
#: spa/plugins/alsa/acp/alsa-mixer.c:2765 #: spa/plugins/alsa/acp/alsa-mixer.c:2834
msgid "Chat Input" msgid "Chat Input"
msgstr "Vhod klepeta" msgstr "Vhod klepeta"
#: spa/plugins/alsa/acp/alsa-mixer.c:2766 #: spa/plugins/alsa/acp/alsa-mixer.c:2835
msgid "Virtual Surround 7.1" msgid "Virtual Surround 7.1"
msgstr "Navidezni prostorski zvok 7.1" msgstr "Navidezni prostorski zvok 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4458 #: spa/plugins/alsa/acp/alsa-mixer.c:4522
msgid "Analog Mono" msgid "Analog Mono"
msgstr "Analogni mono" msgstr "Analogni mono"
#: spa/plugins/alsa/acp/alsa-mixer.c:4459 #: spa/plugins/alsa/acp/alsa-mixer.c:4523
msgid "Analog Mono (Left)" msgid "Analog Mono (Left)"
msgstr "Analogni mono (levo)" msgstr "Analogni mono (levo)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4460 #: spa/plugins/alsa/acp/alsa-mixer.c:4524
msgid "Analog Mono (Right)" msgid "Analog Mono (Right)"
msgstr "Analogni mono (desno)" msgstr "Analogni mono (desno)"
@ -417,142 +452,142 @@ msgstr "Analogni mono (desno)"
#. * here would lead to the source name to become "Analog Stereo Input #. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output, #. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output. #. * multichannel-input and multichannel-output.
#: spa/plugins/alsa/acp/alsa-mixer.c:4461 #: spa/plugins/alsa/acp/alsa-mixer.c:4525
#: spa/plugins/alsa/acp/alsa-mixer.c:4469 #: spa/plugins/alsa/acp/alsa-mixer.c:4533
#: spa/plugins/alsa/acp/alsa-mixer.c:4470 #: spa/plugins/alsa/acp/alsa-mixer.c:4534
msgid "Analog Stereo" msgid "Analog Stereo"
msgstr "Analogni stereo" msgstr "Analogni stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4462 #: spa/plugins/alsa/acp/alsa-mixer.c:4526
msgid "Mono" msgid "Mono"
msgstr "Mono" msgstr "Mono"
#: spa/plugins/alsa/acp/alsa-mixer.c:4463 #: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Stereo" msgid "Stereo"
msgstr "Stereo" msgstr "Stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4471 #: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4629 #: spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:2360 #: spa/plugins/bluez5/bluez5-device.c:2410
msgid "Headset" msgid "Headset"
msgstr "Slušalka" msgstr "Slušalka"
#: spa/plugins/alsa/acp/alsa-mixer.c:4472 #: spa/plugins/alsa/acp/alsa-mixer.c:4536
#: spa/plugins/alsa/acp/alsa-mixer.c:4630 #: spa/plugins/alsa/acp/alsa-mixer.c:4694
msgid "Speakerphone" msgid "Speakerphone"
msgstr "Zvočnik telefona" msgstr "Zvočnik telefona"
#: spa/plugins/alsa/acp/alsa-mixer.c:4473 #: spa/plugins/alsa/acp/alsa-mixer.c:4537
#: spa/plugins/alsa/acp/alsa-mixer.c:4474 #: spa/plugins/alsa/acp/alsa-mixer.c:4538
msgid "Multichannel" msgid "Multichannel"
msgstr "Večkanalno" msgstr "Večkanalno"
#: spa/plugins/alsa/acp/alsa-mixer.c:4475 #: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Surround 2.1" msgid "Analog Surround 2.1"
msgstr "Analogni prostorski zvok 2.1" msgstr "Analogni prostorski zvok 2.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4476 #: spa/plugins/alsa/acp/alsa-mixer.c:4540
msgid "Analog Surround 3.0" msgid "Analog Surround 3.0"
msgstr "Analogni prostorski zvok 3.0" msgstr "Analogni prostorski zvok 3.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4477 #: spa/plugins/alsa/acp/alsa-mixer.c:4541
msgid "Analog Surround 3.1" msgid "Analog Surround 3.1"
msgstr "Analogni prostorski zvok 3.1" msgstr "Analogni prostorski zvok 3.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4478 #: spa/plugins/alsa/acp/alsa-mixer.c:4542
msgid "Analog Surround 4.0" msgid "Analog Surround 4.0"
msgstr "Analogni prostorski zvok 4.0" msgstr "Analogni prostorski zvok 4.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4479 #: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Analog Surround 4.1" msgid "Analog Surround 4.1"
msgstr "Analogni prostorski zvok 4.1" msgstr "Analogni prostorski zvok 4.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4480 #: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 5.0" msgid "Analog Surround 5.0"
msgstr "Analogni prostorski zvok 5.0" msgstr "Analogni prostorski zvok 5.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4481 #: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 5.1" msgid "Analog Surround 5.1"
msgstr "Analogni prostorski zvok 5.1" msgstr "Analogni prostorski zvok 5.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4482 #: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 6.0" msgid "Analog Surround 6.0"
msgstr "Analogni prostorski zvok 6.0" msgstr "Analogni prostorski zvok 6.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4483 #: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 6.1" msgid "Analog Surround 6.1"
msgstr "Analogni prostorski zvok 6.1" msgstr "Analogni prostorski zvok 6.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4484 #: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 7.0" msgid "Analog Surround 7.0"
msgstr "Analogni prostorski zvok 7.0" msgstr "Analogni prostorski zvok 7.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4485 #: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 7.1" msgid "Analog Surround 7.1"
msgstr "Analogni prostorski zvok 7.1" msgstr "Analogni prostorski zvok 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4486 #: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Digital Stereo (IEC958)" msgid "Digital Stereo (IEC958)"
msgstr "Digitalni stereo (IEC958)" msgstr "Digitalni stereo (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4487 #: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Digital Surround 4.0 (IEC958/AC3)" msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Digitalni prostorski zvok 4.0 (IEC958/AC3)" msgstr "Digitalni prostorski zvok 4.0 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4488 #: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Digital Surround 5.1 (IEC958/AC3)" msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Digitalni prostorski zvok 5.1 (IEC958/AC3)" msgstr "Digitalni prostorski zvok 5.1 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4489 #: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Digital Surround 5.1 (IEC958/DTS)" msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "Digitalni prostorski zvok 5.1 (IEC958/DTS)" msgstr "Digitalni prostorski zvok 5.1 (IEC958/DTS)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4490 #: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Digital Stereo (HDMI)" msgid "Digital Stereo (HDMI)"
msgstr "Digitalni stereo (HDMI)" msgstr "Digitalni stereo (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4491 #: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Surround 5.1 (HDMI)" msgid "Digital Surround 5.1 (HDMI)"
msgstr "Digitalni prostorski zvok 5.1 (HDMI)" msgstr "Digitalni prostorski zvok 5.1 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4492 #: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Chat" msgid "Chat"
msgstr "Klepet" msgstr "Klepet"
#: spa/plugins/alsa/acp/alsa-mixer.c:4493 #: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Game" msgid "Game"
msgstr "Igra" msgstr "Igra"
#: spa/plugins/alsa/acp/alsa-mixer.c:4627 #: spa/plugins/alsa/acp/alsa-mixer.c:4691
msgid "Analog Mono Duplex" msgid "Analog Mono Duplex"
msgstr "Analogni mono dupleks" msgstr "Analogni mono dupleks"
#: spa/plugins/alsa/acp/alsa-mixer.c:4628 #: spa/plugins/alsa/acp/alsa-mixer.c:4692
msgid "Analog Stereo Duplex" msgid "Analog Stereo Duplex"
msgstr "Analogni stereo dupleks" msgstr "Analogni stereo dupleks"
#: spa/plugins/alsa/acp/alsa-mixer.c:4631 #: spa/plugins/alsa/acp/alsa-mixer.c:4695
msgid "Digital Stereo Duplex (IEC958)" msgid "Digital Stereo Duplex (IEC958)"
msgstr "Digitalni stereo dupleks (IEC958)" msgstr "Digitalni stereo dupleks (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4632 #: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Multichannel Duplex" msgid "Multichannel Duplex"
msgstr "Večkanalni dupleks" msgstr "Večkanalni dupleks"
#: spa/plugins/alsa/acp/alsa-mixer.c:4633 #: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Stereo Duplex" msgid "Stereo Duplex"
msgstr "Stereo dupleks" msgstr "Stereo dupleks"
#: spa/plugins/alsa/acp/alsa-mixer.c:4634 #: spa/plugins/alsa/acp/alsa-mixer.c:4698
msgid "Mono Chat + 7.1 Surround" msgid "Mono Chat + 7.1 Surround"
msgstr "Mono klepet + prostorski zvok 7.1" msgstr "Mono klepet + prostorski zvok 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4735 #: spa/plugins/alsa/acp/alsa-mixer.c:4799
#, c-format #, c-format
msgid "%s Output" msgid "%s Output"
msgstr "Izhod %s" msgstr "Izhod %s"
#: spa/plugins/alsa/acp/alsa-mixer.c:4743 #: spa/plugins/alsa/acp/alsa-mixer.c:4807
#, c-format #, c-format
msgid "%s Input" msgid "%s Input"
msgstr "Vhod %s" msgstr "Vhod %s"
@ -592,13 +627,13 @@ msgstr[3] ""
#: spa/plugins/alsa/acp/alsa-util.c:1299 #: spa/plugins/alsa/acp/alsa-util.c:1299
#, c-format #, c-format
msgid "" msgid ""
"snd_pcm_delay() returned a value that is exceptionally large: %li byte " "snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
"(%s%lu ms).\n" "%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers." "to the ALSA developers."
msgid_plural "" msgid_plural ""
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes " "snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
"(%s%lu ms).\n" "%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers." "to the ALSA developers."
msgstr[0] "" msgstr[0] ""
@ -668,7 +703,7 @@ msgstr[3] ""
"Najverjetneje je to napaka v gonilniku ALSA »%s«. O tej težavi obvestite " "Najverjetneje je to napaka v gonilniku ALSA »%s«. O tej težavi obvestite "
"razvijalce ALSA." "razvijalce ALSA."
#: spa/plugins/alsa/acp/channelmap.h:457 #: spa/plugins/alsa/acp/channelmap.h:460
msgid "(invalid)" msgid "(invalid)"
msgstr "(neveljavno)" msgstr "(neveljavno)"
@ -680,100 +715,100 @@ msgstr "Vgrajen zvok"
msgid "Modem" msgid "Modem"
msgstr "Modem" msgstr "Modem"
#: spa/plugins/bluez5/bluez5-device.c:1985 #: spa/plugins/bluez5/bluez5-device.c:2032
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "Zvožni prehod (vir A2DP in HSP/HFP AG)" msgstr "Zvožni prehod (vir A2DP in HSP/HFP AG)"
#: spa/plugins/bluez5/bluez5-device.c:2014 #: spa/plugins/bluez5/bluez5-device.c:2061
msgid "Audio Streaming for Hearing Aids (ASHA Sink)" msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "Pretakanje zvoka za slušne aparate (ponor ASHA)" msgstr "Pretakanje zvoka za slušne aparate (ponor ASHA)"
#: spa/plugins/bluez5/bluez5-device.c:2057 #: spa/plugins/bluez5/bluez5-device.c:2104
#, c-format #, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr "Predvajanje visoke ločljivosti (ponor A2DP, kodek %s)" msgstr "Predvajanje visoke ločljivosti (ponor A2DP, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:2060 #: spa/plugins/bluez5/bluez5-device.c:2107
#, c-format #, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP, kodek %s)" msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:2068 #: spa/plugins/bluez5/bluez5-device.c:2115
msgid "High Fidelity Playback (A2DP Sink)" msgid "High Fidelity Playback (A2DP Sink)"
msgstr "Predvajanje visoke ločljivosti (ponor A2DP)" msgstr "Predvajanje visoke ločljivosti (ponor A2DP)"
#: spa/plugins/bluez5/bluez5-device.c:2070 #: spa/plugins/bluez5/bluez5-device.c:2117
msgid "High Fidelity Duplex (A2DP Source/Sink)" msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP)" msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP)"
#: spa/plugins/bluez5/bluez5-device.c:2144 #: spa/plugins/bluez5/bluez5-device.c:2194
#, c-format #, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)" msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "Predvajanje visoke ločljivosti (ponor BAP, kodek %s)" msgstr "Predvajanje visoke ločljivosti (ponor BAP, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:2149 #: spa/plugins/bluez5/bluez5-device.c:2199
#, c-format #, c-format
msgid "High Fidelity Input (BAP Source, codec %s)" msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "Vhod visoke ločljivosti (vir BAP, kodek %s)" msgstr "Vhod visoke ločljivosti (vir BAP, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:2153 #: spa/plugins/bluez5/bluez5-device.c:2203
#, c-format #, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "Dupleks visoke ločljivosti (vir/ponor BAP, kodek %s)" msgstr "Dupleks visoke ločljivosti (vir/ponor BAP, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:2162 #: spa/plugins/bluez5/bluez5-device.c:2212
msgid "High Fidelity Playback (BAP Sink)" msgid "High Fidelity Playback (BAP Sink)"
msgstr "Predvajanje visoke ločljivosti (ponor BAP)" msgstr "Predvajanje visoke ločljivosti (ponor BAP)"
#: spa/plugins/bluez5/bluez5-device.c:2166 #: spa/plugins/bluez5/bluez5-device.c:2216
msgid "High Fidelity Input (BAP Source)" msgid "High Fidelity Input (BAP Source)"
msgstr "Vhod visoke ločljivosti (vir BAP)" msgstr "Vhod visoke ločljivosti (vir BAP)"
#: spa/plugins/bluez5/bluez5-device.c:2169 #: spa/plugins/bluez5/bluez5-device.c:2219
msgid "High Fidelity Duplex (BAP Source/Sink)" msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "Dupleks visoke ločljivosti (vir/ponor BAP)" msgstr "Dupleks visoke ločljivosti (vir/ponor BAP)"
#: spa/plugins/bluez5/bluez5-device.c:2209 #: spa/plugins/bluez5/bluez5-device.c:2259
#, c-format #, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)" msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr "Naglavna enota slušalk (HSP/HFP, kodek %s)" msgstr "Naglavna enota slušalk (HSP/HFP, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:2361 #: spa/plugins/bluez5/bluez5-device.c:2411
#: spa/plugins/bluez5/bluez5-device.c:2366 #: spa/plugins/bluez5/bluez5-device.c:2416
#: spa/plugins/bluez5/bluez5-device.c:2373 #: spa/plugins/bluez5/bluez5-device.c:2423
#: spa/plugins/bluez5/bluez5-device.c:2379 #: spa/plugins/bluez5/bluez5-device.c:2429
#: spa/plugins/bluez5/bluez5-device.c:2385 #: spa/plugins/bluez5/bluez5-device.c:2435
#: spa/plugins/bluez5/bluez5-device.c:2391 #: spa/plugins/bluez5/bluez5-device.c:2441
#: spa/plugins/bluez5/bluez5-device.c:2397 #: spa/plugins/bluez5/bluez5-device.c:2447
#: spa/plugins/bluez5/bluez5-device.c:2403 #: spa/plugins/bluez5/bluez5-device.c:2453
#: spa/plugins/bluez5/bluez5-device.c:2409 #: spa/plugins/bluez5/bluez5-device.c:2459
msgid "Handsfree" msgid "Handsfree"
msgstr "Prostoročno telefoniranje" msgstr "Prostoročno telefoniranje"
#: spa/plugins/bluez5/bluez5-device.c:2367 #: spa/plugins/bluez5/bluez5-device.c:2417
msgid "Handsfree (HFP)" msgid "Handsfree (HFP)"
msgstr "Prostoročno telefoniranje (HFP)" msgstr "Prostoročno telefoniranje (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:2390 #: spa/plugins/bluez5/bluez5-device.c:2440
msgid "Portable" msgid "Portable"
msgstr "Prenosna naprava" msgstr "Prenosna naprava"
#: spa/plugins/bluez5/bluez5-device.c:2396 #: spa/plugins/bluez5/bluez5-device.c:2446
msgid "Car" msgid "Car"
msgstr "Avtomobil" msgstr "Avtomobil"
#: spa/plugins/bluez5/bluez5-device.c:2402 #: spa/plugins/bluez5/bluez5-device.c:2452
msgid "HiFi" msgid "HiFi"
msgstr "HiFi" msgstr "HiFi"
#: spa/plugins/bluez5/bluez5-device.c:2408 #: spa/plugins/bluez5/bluez5-device.c:2458
msgid "Phone" msgid "Phone"
msgstr "Telefon" msgstr "Telefon"
#: spa/plugins/bluez5/bluez5-device.c:2415 #: spa/plugins/bluez5/bluez5-device.c:2465
msgid "Bluetooth" msgid "Bluetooth"
msgstr "Bluetooth" msgstr "Bluetooth"
#: spa/plugins/bluez5/bluez5-device.c:2416 #: spa/plugins/bluez5/bluez5-device.c:2466
msgid "Bluetooth (HFP)" msgid "Bluetooth Handsfree"
msgstr "Bluetooth (HFP)" msgstr "Bluetooth - prostoročno"

693
po/sr.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

382
po/sv.po
View file

@ -1,9 +1,9 @@
# Swedish translation for pipewire. # Swedish translation for pipewire.
# Copyright © 2008-2025 Free Software Foundation, Inc. # Copyright © 2008-2026 Free Software Foundation, Inc.
# This file is distributed under the same license as the pipewire package. # This file is distributed under the same license as the pipewire package.
# Daniel Nylander <po@danielnylander.se>, 2008, 2012. # Daniel Nylander <po@danielnylander.se>, 2008, 2012.
# Josef Andersson <josef.andersson@fripost.org>, 2014, 2017. # Josef Andersson <josef.andersson@fripost.org>, 2014, 2017.
# Anders Jonsson <anders.jonsson@norsjovallen.se>, 2021, 2022, 2023, 2024, 2025. # Anders Jonsson <anders.jonsson@norsjovallen.se>, 2021, 2022, 2023, 2024, 2025, 2026.
# #
# Termer: # Termer:
# input/output: ingång/utgång (det handlar om ljud) # input/output: ingång/utgång (det handlar om ljud)
@ -19,8 +19,8 @@ msgstr ""
"Project-Id-Version: pipewire\n" "Project-Id-Version: pipewire\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
"issues\n" "issues\n"
"POT-Creation-Date: 2025-04-16 15:31+0000\n" "POT-Creation-Date: 2026-02-09 12:55+0000\n"
"PO-Revision-Date: 2025-04-22 10:43+0200\n" "PO-Revision-Date: 2026-02-22 21:48+0100\n"
"Last-Translator: Anders Jonsson <anders.jonsson@norsjovallen.se>\n" "Last-Translator: Anders Jonsson <anders.jonsson@norsjovallen.se>\n"
"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n" "Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
"Language: sv\n" "Language: sv\n"
@ -28,7 +28,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Poedit 3.5\n" "X-Generator: Poedit 3.8\n"
#: src/daemon/pipewire.c:29 #: src/daemon/pipewire.c:29
#, c-format #, c-format
@ -47,11 +47,11 @@ msgstr ""
" -c, --config Läs in konfig (standard %s)\n" " -c, --config Läs in konfig (standard %s)\n"
" -P --properties Ställ in kontextegenskaper\n" " -P --properties Ställ in kontextegenskaper\n"
#: src/daemon/pipewire.desktop.in:4 #: src/daemon/pipewire.desktop.in:3
msgid "PipeWire Media System" msgid "PipeWire Media System"
msgstr "PipeWire mediasystem" msgstr "PipeWire mediasystem"
#: src/daemon/pipewire.desktop.in:5 #: src/daemon/pipewire.desktop.in:4
msgid "Start the PipeWire Media System" msgid "Start the PipeWire Media System"
msgstr "Starta mediasystemet PipeWire" msgstr "Starta mediasystemet PipeWire"
@ -65,26 +65,51 @@ msgstr "Tunnel till %s%s%s"
msgid "Dummy Output" msgid "Dummy Output"
msgstr "Attrapputgång" msgstr "Attrapputgång"
#: src/modules/module-pulse-tunnel.c:760 #: src/modules/module-pulse-tunnel.c:761
#, c-format #, c-format
msgid "Tunnel for %s@%s" msgid "Tunnel for %s@%s"
msgstr "Tunnel för %s@%s" msgstr "Tunnel för %s@%s"
#: src/modules/module-zeroconf-discover.c:320 #: src/modules/module-zeroconf-discover.c:326
msgid "Unknown device" msgid "Unknown device"
msgstr "Okänd enhet" msgstr "Okänd enhet"
#: src/modules/module-zeroconf-discover.c:332 #: src/modules/module-zeroconf-discover.c:338
#, c-format #, c-format
msgid "%s on %s@%s" msgid "%s on %s@%s"
msgstr "%s på %s@%s" msgstr "%s på %s@%s"
#: src/modules/module-zeroconf-discover.c:336 #: src/modules/module-zeroconf-discover.c:342
#, c-format #, c-format
msgid "%s on %s" msgid "%s on %s"
msgstr "%s på %s" msgstr "%s på %s"
#: src/tools/pw-cat.c:973 #: src/tools/pw-cat.c:264
#, c-format
msgid "Supported formats:\n"
msgstr "Format som stöds:\n"
#: src/tools/pw-cat.c:749
#, c-format
msgid "Supported channel layouts:\n"
msgstr "Kanallayouter som stöds:\n"
#: src/tools/pw-cat.c:759
#, c-format
msgid "Supported channel layout aliases:\n"
msgstr "Kanallayoutalias som stöds:\n"
#: src/tools/pw-cat.c:761
#, c-format
msgid " %s -> %s\n"
msgstr " %s -> %s\n"
#: src/tools/pw-cat.c:766
#, c-format
msgid "Supported channel names:\n"
msgstr "Kanalnamn som stöds:\n"
#: src/tools/pw-cat.c:1177
#, c-format #, c-format
msgid "" msgid ""
"%s [options] [<file>|-]\n" "%s [options] [<file>|-]\n"
@ -99,7 +124,7 @@ msgstr ""
" -v, --verbose Aktivera utförliga operationer\n" " -v, --verbose Aktivera utförliga operationer\n"
"\n" "\n"
#: src/tools/pw-cat.c:980 #: src/tools/pw-cat.c:1184
#, c-format #, c-format
msgid "" msgid ""
" -R, --remote Remote daemon name\n" " -R, --remote Remote daemon name\n"
@ -131,50 +156,64 @@ msgstr ""
" -P --properties Sätt nodegenskaper\n" " -P --properties Sätt nodegenskaper\n"
"\n" "\n"
#: src/tools/pw-cat.c:998 #: src/tools/pw-cat.c:1202
#, c-format #, c-format
msgid "" msgid ""
" --rate Sample rate (req. for rec) (default " " --rate Sample rate (default %u)\n"
"%u)\n" " --channels Number of channels (default %u)\n"
" --channels Number of channels (req. for rec) "
"(default %u)\n"
" --channel-map Channel map\n" " --channel-map Channel map\n"
" one of: \"stereo\", " " a channel layout: \"Stereo\", "
"\"surround-51\",... or\n" "\"5.1\",... or\n"
" comma separated list of channel " " comma separated list of channel "
"names: eg. \"FL,FR\"\n" "names: eg. \"FL,FR\"\n"
" --format Sample format %s (req. for rec) " " --list-layouts List supported channel layouts\n"
"(default %s)\n" " --list-channel-names List supported channel maps\n"
" --format Sample format (default %s)\n"
" --list-formats List supported sample formats\n"
" --container Container format\n"
" --list-containers List supported containers and "
"extensions\n"
" --volume Stream volume 0-1.0 (default %.3f)\n" " --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default " " -q --quality Resampler quality (0 - 15) (default "
"%d)\n" "%d)\n"
" -a, --raw RAW mode\n" " -a, --raw RAW mode\n"
" -M, --force-midi Force midi format, one of \"midi\" "
"or \"ump\", (default ump)\n"
" -n, --sample-count COUNT Stop after COUNT samples\n"
"\n" "\n"
msgstr "" msgstr ""
" --rate Samplingsfrekvens (krävs för insp.) " " --rate Samplingsfrekvens (standard %u)\n"
"(standard %u)\n" " --channels Antal kanaler (standard %u)\n"
" --channels Antal kanaler (krävs för insp.) "
"(standard %u)\n"
" --channel-map Kanalmappning\n" " --channel-map Kanalmappning\n"
" en av: \"stereo\", " " en kanallayout: \"Stereo\", "
"\"surround-51\",... eller\n" "\"5.1\",... eller\n"
" kommaseparerad lista av " " kommaseparerad lista av "
"kanalnamn: t.ex. \"FL,FR\"\n" "kanalnamn: t.ex. \"FL,FR\"\n"
" --format Samplingsformat %s (krävs för insp.) " " --list-layouts Lista kanallayouter som stöds\n"
"(standard %s)\n" " --list-channel-names Lista kanalmappningar som stöds\n"
" --format Samplingsformat (standard %s)\n"
" --list-formats Lista samplingsformat som stöds\n"
" --container Behållarformat\n"
" --list-containers Lista behållare och tillägg som "
"stöds\n"
" --volume Strömvolym 0-1.0 (standard %.3f)\n" " --volume Strömvolym 0-1.0 (standard %.3f)\n"
" -q --quality Omsamplarkvalitet (0 - 15) (standard " " -q --quality Omsamplarkvalitet (0 - 15) (standard "
"%d)\n" "%d)\n"
" -a, --raw RAW-läge\n" " -a, --raw RAW-läge\n"
" -M, --force-midi Tvinga midi-format, en av \"midi\" "
"eller \"ump\", (standard ump)\n"
" -n, --sample-count ANTAL Stoppa efter ANTAL samplar\n"
"\n" "\n"
#: src/tools/pw-cat.c:1016 #: src/tools/pw-cat.c:1227
msgid "" msgid ""
" -p, --playback Playback mode\n" " -p, --playback Playback mode\n"
" -r, --record Recording mode\n" " -r, --record Recording mode\n"
" -m, --midi Midi mode\n" " -m, --midi Midi mode\n"
" -d, --dsd DSD mode\n" " -d, --dsd DSD mode\n"
" -o, --encoded Encoded mode\n" " -o, --encoded Encoded mode\n"
" -s, --sysex SysEx mode\n"
" -c, --midi-clip MIDI clip mode\n"
"\n" "\n"
msgstr "" msgstr ""
" -p, --playback Uppspelningsläge\n" " -p, --playback Uppspelningsläge\n"
@ -182,9 +221,16 @@ msgstr ""
" -m, --midi Midiläge\n" " -m, --midi Midiläge\n"
" -d, --dsd DSD-läge\n" " -d, --dsd DSD-läge\n"
" -o, --encoded Kodat läge\n" " -o, --encoded Kodat läge\n"
" -s, --sysex SysEx-läge\n"
" -c, --midi-clip MIDI-klippläge\n"
"\n" "\n"
#: src/tools/pw-cli.c:2306 #: src/tools/pw-cat.c:1827
#, c-format
msgid "Supported containers and extensions:\n"
msgstr "Behållare och tillägg som stöds:\n"
#: src/tools/pw-cli.c:2386
#, c-format #, c-format
msgid "" msgid ""
"%s [options] [command]\n" "%s [options] [command]\n"
@ -203,200 +249,203 @@ msgstr ""
" -m, --monitor Övervaka aktivitet\n" " -m, --monitor Övervaka aktivitet\n"
"\n" "\n"
#: spa/plugins/alsa/acp/acp.c:350 #: spa/plugins/alsa/acp/acp.c:361
msgid "Pro Audio" msgid "Pro Audio"
msgstr "Professionellt ljud" msgstr "Professionellt ljud"
#: spa/plugins/alsa/acp/acp.c:511 spa/plugins/alsa/acp/alsa-mixer.c:4635 #: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:1802 #: spa/plugins/bluez5/bluez5-device.c:2021
msgid "Off" msgid "Off"
msgstr "Av" msgstr "Av"
#: spa/plugins/alsa/acp/acp.c:593 #: spa/plugins/alsa/acp/acp.c:620
#, c-format #, c-format
msgid "%s [ALSA UCM error]" msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM-fel]" msgstr "%s [ALSA UCM-fel]"
#: spa/plugins/alsa/acp/alsa-mixer.c:2652 #: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Input" msgid "Input"
msgstr "Ingång" msgstr "Ingång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2653 #: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "Docking Station Input" msgid "Docking Station Input"
msgstr "Ingång för dockningsstation" msgstr "Ingång för dockningsstation"
#: spa/plugins/alsa/acp/alsa-mixer.c:2654 #: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Docking Station Microphone" msgid "Docking Station Microphone"
msgstr "Mikrofon för dockningsstation" msgstr "Mikrofon för dockningsstation"
#: spa/plugins/alsa/acp/alsa-mixer.c:2655 #: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "Docking Station Line In" msgid "Docking Station Line In"
msgstr "Linje in för dockningsstation" msgstr "Linje in för dockningsstation"
#: spa/plugins/alsa/acp/alsa-mixer.c:2656 #: spa/plugins/alsa/acp/alsa-mixer.c:2725
#: spa/plugins/alsa/acp/alsa-mixer.c:2747 #: spa/plugins/alsa/acp/alsa-mixer.c:2816
msgid "Line In" msgid "Line In"
msgstr "Linje in" msgstr "Linje in"
#: spa/plugins/alsa/acp/alsa-mixer.c:2657 #: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2741 #: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:2146 #: spa/plugins/bluez5/bluez5-device.c:2422
msgid "Microphone" msgid "Microphone"
msgstr "Mikrofon" msgstr "Mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2658 #: spa/plugins/alsa/acp/alsa-mixer.c:2727
#: spa/plugins/alsa/acp/alsa-mixer.c:2742 #: spa/plugins/alsa/acp/alsa-mixer.c:2811
msgid "Front Microphone" msgid "Front Microphone"
msgstr "Frontmikrofon" msgstr "Frontmikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2659 #: spa/plugins/alsa/acp/alsa-mixer.c:2728
#: spa/plugins/alsa/acp/alsa-mixer.c:2743 #: spa/plugins/alsa/acp/alsa-mixer.c:2812
msgid "Rear Microphone" msgid "Rear Microphone"
msgstr "Bakre mikrofon" msgstr "Bakre mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2660 #: spa/plugins/alsa/acp/alsa-mixer.c:2729
msgid "External Microphone" msgid "External Microphone"
msgstr "Extern mikrofon" msgstr "Extern mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2661 #: spa/plugins/alsa/acp/alsa-mixer.c:2730
#: spa/plugins/alsa/acp/alsa-mixer.c:2745 #: spa/plugins/alsa/acp/alsa-mixer.c:2814
msgid "Internal Microphone" msgid "Internal Microphone"
msgstr "Intern mikrofon" msgstr "Intern mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2662 #: spa/plugins/alsa/acp/alsa-mixer.c:2731
#: spa/plugins/alsa/acp/alsa-mixer.c:2748 #: spa/plugins/alsa/acp/alsa-mixer.c:2817
msgid "Radio" msgid "Radio"
msgstr "Radio" msgstr "Radio"
#: spa/plugins/alsa/acp/alsa-mixer.c:2663 #: spa/plugins/alsa/acp/alsa-mixer.c:2732
#: spa/plugins/alsa/acp/alsa-mixer.c:2749 #: spa/plugins/alsa/acp/alsa-mixer.c:2818
msgid "Video" msgid "Video"
msgstr "Video" msgstr "Video"
#: spa/plugins/alsa/acp/alsa-mixer.c:2664 #: spa/plugins/alsa/acp/alsa-mixer.c:2733
msgid "Automatic Gain Control" msgid "Automatic Gain Control"
msgstr "Automatisk förstärkningskontroll" msgstr "Automatisk förstärkningskontroll"
#: spa/plugins/alsa/acp/alsa-mixer.c:2665 #: spa/plugins/alsa/acp/alsa-mixer.c:2734
msgid "No Automatic Gain Control" msgid "No Automatic Gain Control"
msgstr "Ingen automatisk förstärkningskontroll" msgstr "Ingen automatisk förstärkningskontroll"
#: spa/plugins/alsa/acp/alsa-mixer.c:2666 #: spa/plugins/alsa/acp/alsa-mixer.c:2735
msgid "Boost" msgid "Boost"
msgstr "Ökning" msgstr "Ökning"
#: spa/plugins/alsa/acp/alsa-mixer.c:2667 #: spa/plugins/alsa/acp/alsa-mixer.c:2736
msgid "No Boost" msgid "No Boost"
msgstr "Ingen ökning" msgstr "Ingen ökning"
#: spa/plugins/alsa/acp/alsa-mixer.c:2668 #: spa/plugins/alsa/acp/alsa-mixer.c:2737
msgid "Amplifier" msgid "Amplifier"
msgstr "Förstärkare" msgstr "Förstärkare"
#: spa/plugins/alsa/acp/alsa-mixer.c:2669 #: spa/plugins/alsa/acp/alsa-mixer.c:2738
msgid "No Amplifier" msgid "No Amplifier"
msgstr "Ingen förstärkare" msgstr "Ingen förstärkare"
#: spa/plugins/alsa/acp/alsa-mixer.c:2670 #: spa/plugins/alsa/acp/alsa-mixer.c:2739
msgid "Bass Boost" msgid "Bass Boost"
msgstr "Basökning" msgstr "Basökning"
#: spa/plugins/alsa/acp/alsa-mixer.c:2671 #: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "No Bass Boost" msgid "No Bass Boost"
msgstr "Ingen basökning" msgstr "Ingen basökning"
#: spa/plugins/alsa/acp/alsa-mixer.c:2672 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2152 #: spa/plugins/bluez5/bluez5-device.c:2428
msgid "Speaker" msgid "Speaker"
msgstr "Högtalare" msgstr "Högtalare"
#: spa/plugins/alsa/acp/alsa-mixer.c:2673 #. Don't call it "headset", the HF one has the mic
#: spa/plugins/alsa/acp/alsa-mixer.c:2751 #: spa/plugins/alsa/acp/alsa-mixer.c:2742
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/bluez5/bluez5-device.c:2434
#: spa/plugins/bluez5/bluez5-device.c:2501
msgid "Headphones" msgid "Headphones"
msgstr "Hörlurar" msgstr "Hörlurar"
#: spa/plugins/alsa/acp/alsa-mixer.c:2740 #: spa/plugins/alsa/acp/alsa-mixer.c:2809
msgid "Analog Input" msgid "Analog Input"
msgstr "Analog ingång" msgstr "Analog ingång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2744 #: spa/plugins/alsa/acp/alsa-mixer.c:2813
msgid "Dock Microphone" msgid "Dock Microphone"
msgstr "Dockmikrofon" msgstr "Dockmikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2746 #: spa/plugins/alsa/acp/alsa-mixer.c:2815
msgid "Headset Microphone" msgid "Headset Microphone"
msgstr "Headset-mikrofon" msgstr "Headset-mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2750 #: spa/plugins/alsa/acp/alsa-mixer.c:2819
msgid "Analog Output" msgid "Analog Output"
msgstr "Analog utgång" msgstr "Analog utgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2752 #: spa/plugins/alsa/acp/alsa-mixer.c:2821
msgid "Headphones 2" msgid "Headphones 2"
msgstr "Hörlurar 2" msgstr "Hörlurar 2"
#: spa/plugins/alsa/acp/alsa-mixer.c:2753 #: spa/plugins/alsa/acp/alsa-mixer.c:2822
msgid "Headphones Mono Output" msgid "Headphones Mono Output"
msgstr "Monoutgång för hörlurar" msgstr "Monoutgång för hörlurar"
#: spa/plugins/alsa/acp/alsa-mixer.c:2754 #: spa/plugins/alsa/acp/alsa-mixer.c:2823
msgid "Line Out" msgid "Line Out"
msgstr "Linje ut" msgstr "Linje ut"
#: spa/plugins/alsa/acp/alsa-mixer.c:2755 #: spa/plugins/alsa/acp/alsa-mixer.c:2824
msgid "Analog Mono Output" msgid "Analog Mono Output"
msgstr "Analog monoutgång" msgstr "Analog monoutgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2756 #: spa/plugins/alsa/acp/alsa-mixer.c:2825
msgid "Speakers" msgid "Speakers"
msgstr "Högtalare" msgstr "Högtalare"
#: spa/plugins/alsa/acp/alsa-mixer.c:2757 #: spa/plugins/alsa/acp/alsa-mixer.c:2826
msgid "HDMI / DisplayPort" msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort" msgstr "HDMI / DisplayPort"
#: spa/plugins/alsa/acp/alsa-mixer.c:2758 #: spa/plugins/alsa/acp/alsa-mixer.c:2827
msgid "Digital Output (S/PDIF)" msgid "Digital Output (S/PDIF)"
msgstr "Digital utgång (S/PDIF)" msgstr "Digital utgång (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2759 #: spa/plugins/alsa/acp/alsa-mixer.c:2828
msgid "Digital Input (S/PDIF)" msgid "Digital Input (S/PDIF)"
msgstr "Digital ingång (S/PDIF)" msgstr "Digital ingång (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2760 #: spa/plugins/alsa/acp/alsa-mixer.c:2829
msgid "Multichannel Input" msgid "Multichannel Input"
msgstr "Multikanalingång" msgstr "Multikanalingång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2761 #: spa/plugins/alsa/acp/alsa-mixer.c:2830
msgid "Multichannel Output" msgid "Multichannel Output"
msgstr "Multikanalutgång" msgstr "Multikanalutgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2762 #: spa/plugins/alsa/acp/alsa-mixer.c:2831
msgid "Game Output" msgid "Game Output"
msgstr "Spelutgång" msgstr "Spelutgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2763 #: spa/plugins/alsa/acp/alsa-mixer.c:2832
#: spa/plugins/alsa/acp/alsa-mixer.c:2764 #: spa/plugins/alsa/acp/alsa-mixer.c:2833
msgid "Chat Output" msgid "Chat Output"
msgstr "Chatt-utgång" msgstr "Chatt-utgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2765 #: spa/plugins/alsa/acp/alsa-mixer.c:2834
msgid "Chat Input" msgid "Chat Input"
msgstr "Chatt-ingång" msgstr "Chatt-ingång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2766 #: spa/plugins/alsa/acp/alsa-mixer.c:2835
msgid "Virtual Surround 7.1" msgid "Virtual Surround 7.1"
msgstr "Virtual surround 7.1" msgstr "Virtual surround 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4458 #: spa/plugins/alsa/acp/alsa-mixer.c:4522
msgid "Analog Mono" msgid "Analog Mono"
msgstr "Analog mono" msgstr "Analog mono"
#: spa/plugins/alsa/acp/alsa-mixer.c:4459 #: spa/plugins/alsa/acp/alsa-mixer.c:4523
msgid "Analog Mono (Left)" msgid "Analog Mono (Left)"
msgstr "Analog mono (vänster)" msgstr "Analog mono (vänster)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4460 #: spa/plugins/alsa/acp/alsa-mixer.c:4524
msgid "Analog Mono (Right)" msgid "Analog Mono (Right)"
msgstr "Analog mono (höger)" msgstr "Analog mono (höger)"
@ -405,142 +454,142 @@ msgstr "Analog mono (höger)"
#. * here would lead to the source name to become "Analog Stereo Input #. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output, #. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output. #. * multichannel-input and multichannel-output.
#: spa/plugins/alsa/acp/alsa-mixer.c:4461 #: spa/plugins/alsa/acp/alsa-mixer.c:4525
#: spa/plugins/alsa/acp/alsa-mixer.c:4469 #: spa/plugins/alsa/acp/alsa-mixer.c:4533
#: spa/plugins/alsa/acp/alsa-mixer.c:4470 #: spa/plugins/alsa/acp/alsa-mixer.c:4534
msgid "Analog Stereo" msgid "Analog Stereo"
msgstr "Analog stereo" msgstr "Analog stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4462 #: spa/plugins/alsa/acp/alsa-mixer.c:4526
msgid "Mono" msgid "Mono"
msgstr "Mono" msgstr "Mono"
#: spa/plugins/alsa/acp/alsa-mixer.c:4463 #: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Stereo" msgid "Stereo"
msgstr "Stereo" msgstr "Stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4471 #: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4629 #: spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:2134 #: spa/plugins/bluez5/bluez5-device.c:2410
msgid "Headset" msgid "Headset"
msgstr "Headset" msgstr "Headset"
#: spa/plugins/alsa/acp/alsa-mixer.c:4472 #: spa/plugins/alsa/acp/alsa-mixer.c:4536
#: spa/plugins/alsa/acp/alsa-mixer.c:4630 #: spa/plugins/alsa/acp/alsa-mixer.c:4694
msgid "Speakerphone" msgid "Speakerphone"
msgstr "Högtalartelefon" msgstr "Högtalartelefon"
#: spa/plugins/alsa/acp/alsa-mixer.c:4473 #: spa/plugins/alsa/acp/alsa-mixer.c:4537
#: spa/plugins/alsa/acp/alsa-mixer.c:4474 #: spa/plugins/alsa/acp/alsa-mixer.c:4538
msgid "Multichannel" msgid "Multichannel"
msgstr "Multikanal" msgstr "Multikanal"
#: spa/plugins/alsa/acp/alsa-mixer.c:4475 #: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Surround 2.1" msgid "Analog Surround 2.1"
msgstr "Analog surround 2.1" msgstr "Analog surround 2.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4476 #: spa/plugins/alsa/acp/alsa-mixer.c:4540
msgid "Analog Surround 3.0" msgid "Analog Surround 3.0"
msgstr "Analog surround 3.0" msgstr "Analog surround 3.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4477 #: spa/plugins/alsa/acp/alsa-mixer.c:4541
msgid "Analog Surround 3.1" msgid "Analog Surround 3.1"
msgstr "Analog surround 3.1" msgstr "Analog surround 3.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4478 #: spa/plugins/alsa/acp/alsa-mixer.c:4542
msgid "Analog Surround 4.0" msgid "Analog Surround 4.0"
msgstr "Analog surround 4.0" msgstr "Analog surround 4.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4479 #: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Analog Surround 4.1" msgid "Analog Surround 4.1"
msgstr "Analog surround 4.1" msgstr "Analog surround 4.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4480 #: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 5.0" msgid "Analog Surround 5.0"
msgstr "Analog surround 5.0" msgstr "Analog surround 5.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4481 #: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 5.1" msgid "Analog Surround 5.1"
msgstr "Analog surround 5.1" msgstr "Analog surround 5.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4482 #: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 6.0" msgid "Analog Surround 6.0"
msgstr "Analog surround 6.0" msgstr "Analog surround 6.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4483 #: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 6.1" msgid "Analog Surround 6.1"
msgstr "Analog surround 6.1" msgstr "Analog surround 6.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4484 #: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 7.0" msgid "Analog Surround 7.0"
msgstr "Analog surround 7.0" msgstr "Analog surround 7.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4485 #: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 7.1" msgid "Analog Surround 7.1"
msgstr "Analog surround 7.1" msgstr "Analog surround 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4486 #: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Digital Stereo (IEC958)" msgid "Digital Stereo (IEC958)"
msgstr "Digital stereo (IEC958)" msgstr "Digital stereo (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4487 #: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Digital Surround 4.0 (IEC958/AC3)" msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Digital surround 4.0 (IEC958/AC3)" msgstr "Digital surround 4.0 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4488 #: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Digital Surround 5.1 (IEC958/AC3)" msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Digital surround 5.1 (IEC958/AC3)" msgstr "Digital surround 5.1 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4489 #: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Digital Surround 5.1 (IEC958/DTS)" msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "Digital surround 5.1 (IEC958/DTS)" msgstr "Digital surround 5.1 (IEC958/DTS)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4490 #: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Digital Stereo (HDMI)" msgid "Digital Stereo (HDMI)"
msgstr "Digital stereo (HDMI)" msgstr "Digital stereo (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4491 #: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Surround 5.1 (HDMI)" msgid "Digital Surround 5.1 (HDMI)"
msgstr "Digital surround 5.1 (HDMI)" msgstr "Digital surround 5.1 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4492 #: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Chat" msgid "Chat"
msgstr "Chatt" msgstr "Chatt"
#: spa/plugins/alsa/acp/alsa-mixer.c:4493 #: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Game" msgid "Game"
msgstr "Spel" msgstr "Spel"
#: spa/plugins/alsa/acp/alsa-mixer.c:4627 #: spa/plugins/alsa/acp/alsa-mixer.c:4691
msgid "Analog Mono Duplex" msgid "Analog Mono Duplex"
msgstr "Analog mono duplex" msgstr "Analog mono duplex"
#: spa/plugins/alsa/acp/alsa-mixer.c:4628 #: spa/plugins/alsa/acp/alsa-mixer.c:4692
msgid "Analog Stereo Duplex" msgid "Analog Stereo Duplex"
msgstr "Analog stereo duplex" msgstr "Analog stereo duplex"
#: spa/plugins/alsa/acp/alsa-mixer.c:4631 #: spa/plugins/alsa/acp/alsa-mixer.c:4695
msgid "Digital Stereo Duplex (IEC958)" msgid "Digital Stereo Duplex (IEC958)"
msgstr "Digital stereo duplex (IEC958)" msgstr "Digital stereo duplex (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4632 #: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Multichannel Duplex" msgid "Multichannel Duplex"
msgstr "Multikanalduplex" msgstr "Multikanalduplex"
#: spa/plugins/alsa/acp/alsa-mixer.c:4633 #: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Stereo Duplex" msgid "Stereo Duplex"
msgstr "Stereo duplex" msgstr "Stereo duplex"
#: spa/plugins/alsa/acp/alsa-mixer.c:4634 #: spa/plugins/alsa/acp/alsa-mixer.c:4698
msgid "Mono Chat + 7.1 Surround" msgid "Mono Chat + 7.1 Surround"
msgstr "Mono Chatt + 7.1 Surround" msgstr "Mono Chatt + 7.1 Surround"
#: spa/plugins/alsa/acp/alsa-mixer.c:4735 #: spa/plugins/alsa/acp/alsa-mixer.c:4799
#, c-format #, c-format
msgid "%s Output" msgid "%s Output"
msgstr "%s-utgång" msgstr "%s-utgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:4743 #: spa/plugins/alsa/acp/alsa-mixer.c:4807
#, c-format #, c-format
msgid "%s Input" msgid "%s Input"
msgstr "%s-ingång" msgstr "%s-ingång"
@ -627,116 +676,115 @@ msgstr[1] ""
"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera " "Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
"problemet till ALSA-utvecklarna." "problemet till ALSA-utvecklarna."
#: spa/plugins/alsa/acp/channelmap.h:457 #: spa/plugins/alsa/acp/channelmap.h:460
msgid "(invalid)" msgid "(invalid)"
msgstr "(ogiltig)" msgstr "(ogiltig)"
#: spa/plugins/alsa/acp/compat.c:193 #: spa/plugins/alsa/acp/compat.c:194
msgid "Built-in Audio" msgid "Built-in Audio"
msgstr "Inbyggt ljud" msgstr "Inbyggt ljud"
#: spa/plugins/alsa/acp/compat.c:198 #: spa/plugins/alsa/acp/compat.c:199
msgid "Modem" msgid "Modem"
msgstr "Modem" msgstr "Modem"
#: spa/plugins/bluez5/bluez5-device.c:1813 #: spa/plugins/bluez5/bluez5-device.c:2032
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "Audio gateway (A2DP-källa & HSP/HFP AG)" msgstr "Audio gateway (A2DP-källa & HSP/HFP AG)"
#: spa/plugins/bluez5/bluez5-device.c:1841 #: spa/plugins/bluez5/bluez5-device.c:2061
msgid "Audio Streaming for Hearing Aids (ASHA Sink)" msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "Ljudströmning för hörhjälpmedel (ASHA-utgång)" msgstr "Ljudströmning för hörhjälpmedel (ASHA-utgång)"
#: spa/plugins/bluez5/bluez5-device.c:1881 #: spa/plugins/bluez5/bluez5-device.c:2104
#, c-format #, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr "High fidelity-uppspelning (A2DP-utgång, kodek %s)" msgstr "High fidelity-uppspelning (A2DP-utgång, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:1884 #: spa/plugins/bluez5/bluez5-device.c:2107
#, c-format #, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr "High fidelity duplex (A2DP-källa/utgång, kodek %s)" msgstr "High fidelity duplex (A2DP-källa/utgång, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:1892 #: spa/plugins/bluez5/bluez5-device.c:2115
msgid "High Fidelity Playback (A2DP Sink)" msgid "High Fidelity Playback (A2DP Sink)"
msgstr "High fidelity-uppspelning (A2DP-utgång)" msgstr "High fidelity-uppspelning (A2DP-utgång)"
#: spa/plugins/bluez5/bluez5-device.c:1894 #: spa/plugins/bluez5/bluez5-device.c:2117
msgid "High Fidelity Duplex (A2DP Source/Sink)" msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "High fidelity duplex (A2DP-källa/utgång)" msgstr "High fidelity duplex (A2DP-källa/utgång)"
#: spa/plugins/bluez5/bluez5-device.c:1944 #: spa/plugins/bluez5/bluez5-device.c:2194
#, c-format #, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)" msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "High fidelity-uppspelning (BAP-utgång, kodek %s)" msgstr "High fidelity-uppspelning (BAP-utgång, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:1949 #: spa/plugins/bluez5/bluez5-device.c:2199
#, c-format #, c-format
msgid "High Fidelity Input (BAP Source, codec %s)" msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "High fidelity-ingång (BAP-källa, kodek %s)" msgstr "High fidelity-ingång (BAP-källa, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:1953 #: spa/plugins/bluez5/bluez5-device.c:2203
#, c-format #, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "High fidelity duplex (BAP-källa/utgång, kodek %s)" msgstr "High fidelity duplex (BAP-källa/utgång, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:1962 #: spa/plugins/bluez5/bluez5-device.c:2212
msgid "High Fidelity Playback (BAP Sink)" msgid "High Fidelity Playback (BAP Sink)"
msgstr "High fidelity-uppspelning (BAP-utgång)" msgstr "High fidelity-uppspelning (BAP-utgång)"
#: spa/plugins/bluez5/bluez5-device.c:1966 #: spa/plugins/bluez5/bluez5-device.c:2216
msgid "High Fidelity Input (BAP Source)" msgid "High Fidelity Input (BAP Source)"
msgstr "High fidelity-ingång (BAP-källa)" msgstr "High fidelity-ingång (BAP-källa)"
#: spa/plugins/bluez5/bluez5-device.c:1969 #: spa/plugins/bluez5/bluez5-device.c:2219
msgid "High Fidelity Duplex (BAP Source/Sink)" msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "High fidelity duplex (BAP-källa/utgång)" msgstr "High fidelity duplex (BAP-källa/utgång)"
#: spa/plugins/bluez5/bluez5-device.c:2015 #: spa/plugins/bluez5/bluez5-device.c:2259
#, c-format #, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)" msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr "Headset-huvudenhet (HSP/HFP, kodek %s)" msgstr "Headset-huvudenhet (HSP/HFP, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:2135 #: spa/plugins/bluez5/bluez5-device.c:2411
#: spa/plugins/bluez5/bluez5-device.c:2140 #: spa/plugins/bluez5/bluez5-device.c:2416
#: spa/plugins/bluez5/bluez5-device.c:2147 #: spa/plugins/bluez5/bluez5-device.c:2423
#: spa/plugins/bluez5/bluez5-device.c:2153 #: spa/plugins/bluez5/bluez5-device.c:2429
#: spa/plugins/bluez5/bluez5-device.c:2159 #: spa/plugins/bluez5/bluez5-device.c:2435
#: spa/plugins/bluez5/bluez5-device.c:2165 #: spa/plugins/bluez5/bluez5-device.c:2441
#: spa/plugins/bluez5/bluez5-device.c:2171 #: spa/plugins/bluez5/bluez5-device.c:2447
#: spa/plugins/bluez5/bluez5-device.c:2177 #: spa/plugins/bluez5/bluez5-device.c:2453
#: spa/plugins/bluez5/bluez5-device.c:2183 #: spa/plugins/bluez5/bluez5-device.c:2459
msgid "Handsfree" msgid "Handsfree"
msgstr "Handsfree" msgstr "Handsfree"
#: spa/plugins/bluez5/bluez5-device.c:2141 #: spa/plugins/bluez5/bluez5-device.c:2417
msgid "Handsfree (HFP)" msgid "Handsfree (HFP)"
msgstr "Handsfree (HFP)" msgstr "Handsfree (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:2158 #: spa/plugins/bluez5/bluez5-device.c:2440
msgid "Headphone"
msgstr "Hörlurar"
#: spa/plugins/bluez5/bluez5-device.c:2164
msgid "Portable" msgid "Portable"
msgstr "Bärbar" msgstr "Bärbar"
#: spa/plugins/bluez5/bluez5-device.c:2170 #: spa/plugins/bluez5/bluez5-device.c:2446
msgid "Car" msgid "Car"
msgstr "Bil" msgstr "Bil"
#: spa/plugins/bluez5/bluez5-device.c:2176 #: spa/plugins/bluez5/bluez5-device.c:2452
msgid "HiFi" msgid "HiFi"
msgstr "HiFi" msgstr "HiFi"
#: spa/plugins/bluez5/bluez5-device.c:2182 #: spa/plugins/bluez5/bluez5-device.c:2458
msgid "Phone" msgid "Phone"
msgstr "Telefon" msgstr "Telefon"
#: spa/plugins/bluez5/bluez5-device.c:2189 #: spa/plugins/bluez5/bluez5-device.c:2465
msgid "Bluetooth" msgid "Bluetooth"
msgstr "Bluetooth" msgstr "Bluetooth"
#: spa/plugins/bluez5/bluez5-device.c:2190 #: spa/plugins/bluez5/bluez5-device.c:2466
msgid "Bluetooth (HFP)" msgid "Bluetooth Handsfree"
msgstr "Bluetooth (HFP)" msgstr "Bluetooth-handsfree"
#~ msgid "Headphone"
#~ msgstr "Hörlurar"

View file

@ -6,15 +6,15 @@
# Cheng-Chia Tseng <pswo10680@gmail.com>, 2010, 2012. # Cheng-Chia Tseng <pswo10680@gmail.com>, 2010, 2012.
# Frank Hill <hxf.prc@gmail.com>, 2015. # Frank Hill <hxf.prc@gmail.com>, 2015.
# Mingye Wang (Arthur2e5) <arthur200126@gmail.com>, 2015. # Mingye Wang (Arthur2e5) <arthur200126@gmail.com>, 2015.
# lumingzh <lumingzh@qq.com>, 2024-2025. # lumingzh <lumingzh@qq.com>, 2024-2026.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: pipewire.master-tx\n" "Project-Id-Version: pipewire.master-tx\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
"issues\n" "issues\n"
"POT-Creation-Date: 2025-11-04 15:35+0000\n" "POT-Creation-Date: 2026-04-08 13:01+0000\n"
"PO-Revision-Date: 2025-11-05 07:47+0800\n" "PO-Revision-Date: 2026-04-09 08:06+0800\n"
"Last-Translator: lumingzh <lumingzh@qq.com>\n" "Last-Translator: lumingzh <lumingzh@qq.com>\n"
"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n" "Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
"Language: zh_CN\n" "Language: zh_CN\n"
@ -22,7 +22,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2016-03-22 13:23+0000\n" "X-Launchpad-Export-Date: 2016-03-22 13:23+0000\n"
"X-Generator: Gtranslator 49.0\n" "X-Generator: Gtranslator 50.0\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: src/daemon/pipewire.c:29 #: src/daemon/pipewire.c:29
@ -65,21 +65,46 @@ msgstr "虚拟输出"
msgid "Tunnel for %s@%s" msgid "Tunnel for %s@%s"
msgstr "用于 %s@%s 的隧道" msgstr "用于 %s@%s 的隧道"
#: src/modules/module-zeroconf-discover.c:320 #: src/modules/module-zeroconf-discover.c:290
msgid "Unknown device" msgid "Unknown device"
msgstr "未知设备" msgstr "未知设备"
#: src/modules/module-zeroconf-discover.c:332 #: src/modules/module-zeroconf-discover.c:302
#, c-format #, c-format
msgid "%s on %s@%s" msgid "%s on %s@%s"
msgstr "%2$s@%3$s 上的 %1$s" msgstr "%2$s@%3$s 上的 %1$s"
#: src/modules/module-zeroconf-discover.c:336 #: src/modules/module-zeroconf-discover.c:306
#, c-format #, c-format
msgid "%s on %s" msgid "%s on %s"
msgstr "%2$s 上的 %1$s" msgstr "%2$s 上的 %1$s"
#: src/tools/pw-cat.c:1096 #: src/tools/pw-cat.c:269
#, c-format
msgid "Supported formats:\n"
msgstr "支持的格式:\n"
#: src/tools/pw-cat.c:754
#, c-format
msgid "Supported channel layouts:\n"
msgstr "支持的声道布局:\n"
#: src/tools/pw-cat.c:764
#, c-format
msgid "Supported channel layout aliases:\n"
msgstr "支持的声道布局别名:\n"
#: src/tools/pw-cat.c:766
#, c-format
msgid " %s -> %s\n"
msgstr " %s -> %s\n"
#: src/tools/pw-cat.c:771
#, c-format
msgid "Supported channel names:\n"
msgstr "支持的声道名称:\n"
#: src/tools/pw-cat.c:1183
#, c-format #, c-format
msgid "" msgid ""
"%s [options] [<file>|-]\n" "%s [options] [<file>|-]\n"
@ -94,7 +119,7 @@ msgstr ""
" -v, --verbose 输出详细操作\n" " -v, --verbose 输出详细操作\n"
"\n" "\n"
#: src/tools/pw-cat.c:1103 #: src/tools/pw-cat.c:1190
#, c-format #, c-format
msgid "" msgid ""
" -R, --remote Remote daemon name\n" " -R, --remote Remote daemon name\n"
@ -104,6 +129,8 @@ msgid ""
" --target Set node target serial or name " " --target Set node target serial or name "
"(default %s)\n" "(default %s)\n"
" 0 means don't link\n" " 0 means don't link\n"
" -C --monitor Capture monitor ports (in recording "
"mode)\n"
" --latency Set node latency (default %s)\n" " --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n" " Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n" " or direct samples (256)\n"
@ -118,6 +145,7 @@ msgstr ""
" --media-role 设置媒体角色 (默认 %s)\n" " --media-role 设置媒体角色 (默认 %s)\n"
" --target 设置节点目标序列或名称 (默认 %s)\n" " --target 设置节点目标序列或名称 (默认 %s)\n"
" 设为 0 则不链接节点\n" " 设为 0 则不链接节点\n"
" -C --monitor 捕获监视器端口 (录制模式)\n"
" --latency 设置节点延迟 (默认 %s)\n" " --latency 设置节点延迟 (默认 %s)\n"
" 时间 (单位可为 s, ms, us, ns)\n" " 时间 (单位可为 s, ms, us, ns)\n"
" 或样本数 (如256)\n" " 或样本数 (如256)\n"
@ -126,20 +154,23 @@ msgstr ""
" -P --properties 设置节点属性\n" " -P --properties 设置节点属性\n"
"\n" "\n"
#: src/tools/pw-cat.c:1121 #: src/tools/pw-cat.c:1209
#, c-format #, c-format
msgid "" msgid ""
" --rate Sample rate (req. for rec) (default " " --rate Sample rate (default %u)\n"
"%u)\n" " --channels Number of channels (default %u)\n"
" --channels Number of channels (req. for rec) "
"(default %u)\n"
" --channel-map Channel map\n" " --channel-map Channel map\n"
" one of: \"stereo\", " " a channel layout: \"Stereo\", "
"\"surround-51\",... or\n" "\"5.1\",... or\n"
" comma separated list of channel " " comma separated list of channel "
"names: eg. \"FL,FR\"\n" "names: eg. \"FL,FR\"\n"
" --format Sample format %s (req. for rec) " " --list-layouts List supported channel layouts\n"
"(default %s)\n" " --list-channel-names List supported channel maps\n"
" --format Sample format (default %s)\n"
" --list-formats List supported sample formats\n"
" --container Container format\n"
" --list-containers List supported containers and "
"extensions\n"
" --volume Stream volume 0-1.0 (default %.3f)\n" " --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default " " -q --quality Resampler quality (0 - 15) (default "
"%d)\n" "%d)\n"
@ -149,15 +180,19 @@ msgid ""
" -n, --sample-count COUNT Stop after COUNT samples\n" " -n, --sample-count COUNT Stop after COUNT samples\n"
"\n" "\n"
msgstr "" msgstr ""
" --rate 采样率 (录制模式需要) (默认 %u)\n" " --rate 采样率 (默认 %u)\n"
" --channels 通道数 (录制模式需要) (默认 %u)\n" " --channels 声道数 (默认 %u)\n"
" --channel-map 道映射\n" " --channel-map 道映射\n"
" \"stereo\", \"surround-51\",... " " 声道布局:\"stereo\", "
"中的其一或\n" "\"5.1\",... 或\n"
" 以\",\"分隔的通道名列表: 如 " " 以英文逗号分隔的声道名列表: 如 "
"\"FL,FR\"\n" "\"FL,FR\"\n"
" --format 采样格式 %s (录制模式需要) (默认 " " --list-layouts 列出支持的声道布局\n"
"%s)\n" " --list-channel-names 列出支持的声道映射\n"
" --format 采样格式 (默认 %s)\n"
" --list-formats 列出支持的采样格式\n"
" --container 容器格式\n"
" --list-containers 列出支持的容器和扩展\n"
" --volume 媒体流音量 0-1.0 (默认 %.3f)\n" " --volume 媒体流音量 0-1.0 (默认 %.3f)\n"
" -q --quality 重采样质量 (0 - 15) (默认 %d)\n" " -q --quality 重采样质量 (0 - 15) (默认 %d)\n"
" -a, --raw 原生模式\n" " -a, --raw 原生模式\n"
@ -166,7 +201,7 @@ msgstr ""
" -n, --sample-count COUNT 计数采样后停止\n" " -n, --sample-count COUNT 计数采样后停止\n"
"\n" "\n"
#: src/tools/pw-cat.c:1141 #: src/tools/pw-cat.c:1234
msgid "" msgid ""
" -p, --playback Playback mode\n" " -p, --playback Playback mode\n"
" -r, --record Recording mode\n" " -r, --record Recording mode\n"
@ -186,6 +221,11 @@ msgstr ""
" -c, --midi-clip MIDI 剪辑模式\n" " -c, --midi-clip MIDI 剪辑模式\n"
"\n" "\n"
#: src/tools/pw-cat.c:1839
#, c-format
msgid "Supported containers and extensions:\n"
msgstr "支持的容器和扩展:\n"
#: src/tools/pw-cli.c:2386 #: src/tools/pw-cli.c:2386
#, c-format #, c-format
msgid "" msgid ""
@ -208,12 +248,12 @@ msgstr ""
msgid "Pro Audio" msgid "Pro Audio"
msgstr "专业音频" msgstr "专业音频"
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699 #: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:1976 #: spa/plugins/bluez5/bluez5-device.c:2163
msgid "Off" msgid "Off"
msgstr "关" msgstr "关"
#: spa/plugins/alsa/acp/acp.c:620 #: spa/plugins/alsa/acp/acp.c:618
#, c-format #, c-format
msgid "%s [ALSA UCM error]" msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM 错误]" msgstr "%s [ALSA UCM 错误]"
@ -241,7 +281,7 @@ msgstr "输入插孔"
#: spa/plugins/alsa/acp/alsa-mixer.c:2726 #: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2810 #: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:2374 #: spa/plugins/bluez5/bluez5-device.c:2596
msgid "Microphone" msgid "Microphone"
msgstr "话筒" msgstr "话筒"
@ -307,15 +347,15 @@ msgid "No Bass Boost"
msgstr "无重低音增强" msgstr "无重低音增强"
#: spa/plugins/alsa/acp/alsa-mixer.c:2741 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2380 #: spa/plugins/bluez5/bluez5-device.c:2602
msgid "Speaker" msgid "Speaker"
msgstr "扬声器" msgstr "扬声器"
#. Don't call it "headset", the HF one has the mic #. Don't call it "headset", the HF one has the mic
#: spa/plugins/alsa/acp/alsa-mixer.c:2742 #: spa/plugins/alsa/acp/alsa-mixer.c:2742
#: spa/plugins/alsa/acp/alsa-mixer.c:2820 #: spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/bluez5/bluez5-device.c:2386 #: spa/plugins/bluez5/bluez5-device.c:2608
#: spa/plugins/bluez5/bluez5-device.c:2453 #: spa/plugins/bluez5/bluez5-device.c:2675
msgid "Headphones" msgid "Headphones"
msgstr "模拟耳机" msgstr "模拟耳机"
@ -425,7 +465,7 @@ msgstr "立体声"
#: spa/plugins/alsa/acp/alsa-mixer.c:4535 #: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4693 #: spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:2362 #: spa/plugins/bluez5/bluez5-device.c:2584
msgid "Headset" msgid "Headset"
msgstr "耳机" msgstr "耳机"
@ -620,101 +660,109 @@ msgstr "内置音频"
msgid "Modem" msgid "Modem"
msgstr "调制解调器" msgstr "调制解调器"
#: spa/plugins/bluez5/bluez5-device.c:1987 #: spa/plugins/bluez5/bluez5-device.c:2174
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "音频网关 (A2DP 信源 或 HSP/HFP 网关)" msgstr "音频网关 (A2DP 信源 或 HSP/HFP 网关)"
#: spa/plugins/bluez5/bluez5-device.c:2016 #: spa/plugins/bluez5/bluez5-device.c:2203
msgid "Audio Streaming for Hearing Aids (ASHA Sink)" msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "助听器音频流 (ASHA 信宿)" msgstr "助听器音频流 (ASHA 信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2059 #: spa/plugins/bluez5/bluez5-device.c:2246
#, c-format #, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr "高保真回放 (A2DP 信宿, 编码 %s)" msgstr "高保真回放 (A2DP 信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2062 #: spa/plugins/bluez5/bluez5-device.c:2249
#, c-format #, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr "高保真双工 (A2DP 信源/信宿, 编码 %s)" msgstr "高保真双工 (A2DP 信源/信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2070 #: spa/plugins/bluez5/bluez5-device.c:2257
msgid "High Fidelity Playback (A2DP Sink)" msgid "High Fidelity Playback (A2DP Sink)"
msgstr "高保真回放 (A2DP 信宿)" msgstr "高保真回放 (A2DP 信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2072 #: spa/plugins/bluez5/bluez5-device.c:2259
msgid "High Fidelity Duplex (A2DP Source/Sink)" msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "高保真双工 (A2DP 信源/信宿)" msgstr "高保真双工 (A2DP 信源/信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2146 #: spa/plugins/bluez5/bluez5-device.c:2281
msgid "Auto: Prefer Quality (A2DP)"
msgstr "自动:质量优先 (A2DP)"
#: spa/plugins/bluez5/bluez5-device.c:2286
msgid "Auto: Prefer Latency (A2DP)"
msgstr "自动:延迟优先 (A2DP)"
#: spa/plugins/bluez5/bluez5-device.c:2366
#, c-format #, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)" msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "高保真回放 (BAP 信宿, 编码 %s)" msgstr "高保真回放 (BAP 信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2151 #: spa/plugins/bluez5/bluez5-device.c:2371
#, c-format #, c-format
msgid "High Fidelity Input (BAP Source, codec %s)" msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "高保真输入 (BAP 信源, 编码 %s)" msgstr "高保真输入 (BAP 信源, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2155 #: spa/plugins/bluez5/bluez5-device.c:2375
#, c-format #, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "高保真双工 (BAP 信源/信宿, 编码 %s)" msgstr "高保真双工 (BAP 信源/信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2164 #: spa/plugins/bluez5/bluez5-device.c:2384
msgid "High Fidelity Playback (BAP Sink)" msgid "High Fidelity Playback (BAP Sink)"
msgstr "高保真回放 (BAP 信宿)" msgstr "高保真回放 (BAP 信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2168 #: spa/plugins/bluez5/bluez5-device.c:2388
msgid "High Fidelity Input (BAP Source)" msgid "High Fidelity Input (BAP Source)"
msgstr "高保真输入 (BAP 信源)" msgstr "高保真输入 (BAP 信源)"
#: spa/plugins/bluez5/bluez5-device.c:2171 #: spa/plugins/bluez5/bluez5-device.c:2391
msgid "High Fidelity Duplex (BAP Source/Sink)" msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "高保真双工 (BAP 信源/信宿)" msgstr "高保真双工 (BAP 信源/信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2211 #: spa/plugins/bluez5/bluez5-device.c:2431
#, c-format #, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)" msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr "头戴式耳机单元 (HSP/HFP, 编码 %s)" msgstr "头戴式耳机单元 (HSP/HFP, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2363 #: spa/plugins/bluez5/bluez5-device.c:2585
#: spa/plugins/bluez5/bluez5-device.c:2368 #: spa/plugins/bluez5/bluez5-device.c:2590
#: spa/plugins/bluez5/bluez5-device.c:2375 #: spa/plugins/bluez5/bluez5-device.c:2597
#: spa/plugins/bluez5/bluez5-device.c:2381 #: spa/plugins/bluez5/bluez5-device.c:2603
#: spa/plugins/bluez5/bluez5-device.c:2387 #: spa/plugins/bluez5/bluez5-device.c:2609
#: spa/plugins/bluez5/bluez5-device.c:2393 #: spa/plugins/bluez5/bluez5-device.c:2615
#: spa/plugins/bluez5/bluez5-device.c:2399 #: spa/plugins/bluez5/bluez5-device.c:2621
#: spa/plugins/bluez5/bluez5-device.c:2405 #: spa/plugins/bluez5/bluez5-device.c:2627
#: spa/plugins/bluez5/bluez5-device.c:2411 #: spa/plugins/bluez5/bluez5-device.c:2633
msgid "Handsfree" msgid "Handsfree"
msgstr "免提" msgstr "免提"
#: spa/plugins/bluez5/bluez5-device.c:2369 #: spa/plugins/bluez5/bluez5-device.c:2591
msgid "Handsfree (HFP)" msgid "Handsfree (HFP)"
msgstr "免提HFP" msgstr "免提HFP"
#: spa/plugins/bluez5/bluez5-device.c:2392 #: spa/plugins/bluez5/bluez5-device.c:2614
msgid "Portable" msgid "Portable"
msgstr "便携式" msgstr "便携式"
#: spa/plugins/bluez5/bluez5-device.c:2398 #: spa/plugins/bluez5/bluez5-device.c:2620
msgid "Car" msgid "Car"
msgstr "车内" msgstr "车内"
#: spa/plugins/bluez5/bluez5-device.c:2404 #: spa/plugins/bluez5/bluez5-device.c:2626
msgid "HiFi" msgid "HiFi"
msgstr "高保真" msgstr "高保真"
#: spa/plugins/bluez5/bluez5-device.c:2410 #: spa/plugins/bluez5/bluez5-device.c:2632
msgid "Phone" msgid "Phone"
msgstr "电话" msgstr "电话"
#: spa/plugins/bluez5/bluez5-device.c:2417 #: spa/plugins/bluez5/bluez5-device.c:2639
msgid "Bluetooth" msgid "Bluetooth"
msgstr "蓝牙" msgstr "蓝牙"
#: spa/plugins/bluez5/bluez5-device.c:2418 #: spa/plugins/bluez5/bluez5-device.c:2640
msgid "Bluetooth Handsfree" msgid "Bluetooth Handsfree"
msgstr "蓝牙免提" msgstr "蓝牙免提"

View file

@ -4,110 +4,221 @@
# #
# Cheng-Chia Tseng <pswo10680@gmail.com>, 2010, 2012. # Cheng-Chia Tseng <pswo10680@gmail.com>, 2010, 2012.
# pan93412 <pan93412@gmail.com>, 2020. # pan93412 <pan93412@gmail.com>, 2020.
# SPDX-FileCopyrightText: 2026 Kisaragi Hiu <mail@kisaragi-hiu.com>
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PipeWire Volume Control\n" "Project-Id-Version: PipeWire Volume Control\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
"issues/new\n" "issues/new\n"
"POT-Creation-Date: 2021-04-18 16:54+0800\n" "POT-Creation-Date: 2026-03-11 22:03+0900\n"
"PO-Revision-Date: 2020-01-11 13:49+0800\n" "PO-Revision-Date: 2026-03-11 21:24+0900\n"
"Last-Translator: pan93412 <pan93412@gmail.com>\n" "Last-Translator: Kisaragi Hiu <mail@kisaragi-hiu.com>\n"
"Language-Team: Chinese <zh-l10n@lists.linux.org.tw>\n" "Language-Team: Chinese (Taiwan) <zh-l10n@lists.slat.org>\n"
"Language: zh_TW\n" "Language: zh_TW\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Lokalize 19.12.0\n" "X-Generator: Lokalize 26.03.70\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: src/daemon/pipewire.c:43 #: src/daemon/pipewire.c:29
#, c-format #, c-format
msgid "" msgid ""
"%s [options]\n" "%s [options]\n"
" -h, --help Show this help\n" " -h, --help Show this help\n"
" -v, --verbose Increase verbosity by one level\n"
" --version Show version\n" " --version Show version\n"
" -c, --config Load config (Default %s)\n" " -c, --config Load config (Default %s)\n"
" -P --properties Set context properties\n"
msgstr "" msgstr ""
"%s [選項]\n"
" -h, --help 顯示此說明\n"
" -v, --verbose 提高訊息詳細程度一階\n"
" --version 顯示版本\n"
" -c, --config 載入設定檔案(預設 %s\n"
" -P --properties 設定前後文屬性\n"
#: src/daemon/pipewire.desktop.in:4 #: src/daemon/pipewire.desktop.in:4
msgid "PipeWire Media System" msgid "PipeWire Media System"
msgstr "" msgstr "PipeWire 媒體系統"
#: src/daemon/pipewire.desktop.in:5 #: src/daemon/pipewire.desktop.in:5
msgid "Start the PipeWire Media System" msgid "Start the PipeWire Media System"
msgstr "" msgstr "啟動 PipeWire 媒體系統"
#: src/examples/media-session/alsa-monitor.c:526 #: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
#: spa/plugins/alsa/acp/compat.c:187 #: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
msgid "Built-in Audio" #, c-format
msgstr "內部音效" msgid "Tunnel to %s%s%s"
msgstr "穿隧到 %s%s%s"
#: src/examples/media-session/alsa-monitor.c:530 #: src/modules/module-fallback-sink.c:40
#: spa/plugins/alsa/acp/compat.c:192 msgid "Dummy Output"
msgid "Modem" msgstr "虛擬輸出"
msgstr "數據機"
#: src/examples/media-session/alsa-monitor.c:539 #: src/modules/module-pulse-tunnel.c:761
#, c-format
msgid "Tunnel for %s@%s"
msgstr "%s@%s 的穿隧道"
#: src/modules/module-zeroconf-discover.c:290
msgid "Unknown device" msgid "Unknown device"
msgstr "" msgstr "未知裝置"
#: src/tools/pw-cat.c:991 #: src/modules/module-zeroconf-discover.c:302
#, c-format
msgid "%s on %s@%s"
msgstr "%s 於 %s@%s"
#: src/modules/module-zeroconf-discover.c:306
#, c-format
msgid "%s on %s"
msgstr "%s 於 %s"
#: src/tools/pw-cat.c:269
#, c-format
msgid "Supported formats:\n"
msgstr "支援的格式:\n"
#: src/tools/pw-cat.c:754
#, c-format
msgid "Supported channel layouts:\n"
msgstr "支援的頻道配置:\n"
#: src/tools/pw-cat.c:764
#, c-format
msgid "Supported channel layout aliases:\n"
msgstr "支援的頻道配置別名:\n"
#: src/tools/pw-cat.c:766
#, c-format
msgid " %s -> %s\n"
msgstr " %s -> %s\n"
#: src/tools/pw-cat.c:771
#, c-format
msgid "Supported channel names:\n"
msgstr "支援的頻道名稱:\n"
#: src/tools/pw-cat.c:1182
#, c-format #, c-format
msgid "" msgid ""
"%s [options] <file>\n" "%s [options] [<file>|-]\n"
" -h, --help Show this help\n" " -h, --help Show this help\n"
" --version Show version\n" " --version Show version\n"
" -v, --verbose Enable verbose operations\n" " -v, --verbose Enable verbose operations\n"
"\n" "\n"
msgstr "" msgstr ""
"%s [選項] [<檔案>|-]\n"
" -h, --help 顯示此說明\n"
" --version 顯示版本\n"
" -v, --verbose 操作進行時顯示詳細訊息\n"
"\n"
#: src/tools/pw-cat.c:998 #: src/tools/pw-cat.c:1189
#, c-format #, c-format
msgid "" msgid ""
" -R, --remote Remote daemon name\n" " -R, --remote Remote daemon name\n"
" --media-type Set media type (default %s)\n" " --media-type Set media type (default %s)\n"
" --media-category Set media category (default %s)\n" " --media-category Set media category (default %s)\n"
" --media-role Set media role (default %s)\n" " --media-role Set media role (default %s)\n"
" --target Set node target (default %s)\n" " --target Set node target serial or name "
"(default %s)\n"
" 0 means don't link\n" " 0 means don't link\n"
" --latency Set node latency (default %s)\n" " --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n" " Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n" " or direct samples (256)\n"
" the rate is the one of the source " " the rate is the one of the source "
"file\n" "file\n"
" --list-targets List available targets for --target\n" " -P --properties Set node properties\n"
"\n" "\n"
msgstr "" msgstr ""
" -R, --remote 遠端守護程式名稱\n"
" --media-type 設定媒體型態(預設 %s\n"
" --media-category 設定媒體分類(預設 %s\n"
" --media-role 設定媒體角色(預設 %s\n"
" --target 設定節點目標序號或名稱(預設 %s\n"
" 0 代表不要連結\n"
" --latency 設定節點延遲(預設 %s\n"
" X單位單位為 s, ms, us 或 ns\n"
" 或直接指定樣本數 (256)\n"
" 取樣率取自來源檔案\n"
" -P --properties 設定節點屬性\n"
"\n"
#: src/tools/pw-cat.c:1016 #: src/tools/pw-cat.c:1207
#, c-format #, c-format
msgid "" msgid ""
" --rate Sample rate (req. for rec) (default " " --rate Sample rate (default %u)\n"
"%u)\n" " --channels Number of channels (default %u)\n"
" --channels Number of channels (req. for rec) "
"(default %u)\n"
" --channel-map Channel map\n" " --channel-map Channel map\n"
" one of: \"stereo\", " " a channel layout: \"Stereo\", "
"\"surround-51\",... or\n" "\"5.1\",... or\n"
" comma separated list of channel " " comma separated list of channel "
"names: eg. \"FL,FR\"\n" "names: eg. \"FL,FR\"\n"
" --format Sample format %s (req. for rec) " " --list-layouts List supported channel layouts\n"
"(default %s)\n" " --list-channel-names List supported channel maps\n"
" --format Sample format (default %s)\n"
" --list-formats List supported sample formats\n"
" --container Container format\n"
" --list-containers List supported containers and "
"extensions\n"
" --volume Stream volume 0-1.0 (default %.3f)\n" " --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default " " -q --quality Resampler quality (0 - 15) (default "
"%d)\n" "%d)\n"
" -a, --raw RAW mode\n"
" -M, --force-midi Force midi format, one of \"midi\" "
"or \"ump\", (default ump)\n"
" -n, --sample-count COUNT Stop after COUNT samples\n"
"\n" "\n"
msgstr "" msgstr ""
" --rate 取樣率(預設 %u\n"
" --channels 頻道數量(預設 %u\n"
" --channel-map 頻道映射\n"
" 可以是頻道配置名稱,像是 "
"\"Stereo\"、\"5.1\" 等等,或是\n"
" 以逗號分隔的頻道名稱清單,像是 "
"\"FL,FR\"\n"
" --list-layouts 列出支援的頻道配置\n"
" --list-channel-names 列出支援的頻道映射\n"
" --format 取樣格式(預設 %s\n"
" --list-formats 列出支援的取樣格式\n"
" --container 容器格式\n"
" --list-containers 列出支援的容器與副檔名\n"
" --volume 串流音量 0-1.0(預設 %.3f\n"
" -q --quality 重新取樣器品質 0-15預設 %d\n"
" -a, --raw 原始模式\n"
" -M, --force-midi 強制使用 midi 格式,可選擇 \"midi\" "
"或 \"ump\"(預設 ump\n"
" -n, --sample-count 樣本數 在「樣本數」個樣本之後停止\n"
"\n"
#: src/tools/pw-cat.c:1033 #: src/tools/pw-cat.c:1232
msgid "" msgid ""
" -p, --playback Playback mode\n" " -p, --playback Playback mode\n"
" -r, --record Recording mode\n" " -r, --record Recording mode\n"
" -m, --midi Midi mode\n" " -m, --midi Midi mode\n"
" -d, --dsd DSD mode\n"
" -o, --encoded Encoded mode\n"
" -s, --sysex SysEx mode\n"
" -c, --midi-clip MIDI clip mode\n"
"\n" "\n"
msgstr "" msgstr ""
" -p, --playback 播放模式\n"
" -r, --record 錄製模式\n"
" -m, --midi Midi 模式\n"
" -d, --dsd DSD 模式\n"
" -o, --encoded 已編碼模式\n"
" -s, --sysex SysEx 模式\n"
" -c, --midi-clip MIDI 素材模式\n"
"\n"
#: src/tools/pw-cli.c:2932 #: src/tools/pw-cat.c:1837
#, c-format
msgid "Supported containers and extensions:\n"
msgstr "支援的容器與副檔名:\n"
#: src/tools/pw-cli.c:2386
#, c-format #, c-format
msgid "" msgid ""
"%s [options] [command]\n" "%s [options] [command]\n"
@ -115,357 +226,363 @@ msgid ""
" --version Show version\n" " --version Show version\n"
" -d, --daemon Start as daemon (Default false)\n" " -d, --daemon Start as daemon (Default false)\n"
" -r, --remote Remote daemon name\n" " -r, --remote Remote daemon name\n"
" -m, --monitor Monitor activity\n"
"\n" "\n"
msgstr "" msgstr ""
"%s [選項] [指令]\n"
" -h, --help 顯示此說明\n"
" --version 顯示版本\n"
" -d, --daemon 作為守護程式啟動(預設為否)\n"
" -r, --remote 遠端守護程式名稱\n"
" -m, --monitor 監控活動\n"
"\n"
#: spa/plugins/alsa/acp/acp.c:290 #: spa/plugins/alsa/acp/acp.c:361
msgid "Pro Audio" msgid "Pro Audio"
msgstr "" msgstr "Pro Audio"
#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704 #: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:1000 #: spa/plugins/bluez5/bluez5-device.c:2021
msgid "Off" msgid "Off"
msgstr "關閉" msgstr "關閉"
#: spa/plugins/alsa/acp/channelmap.h:466 #: spa/plugins/alsa/acp/acp.c:618
msgid "(invalid)" #, c-format
msgstr "(無效)" msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM 錯誤]"
#: spa/plugins/alsa/acp/alsa-mixer.c:2709 #: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Input" msgid "Input"
msgstr "輸入" msgstr "輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2710 #: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "Docking Station Input" msgid "Docking Station Input"
msgstr "Docking Station 輸入" msgstr "Docking Station 輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2711 #: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Docking Station Microphone" msgid "Docking Station Microphone"
msgstr "Docking Station 麥克風" msgstr "Docking Station 麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2712 #: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "Docking Station Line In" msgid "Docking Station Line In"
msgstr "Docking Station 線路輸入" msgstr "Docking Station 線路輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2713 #: spa/plugins/alsa/acp/alsa-mixer.c:2725
#: spa/plugins/alsa/acp/alsa-mixer.c:2804 #: spa/plugins/alsa/acp/alsa-mixer.c:2816
msgid "Line In" msgid "Line In"
msgstr "線路輸入" msgstr "線路輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2714 #: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2798 #: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:1145 #: spa/plugins/bluez5/bluez5-device.c:2422
msgid "Microphone" msgid "Microphone"
msgstr "麥克風" msgstr "麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2715 #: spa/plugins/alsa/acp/alsa-mixer.c:2727
#: spa/plugins/alsa/acp/alsa-mixer.c:2799 #: spa/plugins/alsa/acp/alsa-mixer.c:2811
msgid "Front Microphone" msgid "Front Microphone"
msgstr "前方麥克風" msgstr "前方麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2716 #: spa/plugins/alsa/acp/alsa-mixer.c:2728
#: spa/plugins/alsa/acp/alsa-mixer.c:2800 #: spa/plugins/alsa/acp/alsa-mixer.c:2812
msgid "Rear Microphone" msgid "Rear Microphone"
msgstr "後方麥克風" msgstr "後方麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2717 #: spa/plugins/alsa/acp/alsa-mixer.c:2729
msgid "External Microphone" msgid "External Microphone"
msgstr "外接麥克風" msgstr "外接麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2718 #: spa/plugins/alsa/acp/alsa-mixer.c:2730
#: spa/plugins/alsa/acp/alsa-mixer.c:2802 #: spa/plugins/alsa/acp/alsa-mixer.c:2814
msgid "Internal Microphone" msgid "Internal Microphone"
msgstr "內建麥克風" msgstr "內建麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2719 #: spa/plugins/alsa/acp/alsa-mixer.c:2731
#: spa/plugins/alsa/acp/alsa-mixer.c:2805 #: spa/plugins/alsa/acp/alsa-mixer.c:2817
msgid "Radio" msgid "Radio"
msgstr "無線電" msgstr "無線電"
#: spa/plugins/alsa/acp/alsa-mixer.c:2720 #: spa/plugins/alsa/acp/alsa-mixer.c:2732
#: spa/plugins/alsa/acp/alsa-mixer.c:2806 #: spa/plugins/alsa/acp/alsa-mixer.c:2818
msgid "Video" msgid "Video"
msgstr "視訊" msgstr "視訊"
#: spa/plugins/alsa/acp/alsa-mixer.c:2721 #: spa/plugins/alsa/acp/alsa-mixer.c:2733
msgid "Automatic Gain Control" msgid "Automatic Gain Control"
msgstr "自動增益控制" msgstr "自動增益控制"
#: spa/plugins/alsa/acp/alsa-mixer.c:2722 #: spa/plugins/alsa/acp/alsa-mixer.c:2734
msgid "No Automatic Gain Control" msgid "No Automatic Gain Control"
msgstr "無自動增益控制" msgstr "無自動增益控制"
#: spa/plugins/alsa/acp/alsa-mixer.c:2723 #: spa/plugins/alsa/acp/alsa-mixer.c:2735
msgid "Boost" msgid "Boost"
msgstr "增強" msgstr "增強"
#: spa/plugins/alsa/acp/alsa-mixer.c:2724 #: spa/plugins/alsa/acp/alsa-mixer.c:2736
msgid "No Boost" msgid "No Boost"
msgstr "無增強" msgstr "無增強"
#: spa/plugins/alsa/acp/alsa-mixer.c:2725 #: spa/plugins/alsa/acp/alsa-mixer.c:2737
msgid "Amplifier" msgid "Amplifier"
msgstr "擴大器" msgstr "擴大器"
#: spa/plugins/alsa/acp/alsa-mixer.c:2726 #: spa/plugins/alsa/acp/alsa-mixer.c:2738
msgid "No Amplifier" msgid "No Amplifier"
msgstr "無擴大器" msgstr "無擴大器"
#: spa/plugins/alsa/acp/alsa-mixer.c:2727 #: spa/plugins/alsa/acp/alsa-mixer.c:2739
msgid "Bass Boost" msgid "Bass Boost"
msgstr "低音增強" msgstr "低音增強"
#: spa/plugins/alsa/acp/alsa-mixer.c:2728 #: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "No Bass Boost" msgid "No Bass Boost"
msgstr "無低音增強" msgstr "無低音增強"
#: spa/plugins/alsa/acp/alsa-mixer.c:2729 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:1150 #: spa/plugins/bluez5/bluez5-device.c:2428
msgid "Speaker" msgid "Speaker"
msgstr "喇叭" msgstr "喇叭"
#: spa/plugins/alsa/acp/alsa-mixer.c:2730 #. Don't call it "headset", the HF one has the mic
#: spa/plugins/alsa/acp/alsa-mixer.c:2808 #: spa/plugins/alsa/acp/alsa-mixer.c:2742
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/bluez5/bluez5-device.c:2434
#: spa/plugins/bluez5/bluez5-device.c:2501
msgid "Headphones" msgid "Headphones"
msgstr "頭戴式耳機" msgstr "頭戴式耳機"
#: spa/plugins/alsa/acp/alsa-mixer.c:2797 #: spa/plugins/alsa/acp/alsa-mixer.c:2809
msgid "Analog Input" msgid "Analog Input"
msgstr "類比輸入" msgstr "類比輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2801 #: spa/plugins/alsa/acp/alsa-mixer.c:2813
msgid "Dock Microphone" msgid "Dock Microphone"
msgstr "臺座麥克風" msgstr "臺座麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2803 #: spa/plugins/alsa/acp/alsa-mixer.c:2815
msgid "Headset Microphone" msgid "Headset Microphone"
msgstr "耳麥麥克風" msgstr "耳麥麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2807 #: spa/plugins/alsa/acp/alsa-mixer.c:2819
msgid "Analog Output" msgid "Analog Output"
msgstr "類比輸出" msgstr "類比輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2809 #: spa/plugins/alsa/acp/alsa-mixer.c:2821
#, fuzzy
msgid "Headphones 2" msgid "Headphones 2"
msgstr "頭戴式耳機" msgstr "頭戴式耳機 2"
#: spa/plugins/alsa/acp/alsa-mixer.c:2810 #: spa/plugins/alsa/acp/alsa-mixer.c:2822
msgid "Headphones Mono Output" msgid "Headphones Mono Output"
msgstr "頭戴式耳機單聲道輸出" msgstr "頭戴式耳機單聲道輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2811 #: spa/plugins/alsa/acp/alsa-mixer.c:2823
msgid "Line Out" msgid "Line Out"
msgstr "線路輸出" msgstr "線路輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2812 #: spa/plugins/alsa/acp/alsa-mixer.c:2824
msgid "Analog Mono Output" msgid "Analog Mono Output"
msgstr "類比單聲道輸出" msgstr "類比單聲道輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2813 #: spa/plugins/alsa/acp/alsa-mixer.c:2825
msgid "Speakers" msgid "Speakers"
msgstr "喇叭" msgstr "喇叭"
#: spa/plugins/alsa/acp/alsa-mixer.c:2814 #: spa/plugins/alsa/acp/alsa-mixer.c:2826
msgid "HDMI / DisplayPort" msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort" msgstr "HDMI / DisplayPort"
#: spa/plugins/alsa/acp/alsa-mixer.c:2815 #: spa/plugins/alsa/acp/alsa-mixer.c:2827
msgid "Digital Output (S/PDIF)" msgid "Digital Output (S/PDIF)"
msgstr "數位輸出 (S/PDIF)" msgstr "數位輸出 (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2816 #: spa/plugins/alsa/acp/alsa-mixer.c:2828
msgid "Digital Input (S/PDIF)" msgid "Digital Input (S/PDIF)"
msgstr "數位輸入 (S/PDIF)" msgstr "數位輸入 (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2817 #: spa/plugins/alsa/acp/alsa-mixer.c:2829
msgid "Multichannel Input" msgid "Multichannel Input"
msgstr "多聲道輸入" msgstr "多聲道輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2818 #: spa/plugins/alsa/acp/alsa-mixer.c:2830
msgid "Multichannel Output" msgid "Multichannel Output"
msgstr "多聲道輸出" msgstr "多聲道輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2819 #: spa/plugins/alsa/acp/alsa-mixer.c:2831
msgid "Game Output" msgid "Game Output"
msgstr "遊戲輸出" msgstr "遊戲輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2820 #: spa/plugins/alsa/acp/alsa-mixer.c:2832
#: spa/plugins/alsa/acp/alsa-mixer.c:2821 #: spa/plugins/alsa/acp/alsa-mixer.c:2833
msgid "Chat Output" msgid "Chat Output"
msgstr "聊天輸出" msgstr "聊天輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2822 #: spa/plugins/alsa/acp/alsa-mixer.c:2834
#, fuzzy
msgid "Chat Input" msgid "Chat Input"
msgstr "聊天輸" msgstr "聊天輸"
#: spa/plugins/alsa/acp/alsa-mixer.c:2823 #: spa/plugins/alsa/acp/alsa-mixer.c:2835
#, fuzzy
msgid "Virtual Surround 7.1" msgid "Virtual Surround 7.1"
msgstr "虛擬環繞聲 sink" msgstr "虛擬環繞聲 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4527 #: spa/plugins/alsa/acp/alsa-mixer.c:4522
msgid "Analog Mono" msgid "Analog Mono"
msgstr "類比單聲道" msgstr "類比單聲道"
#: spa/plugins/alsa/acp/alsa-mixer.c:4528 #: spa/plugins/alsa/acp/alsa-mixer.c:4523
#, fuzzy
msgid "Analog Mono (Left)" msgid "Analog Mono (Left)"
msgstr "類比單聲道" msgstr "類比單聲道(左)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4529 #: spa/plugins/alsa/acp/alsa-mixer.c:4524
#, fuzzy
msgid "Analog Mono (Right)" msgid "Analog Mono (Right)"
msgstr "類比單聲道" msgstr "類比單聲道(右)"
#. Note: Not translated to "Analog Stereo Input", because the source #. Note: Not translated to "Analog Stereo Input", because the source
#. * name gets "Input" appended to it automatically, so adding "Input" #. * name gets "Input" appended to it automatically, so adding "Input"
#. * here would lead to the source name to become "Analog Stereo Input #. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output, #. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output. #. * multichannel-input and multichannel-output.
#: spa/plugins/alsa/acp/alsa-mixer.c:4530 #: spa/plugins/alsa/acp/alsa-mixer.c:4525
#: spa/plugins/alsa/acp/alsa-mixer.c:4538 #: spa/plugins/alsa/acp/alsa-mixer.c:4533
#: spa/plugins/alsa/acp/alsa-mixer.c:4539 #: spa/plugins/alsa/acp/alsa-mixer.c:4534
msgid "Analog Stereo" msgid "Analog Stereo"
msgstr "類比立體聲" msgstr "類比立體聲"
#: spa/plugins/alsa/acp/alsa-mixer.c:4531 #: spa/plugins/alsa/acp/alsa-mixer.c:4526
msgid "Mono" msgid "Mono"
msgstr "單聲道" msgstr "單聲道"
#: spa/plugins/alsa/acp/alsa-mixer.c:4532 #: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Stereo" msgid "Stereo"
msgstr "立體聲" msgstr "立體聲"
#: spa/plugins/alsa/acp/alsa-mixer.c:4540 #: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4698 #: spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:1135 #: spa/plugins/bluez5/bluez5-device.c:2410
msgid "Headset" msgid "Headset"
msgstr "耳麥" msgstr "耳麥"
#: spa/plugins/alsa/acp/alsa-mixer.c:4541 #: spa/plugins/alsa/acp/alsa-mixer.c:4536
#: spa/plugins/alsa/acp/alsa-mixer.c:4699 #: spa/plugins/alsa/acp/alsa-mixer.c:4694
#, fuzzy
msgid "Speakerphone" msgid "Speakerphone"
msgstr "喇叭" msgstr "會議揚聲器"
#: spa/plugins/alsa/acp/alsa-mixer.c:4542 #: spa/plugins/alsa/acp/alsa-mixer.c:4537
#: spa/plugins/alsa/acp/alsa-mixer.c:4543 #: spa/plugins/alsa/acp/alsa-mixer.c:4538
msgid "Multichannel" msgid "Multichannel"
msgstr "多聲道" msgstr "多聲道"
#: spa/plugins/alsa/acp/alsa-mixer.c:4544 #: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Surround 2.1" msgid "Analog Surround 2.1"
msgstr "類比環繞聲 2.1" msgstr "類比環繞聲 2.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4545 #: spa/plugins/alsa/acp/alsa-mixer.c:4540
msgid "Analog Surround 3.0" msgid "Analog Surround 3.0"
msgstr "類比環繞聲 3.0" msgstr "類比環繞聲 3.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4546 #: spa/plugins/alsa/acp/alsa-mixer.c:4541
msgid "Analog Surround 3.1" msgid "Analog Surround 3.1"
msgstr "類比環繞聲 3.1" msgstr "類比環繞聲 3.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4547 #: spa/plugins/alsa/acp/alsa-mixer.c:4542
msgid "Analog Surround 4.0" msgid "Analog Surround 4.0"
msgstr "類比環繞聲 4.0" msgstr "類比環繞聲 4.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4548 #: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Analog Surround 4.1" msgid "Analog Surround 4.1"
msgstr "類比環繞聲 4.1" msgstr "類比環繞聲 4.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4549 #: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 5.0" msgid "Analog Surround 5.0"
msgstr "類比環繞聲 5.0" msgstr "類比環繞聲 5.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4550 #: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 5.1" msgid "Analog Surround 5.1"
msgstr "類比環繞聲 5.1" msgstr "類比環繞聲 5.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4551 #: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 6.0" msgid "Analog Surround 6.0"
msgstr "類比環繞聲 6.0" msgstr "類比環繞聲 6.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4552 #: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 6.1" msgid "Analog Surround 6.1"
msgstr "類比環繞聲 6.1" msgstr "類比環繞聲 6.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4553 #: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 7.0" msgid "Analog Surround 7.0"
msgstr "類比環繞聲 7.0" msgstr "類比環繞聲 7.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4554 #: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 7.1" msgid "Analog Surround 7.1"
msgstr "類比環繞聲 7.1" msgstr "類比環繞聲 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4555 #: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Digital Stereo (IEC958)" msgid "Digital Stereo (IEC958)"
msgstr "數位立體聲 (IEC958)" msgstr "數位立體聲 (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4556 #: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Digital Surround 4.0 (IEC958/AC3)" msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "數位環繞聲 4.0 (IEC958/AC3)" msgstr "數位環繞聲 4.0 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4557 #: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Digital Surround 5.1 (IEC958/AC3)" msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "數位環繞聲 5.1 (IEC958/AC3)" msgstr "數位環繞聲 5.1 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4558 #: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Digital Surround 5.1 (IEC958/DTS)" msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "數位環繞聲 5.1 (IEC958/DTS)" msgstr "數位環繞聲 5.1 (IEC958/DTS)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4559 #: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Digital Stereo (HDMI)" msgid "Digital Stereo (HDMI)"
msgstr "數位立體聲 (HDMI)" msgstr "數位立體聲 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4560 #: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Surround 5.1 (HDMI)" msgid "Digital Surround 5.1 (HDMI)"
msgstr "數位環繞聲 5.1 (HDMI)" msgstr "數位環繞聲 5.1 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4561 #: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Chat" msgid "Chat"
msgstr "" msgstr "聊天"
#: spa/plugins/alsa/acp/alsa-mixer.c:4562 #: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Game" msgid "Game"
msgstr "" msgstr "遊戲"
#: spa/plugins/alsa/acp/alsa-mixer.c:4696 #: spa/plugins/alsa/acp/alsa-mixer.c:4691
msgid "Analog Mono Duplex" msgid "Analog Mono Duplex"
msgstr "類比單聲道雙工" msgstr "類比單聲道雙工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4697 #: spa/plugins/alsa/acp/alsa-mixer.c:4692
msgid "Analog Stereo Duplex" msgid "Analog Stereo Duplex"
msgstr "類比立體聲雙工" msgstr "類比立體聲雙工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4700 #: spa/plugins/alsa/acp/alsa-mixer.c:4695
msgid "Digital Stereo Duplex (IEC958)" msgid "Digital Stereo Duplex (IEC958)"
msgstr "數位立體聲雙工 (IEC958)" msgstr "數位立體聲雙工 (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4701 #: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Multichannel Duplex" msgid "Multichannel Duplex"
msgstr "多聲道雙工" msgstr "多聲道雙工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4702 #: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Stereo Duplex" msgid "Stereo Duplex"
msgstr "立體聲雙工" msgstr "立體聲雙工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4703 #: spa/plugins/alsa/acp/alsa-mixer.c:4698
msgid "Mono Chat + 7.1 Surround" msgid "Mono Chat + 7.1 Surround"
msgstr "" msgstr "單聲道聊天 + 7.1 立體聲"
#: spa/plugins/alsa/acp/alsa-mixer.c:4806 #: spa/plugins/alsa/acp/alsa-mixer.c:4799
#, c-format #, c-format
msgid "%s Output" msgid "%s Output"
msgstr "%s 輸出" msgstr "%s 輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:4813 #: spa/plugins/alsa/acp/alsa-mixer.c:4807
#, c-format #, c-format
msgid "%s Input" msgid "%s Input"
msgstr "%s 輸入" msgstr "%s 輸入"
#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269 #: spa/plugins/alsa/acp/alsa-util.c:1233 spa/plugins/alsa/acp/alsa-util.c:1327
#, c-format #, c-format
msgid "" msgid ""
"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu " "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
@ -481,23 +598,23 @@ msgstr[0] ""
"snd_pcm_avail() 傳回超出預期的大值:%lu bytes (%lu ms)。\n" "snd_pcm_avail() 傳回超出預期的大值:%lu bytes (%lu ms)。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。" "這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
#: spa/plugins/alsa/acp/alsa-util.c:1241 #: spa/plugins/alsa/acp/alsa-util.c:1299
#, c-format #, c-format
msgid "" msgid ""
"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s" "snd_pcm_delay() returned a value that is exceptionally large: %li byte "
"%lu ms).\n" "(%s%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers." "to the ALSA developers."
msgid_plural "" msgid_plural ""
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s" "snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
"%lu ms).\n" "(%s%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers." "to the ALSA developers."
msgstr[0] "" msgstr[0] ""
"snd_pcm_delay() 傳回超出預期的大值:%li bytes (%s%lu ms)。\n" "snd_pcm_delay() 傳回超出預期的大值:%li bytes (%s%lu ms)。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。" "這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
#: spa/plugins/alsa/acp/alsa-util.c:1288 #: spa/plugins/alsa/acp/alsa-util.c:1346
#, c-format #, c-format
msgid "" msgid ""
"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail " "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
@ -508,7 +625,7 @@ msgstr ""
"snd_pcm_avail_delay() 傳回超出預期的大值:延遲 %lu 少於可用的 %lu。\n" "snd_pcm_avail_delay() 傳回超出預期的大值:延遲 %lu 少於可用的 %lu。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。" "這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
#: spa/plugins/alsa/acp/alsa-util.c:1331 #: spa/plugins/alsa/acp/alsa-util.c:1389
#, c-format #, c-format
msgid "" msgid ""
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte " "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
@ -524,62 +641,115 @@ msgstr[0] ""
"snd_pcm_mmap_begin() 傳回超出預期的大值:%lu bytes (%lu ms)。\n" "snd_pcm_mmap_begin() 傳回超出預期的大值:%lu bytes (%lu ms)。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。" "這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
#: spa/plugins/bluez5/bluez5-device.c:1010 #: spa/plugins/alsa/acp/channelmap.h:460
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgid "(invalid)"
msgstr "" msgstr "(無效)"
#: spa/plugins/bluez5/bluez5-device.c:1033 #: spa/plugins/alsa/acp/compat.c:194
msgid "Built-in Audio"
msgstr "內部音效"
#: spa/plugins/alsa/acp/compat.c:199
msgid "Modem"
msgstr "數據機"
#: spa/plugins/bluez5/bluez5-device.c:2032
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "音訊閘道 (A2DP Source & HSP/HFP AG)"
#: spa/plugins/bluez5/bluez5-device.c:2061
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "助聽器的音訊串流 (ASHA Sink)"
#: spa/plugins/bluez5/bluez5-device.c:2104
#, c-format #, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr "" msgstr "高傳真播放裝置 (A2DP Sink編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:1035 #: spa/plugins/bluez5/bluez5-device.c:2107
#, c-format #, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr "" msgstr "高傳真雙工裝置 (A2DP Source/Sink編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:1041 #: spa/plugins/bluez5/bluez5-device.c:2115
msgid "High Fidelity Playback (A2DP Sink)" msgid "High Fidelity Playback (A2DP Sink)"
msgstr "" msgstr "高傳真播放裝置 (A2DP Sink)"
#: spa/plugins/bluez5/bluez5-device.c:1043 #: spa/plugins/bluez5/bluez5-device.c:2117
msgid "High Fidelity Duplex (A2DP Source/Sink)" msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "" msgstr "高傳真雙工裝置 (A2DP Source/Sink)"
#: spa/plugins/bluez5/bluez5-device.c:1070 #: spa/plugins/bluez5/bluez5-device.c:2194
#, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "高傳真播放裝置 (BAP Sink編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2199
#, c-format
msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "高傳真輸入裝置 (BAP Source編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2203
#, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "高傳真雙工裝置 (BAP Source/Sink編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2212
msgid "High Fidelity Playback (BAP Sink)"
msgstr "高傳真播放裝置 (BAP Sink)"
#: spa/plugins/bluez5/bluez5-device.c:2216
msgid "High Fidelity Input (BAP Source)"
msgstr "高傳真輸入裝置 (BAP Source)"
#: spa/plugins/bluez5/bluez5-device.c:2219
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "高傳真雙工裝置 (BAP Source/Sink)"
#: spa/plugins/bluez5/bluez5-device.c:2259
#, c-format #, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)" msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr "" msgstr "耳麥頭戴單元 (HSP/HFP編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:1074 #: spa/plugins/bluez5/bluez5-device.c:2411
msgid "Headset Head Unit (HSP/HFP)" #: spa/plugins/bluez5/bluez5-device.c:2416
msgstr "" #: spa/plugins/bluez5/bluez5-device.c:2423
#: spa/plugins/bluez5/bluez5-device.c:2429
#: spa/plugins/bluez5/bluez5-device.c:1140 #: spa/plugins/bluez5/bluez5-device.c:2435
#: spa/plugins/bluez5/bluez5-device.c:2441
#: spa/plugins/bluez5/bluez5-device.c:2447
#: spa/plugins/bluez5/bluez5-device.c:2453
#: spa/plugins/bluez5/bluez5-device.c:2459
msgid "Handsfree" msgid "Handsfree"
msgstr "免持裝置" msgstr "免持裝置"
#: spa/plugins/bluez5/bluez5-device.c:1155 #: spa/plugins/bluez5/bluez5-device.c:2417
msgid "Headphone" msgid "Handsfree (HFP)"
msgstr "頭戴式耳機" msgstr "免持裝置 (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:1160 #: spa/plugins/bluez5/bluez5-device.c:2440
msgid "Portable" msgid "Portable"
msgstr "可攜裝置" msgstr "可攜裝置"
#: spa/plugins/bluez5/bluez5-device.c:1165 #: spa/plugins/bluez5/bluez5-device.c:2446
msgid "Car" msgid "Car"
msgstr "汽車" msgstr "汽車"
#: spa/plugins/bluez5/bluez5-device.c:1170 #: spa/plugins/bluez5/bluez5-device.c:2452
msgid "HiFi" msgid "HiFi"
msgstr "HiFi" msgstr "HiFi"
#: spa/plugins/bluez5/bluez5-device.c:1175 #: spa/plugins/bluez5/bluez5-device.c:2458
msgid "Phone" msgid "Phone"
msgstr "手機" msgstr "手機"
#: spa/plugins/bluez5/bluez5-device.c:1181 #: spa/plugins/bluez5/bluez5-device.c:2465
#, fuzzy
msgid "Bluetooth" msgid "Bluetooth"
msgstr "藍牙輸入" msgstr "藍牙"
#: spa/plugins/bluez5/bluez5-device.c:2466
msgid "Bluetooth Handsfree"
msgstr "藍牙免持裝置"
#~ msgid "Headphone"
#~ msgstr "頭戴式耳機"

View file

@ -8,6 +8,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <spa/debug/context.h> #include <spa/debug/context.h>
#include <spa/utils/string.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -30,13 +31,15 @@ SPA_API_DEBUG_MEM int spa_debugc_mem(struct spa_debug_context *ctx, int indent,
{ {
const uint8_t *t = (const uint8_t*)data; const uint8_t *t = (const uint8_t*)data;
char buffer[512]; char buffer[512];
struct spa_strbuf b;
size_t i; size_t i;
int pos = 0;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (i % 16 == 0) if (i % 16 == 0) {
pos = sprintf(buffer, "%p: ", &t[i]); spa_strbuf_init(&b, buffer, sizeof(buffer));
pos += sprintf(buffer + pos, "%02x ", t[i]); spa_strbuf_append(&b, "%p: ", &t[i]);
}
spa_strbuf_append(&b, "%02x ", t[i]);
if (i % 16 == 15 || i == size - 1) { if (i % 16 == 15 || i == size - 1) {
spa_debugc(ctx, "%*s" "%s", indent, "", buffer); spa_debugc(ctx, "%*s" "%s", indent, "", buffer);
} }

View file

@ -33,6 +33,8 @@ spa_format_audio_dsd_parse(const struct spa_pod *format, struct spa_audio_info_d
{ {
struct spa_pod *position = NULL; struct spa_pod *position = NULL;
int res; int res;
uint32_t max_position = SPA_N_ELEMENTS(info->position);
info->flags = 0; info->flags = 0;
res = spa_pod_parse_object(format, res = spa_pod_parse_object(format,
SPA_TYPE_OBJECT_Format, NULL, SPA_TYPE_OBJECT_Format, NULL,
@ -41,10 +43,13 @@ spa_format_audio_dsd_parse(const struct spa_pod *format, struct spa_audio_info_d
SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate), SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate),
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
if (info->channels > max_position)
return -EINVAL;
if (position == NULL || if (position == NULL ||
!spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_N_ELEMENTS(info->position))) spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) {
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
spa_memzero(info->position, max_position * sizeof(info->position[0]));
}
return res; return res;
} }

View file

@ -75,7 +75,7 @@ static const struct spa_type_audio_layout_info {
{ "7.1", { SPA_AUDIO_LAYOUT_7_1 } }, { "7.1", { SPA_AUDIO_LAYOUT_7_1 } },
{ "7.1W", { SPA_AUDIO_LAYOUT_7_1W } }, { "7.1W", { SPA_AUDIO_LAYOUT_7_1W } },
{ "7.1WR", { SPA_AUDIO_LAYOUT_7_1WR } }, { "7.1WR", { SPA_AUDIO_LAYOUT_7_1WR } },
{ NULL, }, { NULL, { 0, { SPA_AUDIO_CHANNEL_UNKNOWN } } },
}; };
SPA_API_AUDIO_LAYOUT_TYPES int SPA_API_AUDIO_LAYOUT_TYPES int
@ -87,7 +87,7 @@ spa_audio_layout_info_parse_name(struct spa_audio_layout_info *layout, size_t si
uint32_t i, n_pos; uint32_t i, n_pos;
if (spa_atou32(name+3, &n_pos, 10)) { if (spa_atou32(name+3, &n_pos, 10)) {
if (n_pos > max_position) if (n_pos > max_position)
return -ECHRNG; return -EINVAL;
for (i = 0; i < 0x1000 && i < n_pos; i++) for (i = 0; i < 0x1000 && i < n_pos; i++)
layout->position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; layout->position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
for (; i < n_pos; i++) for (; i < n_pos; i++)
@ -99,7 +99,7 @@ spa_audio_layout_info_parse_name(struct spa_audio_layout_info *layout, size_t si
SPA_FOR_EACH_ELEMENT_VAR(spa_type_audio_layout_info, i) { SPA_FOR_EACH_ELEMENT_VAR(spa_type_audio_layout_info, i) {
if (spa_streq(name, i->name)) { if (spa_streq(name, i->name)) {
if (i->layout.n_channels > max_position) if (i->layout.n_channels > max_position)
return -ECHRNG; return -EINVAL;
*layout = i->layout; *layout = i->layout;
return i->layout.n_channels; return i->layout.n_channels;
} }

View file

@ -88,14 +88,14 @@ spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size,
} else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) { } else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) {
if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) { if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) {
if (v > max_position) if (v > max_position)
return -ECHRNG; return -EINVAL;
info->channels = v; info->channels = v;
} }
} else if (spa_streq(key, SPA_KEY_AUDIO_LAYOUT)) { } else if (spa_streq(key, SPA_KEY_AUDIO_LAYOUT)) {
if (force || info->channels == 0) { if (force || info->channels == 0) {
if (spa_audio_parse_layout(val, info->position, max_position, &v) > 0) { if (spa_audio_parse_layout(val, info->position, max_position, &v) > 0) {
if (v > max_position) if (v > max_position)
return -ECHRNG; return -EINVAL;
info->channels = v; info->channels = v;
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
} }
@ -105,7 +105,7 @@ spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size,
if (spa_audio_parse_position_n(val, strlen(val), info->position, if (spa_audio_parse_position_n(val, strlen(val), info->position,
max_position, &v) > 0) { max_position, &v) > 0) {
if (v > max_position) if (v > max_position)
return -ECHRNG; return -EINVAL;
info->channels = v; info->channels = v;
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
} }

View file

@ -46,10 +46,12 @@ spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_in
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
if (info->channels > max_position) if (info->channels > max_position)
return -ECHRNG; return -EINVAL;
if (position == NULL || if (position == NULL ||
spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) {
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
spa_memzero(info->position, max_position * sizeof(info->position[0]));
}
return res; return res;
} }

View file

@ -156,41 +156,42 @@ enum spa_audio_channel {
SPA_AUDIO_CHANNEL_MONO, /**< mono stream */ SPA_AUDIO_CHANNEL_MONO, /**< mono stream */
SPA_AUDIO_CHANNEL_FL, /**< front left */ /** Azimuth Elevation */
SPA_AUDIO_CHANNEL_FR, /**< front right */ SPA_AUDIO_CHANNEL_FL, /**< front left 30 0 */
SPA_AUDIO_CHANNEL_FC, /**< front center */ SPA_AUDIO_CHANNEL_FR, /**< front right -30 0 */
SPA_AUDIO_CHANNEL_LFE, /**< LFE */ SPA_AUDIO_CHANNEL_FC, /**< front center 0 0 */
SPA_AUDIO_CHANNEL_SL, /**< side left */ SPA_AUDIO_CHANNEL_LFE, /**< LFE 0 -30 */
SPA_AUDIO_CHANNEL_SR, /**< side right */ SPA_AUDIO_CHANNEL_SL, /**< side left 90 0 */
SPA_AUDIO_CHANNEL_FLC, /**< front left center */ SPA_AUDIO_CHANNEL_SR, /**< side right -90 0 */
SPA_AUDIO_CHANNEL_FRC, /**< front right center */ SPA_AUDIO_CHANNEL_FLC, /**< front left center 22.5 0 */
SPA_AUDIO_CHANNEL_RC, /**< rear center */ SPA_AUDIO_CHANNEL_FRC, /**< front right center -22.5 0 */
SPA_AUDIO_CHANNEL_RL, /**< rear left */ SPA_AUDIO_CHANNEL_RC, /**< rear center 180 0 */
SPA_AUDIO_CHANNEL_RR, /**< rear right */ SPA_AUDIO_CHANNEL_RL, /**< rear left 110 0 */
SPA_AUDIO_CHANNEL_TC, /**< top center */ SPA_AUDIO_CHANNEL_RR, /**< rear right -110 0 */
SPA_AUDIO_CHANNEL_TFL, /**< top front left */ SPA_AUDIO_CHANNEL_TC, /**< top center 0 90 */
SPA_AUDIO_CHANNEL_TFC, /**< top front center */ SPA_AUDIO_CHANNEL_TFL, /**< top front left 30 30 */
SPA_AUDIO_CHANNEL_TFR, /**< top front right */ SPA_AUDIO_CHANNEL_TFC, /**< top front center 0 30 */
SPA_AUDIO_CHANNEL_TRL, /**< top rear left */ SPA_AUDIO_CHANNEL_TFR, /**< top front right -30 30 */
SPA_AUDIO_CHANNEL_TRC, /**< top rear center */ SPA_AUDIO_CHANNEL_TRL, /**< top rear left 110 30 */
SPA_AUDIO_CHANNEL_TRR, /**< top rear right */ SPA_AUDIO_CHANNEL_TRC, /**< top rear center 180 30 */
SPA_AUDIO_CHANNEL_RLC, /**< rear left center */ SPA_AUDIO_CHANNEL_TRR, /**< top rear right -110 30 */
SPA_AUDIO_CHANNEL_RRC, /**< rear right center */ SPA_AUDIO_CHANNEL_RLC, /**< rear left center 135 0 */
SPA_AUDIO_CHANNEL_FLW, /**< front left wide */ SPA_AUDIO_CHANNEL_RRC, /**< rear right center -135 0 */
SPA_AUDIO_CHANNEL_FRW, /**< front right wide */ SPA_AUDIO_CHANNEL_FLW, /**< front left wide 60 0 */
SPA_AUDIO_CHANNEL_LFE2, /**< LFE 2 */ SPA_AUDIO_CHANNEL_FRW, /**< front right wide -60 0 */
SPA_AUDIO_CHANNEL_FLH, /**< front left high */ SPA_AUDIO_CHANNEL_LFE2, /**< LFE 2 0 -30 */
SPA_AUDIO_CHANNEL_FCH, /**< front center high */ SPA_AUDIO_CHANNEL_FLH, /**< front left high 22.5 30 */
SPA_AUDIO_CHANNEL_FRH, /**< front right high */ SPA_AUDIO_CHANNEL_FCH, /**< front center high 0 30 */
SPA_AUDIO_CHANNEL_TFLC, /**< top front left center */ SPA_AUDIO_CHANNEL_FRH, /**< front right high -22.5 30 */
SPA_AUDIO_CHANNEL_TFRC, /**< top front right center */ SPA_AUDIO_CHANNEL_TFLC, /**< top front left center 45 30 */
SPA_AUDIO_CHANNEL_TSL, /**< top side left */ SPA_AUDIO_CHANNEL_TFRC, /**< top front right center -45 30 */
SPA_AUDIO_CHANNEL_TSR, /**< top side right */ SPA_AUDIO_CHANNEL_TSL, /**< top side left 90 0 */
SPA_AUDIO_CHANNEL_LLFE, /**< left LFE */ SPA_AUDIO_CHANNEL_TSR, /**< top side right -90 0 */
SPA_AUDIO_CHANNEL_RLFE, /**< right LFE */ SPA_AUDIO_CHANNEL_LLFE, /**< left LFE 45 -30 */
SPA_AUDIO_CHANNEL_BC, /**< bottom center */ SPA_AUDIO_CHANNEL_RLFE, /**< right LFE -45 -30 */
SPA_AUDIO_CHANNEL_BLC, /**< bottom left center */ SPA_AUDIO_CHANNEL_BC, /**< bottom center 0 -30 */
SPA_AUDIO_CHANNEL_BRC, /**< bottom right center */ SPA_AUDIO_CHANNEL_BLC, /**< bottom left center 45 -30 */
SPA_AUDIO_CHANNEL_BRC, /**< bottom right center -45 -30 */
SPA_AUDIO_CHANNEL_START_Aux = 0x1000, /**< aux channels */ SPA_AUDIO_CHANNEL_START_Aux = 0x1000, /**< aux channels */
SPA_AUDIO_CHANNEL_AUX0 = SPA_AUDIO_CHANNEL_START_Aux, SPA_AUDIO_CHANNEL_AUX0 = SPA_AUDIO_CHANNEL_START_Aux,

View file

@ -0,0 +1,38 @@
/* Simple Plugin API */
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#ifndef SPA_PARAM_DICT_TYPES_H
#define SPA_PARAM_DICT_TYPES_H
#include <spa/utils/enum-types.h>
#include <spa/param/param-types.h>
#include <spa/param/dict.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \addtogroup spa_param
* \{
*/
#define SPA_TYPE_INFO_PARAM_Dict SPA_TYPE_INFO_PARAM_BASE "Dict"
#define SPA_TYPE_INFO_PARAM_DICT_BASE SPA_TYPE_INFO_PARAM_Dict ":"
static const struct spa_type_info spa_type_param_dict[] = {
{ SPA_PARAM_DICT_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_DICT_BASE, spa_type_param, },
{ SPA_PARAM_DICT_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_DICT_BASE "info", NULL, },
{ 0, 0, NULL, NULL },
};
/**
* \}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SPA_PARAM_DICT_TYPES_H */

View file

@ -0,0 +1,125 @@
/* Simple Plugin API */
/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#ifndef SPA_PARAM_DICT_UTILS_H
#define SPA_PARAM_DICT_UTILS_H
#include <float.h>
#include <spa/utils/dict.h>
#include <spa/pod/builder.h>
#include <spa/pod/iter.h>
#include <spa/pod/parser.h>
#include <spa/pod/compare.h>
#include <spa/param/dict.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \addtogroup spa_param
* \{
*/
#ifndef SPA_API_DICT_UTILS
#ifdef SPA_API_IMPL
#define SPA_API_DICT_UTILS SPA_API_IMPL
#else
#define SPA_API_DICT_UTILS static inline
#endif
#endif
SPA_API_DICT_UTILS int
spa_param_dict_compare(const struct spa_pod *a, const struct spa_pod *b)
{
return spa_pod_memcmp(a, b);
}
SPA_API_DICT_UTILS struct spa_pod *
spa_param_dict_build_dict(struct spa_pod_builder *builder, uint32_t id, struct spa_dict *dict)
{
struct spa_pod_frame f[2];
uint32_t i, n_items;
spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_ParamDict, id);
n_items = dict ? dict->n_items : 0;
spa_pod_builder_prop(builder, SPA_PARAM_DICT_info, SPA_POD_PROP_FLAG_HINT_DICT);
spa_pod_builder_push_struct(builder, &f[1]);
spa_pod_builder_int(builder, n_items);
for (i = 0; i < n_items; i++) {
spa_pod_builder_string(builder, dict->items[i].key);
spa_pod_builder_string(builder, dict->items[i].value);
}
spa_pod_builder_pop(builder, &f[1]);
return (struct spa_pod*)spa_pod_builder_pop(builder, &f[0]);
}
SPA_API_DICT_UTILS struct spa_pod *
spa_param_dict_build_info(struct spa_pod_builder *builder, uint32_t id, struct spa_param_dict_info *info)
{
struct spa_pod_frame f;
spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_ParamDict, id);
spa_pod_builder_add(builder, SPA_PARAM_DICT_info, SPA_POD_PROP_FLAG_HINT_DICT);
spa_pod_builder_primitive(builder, info->info);
return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
}
SPA_API_DICT_UTILS int
spa_param_dict_parse(const struct spa_pod *dict, struct spa_param_dict_info *info, size_t size)
{
memset(info, 0, size);
return spa_pod_parse_object(dict,
SPA_TYPE_OBJECT_ParamDict, NULL,
SPA_PARAM_DICT_info, SPA_POD_PodStruct(&info->info));
}
SPA_API_DICT_UTILS int
spa_param_dict_info_parse(const struct spa_param_dict_info *info, size_t size,
struct spa_dict *dict, struct spa_dict_item *items)
{
struct spa_pod_parser prs;
uint32_t n, n_items;
const char *key, *value;
struct spa_pod_frame f[1];
spa_pod_parser_pod(&prs, info->info);
if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 ||
spa_pod_parser_get_int(&prs, (int32_t*)&n_items) < 0)
return -EINVAL;
if (items == NULL) {
dict->n_items = n_items;
return 0;
}
n_items = SPA_MIN(dict->n_items, n_items);
for (n = 0; n < n_items; n++) {
if (spa_pod_parser_get(&prs,
SPA_POD_String(&key),
SPA_POD_String(&value),
NULL) < 0)
break;
if (key == NULL || value == NULL)
return -EINVAL;
items[n].key = key;
items[n].value = value;
}
dict->items = items;
spa_pod_parser_pop(&prs, &f[0]);
return 0;
}
/**
* \}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SPA_PARAM_DICT_UTILS_H */

View file

@ -0,0 +1,42 @@
/* Simple Plugin API */
/* SPDX-FileCopyrightText: Copyright © 2025 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#ifndef SPA_PARAM_DICT_H
#define SPA_PARAM_DICT_H
#include <spa/param/param.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \addtogroup spa_param
* \{
*/
/** properties for SPA_TYPE_OBJECT_ParamDict */
enum spa_param_dict {
SPA_PARAM_DICT_START,
SPA_PARAM_DICT_info, /**< Struct(
* Int: n_items
* (String: key
* String: value)*
* ) */
};
/** helper structure for managing info objects */
struct spa_param_dict_info {
const struct spa_pod *info;
};
/**
* \}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SPA_PARAM_DICT_H */

View file

@ -181,6 +181,8 @@ static const struct spa_type_info spa_type_format[] = {
{ SPA_FORMAT_VIDEO_H264_alignment, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_H264_BASE "alignment", NULL }, { SPA_FORMAT_VIDEO_H264_alignment, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_H264_BASE "alignment", NULL },
{ SPA_FORMAT_CONTROL_types, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_CONTROL_BASE "types", spa_type_control }, { SPA_FORMAT_CONTROL_types, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_CONTROL_BASE "types", spa_type_control },
{ SPA_FORMAT_VIDEO_deviceId, SPA_TYPE_Bytes, SPA_TYPE_INFO_FORMAT_BASE "deviceId", NULL },
{ 0, 0, NULL, NULL }, { 0, 0, NULL, NULL },
}; };

View file

@ -144,6 +144,7 @@ enum spa_format {
SPA_FORMAT_VIDEO_H264_alignment, /**< (Id enum spa_h264_alignment) */ SPA_FORMAT_VIDEO_H264_alignment, /**< (Id enum spa_h264_alignment) */
SPA_FORMAT_VIDEO_H265_streamFormat, /**< (Id enum spa_h265_stream_format) */ SPA_FORMAT_VIDEO_H265_streamFormat, /**< (Id enum spa_h265_stream_format) */
SPA_FORMAT_VIDEO_H265_alignment, /**< (Id enum spa_h265_alignment) */ SPA_FORMAT_VIDEO_H265_alignment, /**< (Id enum spa_h265_alignment) */
SPA_FORMAT_VIDEO_deviceId, /**< dev_t identifier (Bytes) */
/* Image Format keys */ /* Image Format keys */
SPA_FORMAT_START_Image = 0x30000, SPA_FORMAT_START_Image = 0x30000,

View file

@ -41,7 +41,9 @@ static const struct spa_type_info spa_type_param[] = {
{ SPA_PARAM_Latency, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_INFO_PARAM_ID_BASE "Latency", NULL }, { SPA_PARAM_Latency, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_INFO_PARAM_ID_BASE "Latency", NULL },
{ SPA_PARAM_ProcessLatency, SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_INFO_PARAM_ID_BASE "ProcessLatency", NULL }, { SPA_PARAM_ProcessLatency, SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_INFO_PARAM_ID_BASE "ProcessLatency", NULL },
{ SPA_PARAM_Tag, SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_INFO_PARAM_ID_BASE "Tag", NULL }, { SPA_PARAM_Tag, SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_INFO_PARAM_ID_BASE "Tag", NULL },
{ SPA_PARAM_PeerFormats, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_ID_BASE "PeerFormats", NULL }, { SPA_PARAM_PeerEnumFormat, SPA_TYPE_OBJECT_PeerParam, SPA_TYPE_INFO_PARAM_ID_BASE "PeerEnumFormat", NULL },
{ SPA_PARAM_Capability, SPA_TYPE_OBJECT_ParamDict, SPA_TYPE_INFO_PARAM_ID_BASE "Capability", NULL },
{ SPA_PARAM_PeerCapability, SPA_TYPE_OBJECT_PeerParam, SPA_TYPE_INFO_PARAM_ID_BASE "PeerCapability", NULL },
{ 0, 0, NULL, NULL }, { 0, 0, NULL, NULL },
}; };

View file

@ -40,7 +40,11 @@ enum spa_param_type {
SPA_PARAM_Latency, /**< latency reporting, a SPA_TYPE_OBJECT_ParamLatency */ SPA_PARAM_Latency, /**< latency reporting, a SPA_TYPE_OBJECT_ParamLatency */
SPA_PARAM_ProcessLatency, /**< processing latency, a SPA_TYPE_OBJECT_ParamProcessLatency */ SPA_PARAM_ProcessLatency, /**< processing latency, a SPA_TYPE_OBJECT_ParamProcessLatency */
SPA_PARAM_Tag, /**< tag reporting, a SPA_TYPE_OBJECT_ParamTag. Since 0.3.79 */ SPA_PARAM_Tag, /**< tag reporting, a SPA_TYPE_OBJECT_ParamTag. Since 0.3.79 */
SPA_PARAM_PeerFormats, /**< peer formats, a SPA_TYPE_Struct of SPA_TYPE_OBJECT_Format. Since 1.5.0 */ SPA_PARAM_PeerEnumFormat, /**< peer formats, a SPA_TYPE_OBJECT_PeerParam with
* SPA_TYPE_OBJECT_Format. Since 1.5.0 */
SPA_PARAM_Capability, /**< capability info, a SPA_TYPE_OBJECT_ParamDict, Since 1.5.84 */
SPA_PARAM_PeerCapability, /**< peer capabilities, a SPA_TYPE_OBJECT_PeerParam with
* SPA_TYPE_OBJECT_ParamDict, since 1.5.84 */
}; };
/** information about a parameter */ /** information about a parameter */

View file

@ -0,0 +1,38 @@
/* Simple Plugin API */
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#ifndef SPA_PARAM_PEER_TYPES_H
#define SPA_PARAM_PEER_TYPES_H
#include <spa/utils/enum-types.h>
#include <spa/param/param-types.h>
#include <spa/param/peer.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \addtogroup spa_param
* \{
*/
#define SPA_TYPE_INFO_PeerParam SPA_TYPE_INFO_OBJECT_BASE "PeerParam"
#define SPA_TYPE_INFO_PEER_PARAM_BASE SPA_TYPE_INFO_PeerParam ":"
static const struct spa_type_info spa_type_peer_param[] = {
{ SPA_PEER_PARAM_START, SPA_TYPE_Id, SPA_TYPE_INFO_PEER_PARAM_BASE, spa_type_param, },
{ SPA_ID_INVALID, SPA_TYPE_Id, SPA_TYPE_INFO_PEER_PARAM_BASE, NULL, },
{ 0, 0, NULL, NULL },
};
/**
* \}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SPA_PARAM_PEER_TYPES_H */

View file

@ -0,0 +1,92 @@
/* Simple Plugin API */
/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#ifndef SPA_PARAM_PEER_PARAM_UTILS_H
#define SPA_PARAM_PEER_PARAM_UTILS_H
#include <float.h>
#include <spa/utils/dict.h>
#include <spa/pod/builder.h>
#include <spa/pod/iter.h>
#include <spa/pod/parser.h>
#include <spa/pod/compare.h>
#include <spa/param/peer.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \addtogroup spa_param
* \{
*/
#ifndef SPA_API_PEER_PARAM_UTILS
#ifdef SPA_API_IMPL
#define SPA_API_PEER_PARAM_UTILS SPA_API_IMPL
#else
#define SPA_API_PEER_PARAM_UTILS static inline
#endif
#endif
SPA_API_PEER_PARAM_UTILS int
spa_peer_param_parse(const struct spa_pod *param, struct spa_peer_param_info *info,
size_t size, void **state)
{
int res;
const struct spa_pod_object *obj = (const struct spa_pod_object*)param;
const struct spa_pod_prop *first, *start, *cur;
if ((res = spa_pod_parse_object(param,
SPA_TYPE_OBJECT_PeerParam, NULL)) < 0)
return res;
first = spa_pod_prop_first(&obj->body);
start = *state ? spa_pod_prop_next((struct spa_pod_prop*)*state) : first;
res = 0;
for (cur = start; spa_pod_prop_is_inside(&obj->body, obj->pod.size, cur);
cur = spa_pod_prop_next(cur)) {
info->peer_id = cur->key;
info->param = &cur->value;
*state = (void*)cur;
return 1;
}
return 0;
}
SPA_API_PEER_PARAM_UTILS void
spa_peer_param_build_start(struct spa_pod_builder *builder, struct spa_pod_frame *f, uint32_t id)
{
spa_pod_builder_push_object(builder, f, SPA_TYPE_OBJECT_PeerParam, id);
}
SPA_API_PEER_PARAM_UTILS void
spa_peer_param_build_add_param(struct spa_pod_builder *builder, uint32_t peer_id,
const struct spa_pod *param)
{
spa_pod_builder_prop(builder, peer_id, 0);
if (param)
spa_pod_builder_primitive(builder, param);
else
spa_pod_builder_none(builder);
}
SPA_API_PEER_PARAM_UTILS struct spa_pod *
spa_peer_param_build_end(struct spa_pod_builder *builder, struct spa_pod_frame *f)
{
return (struct spa_pod*)spa_pod_builder_pop(builder, f);
}
/**
* \}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SPA_PARAM_PEER_PARAM_UTILS_H */

View file

@ -0,0 +1,37 @@
/* Simple Plugin API */
/* SPDX-FileCopyrightText: Copyright © 2025 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#ifndef SPA_PARAM_PEER_PARAM_H
#define SPA_PARAM_PEER_PARAM_H
#include <spa/param/param.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \addtogroup spa_param
* \{
*/
/** properties for SPA_TYPE_OBJECT_PeerParam */
enum spa_peer_param {
SPA_PEER_PARAM_START, /**< id of peer as key, SPA_TYPE_Pod as value */
SPA_PEER_PARAM_END = 0xfffffffe,
};
struct spa_peer_param_info {
uint32_t peer_id;
const struct spa_pod *param;
};
/**
* \}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SPA_PARAM_PEER_PARAM_H */

View file

@ -11,6 +11,7 @@
#include <spa/pod/builder.h> #include <spa/pod/builder.h>
#include <spa/pod/iter.h> #include <spa/pod/iter.h>
#include <spa/pod/parser.h> #include <spa/pod/parser.h>
#include <spa/pod/compare.h>
#include <spa/param/tag.h> #include <spa/param/tag.h>
#ifdef __cplusplus #ifdef __cplusplus
@ -33,8 +34,7 @@ extern "C" {
SPA_API_TAG_UTILS int SPA_API_TAG_UTILS int
spa_tag_compare(const struct spa_pod *a, const struct spa_pod *b) spa_tag_compare(const struct spa_pod *a, const struct spa_pod *b)
{ {
return ((a == b) || (a && b && SPA_POD_SIZE(a) == SPA_POD_SIZE(b) && return spa_pod_memcmp(a, b);
memcmp(a, b, SPA_POD_SIZE(b)) == 0)) ? 0 : 1;
} }
SPA_API_TAG_UTILS int SPA_API_TAG_UTILS int

View file

@ -15,5 +15,7 @@
#include <spa/param/profile-types.h> #include <spa/param/profile-types.h>
#include <spa/param/route-types.h> #include <spa/param/route-types.h>
#include <spa/param/tag-types.h> #include <spa/param/tag-types.h>
#include <spa/param/dict-types.h>
#include <spa/param/peer-types.h>
#endif /* SPA_PARAM_TYPE_INFO_H */ #endif /* SPA_PARAM_TYPE_INFO_H */

View file

@ -111,8 +111,15 @@ SPA_API_POD_BODY int spa_pod_choice_n_values(uint32_t choice_type, uint32_t *min
SPA_API_POD_BODY int spa_pod_body_from_data(void *data, size_t maxsize, off_t offset, size_t size, SPA_API_POD_BODY int spa_pod_body_from_data(void *data, size_t maxsize, off_t offset, size_t size,
struct spa_pod *pod, const void **body) struct spa_pod *pod, const void **body)
{ {
if (offset < 0 || offset > (int64_t)UINT32_MAX) if (offset < 0)
return -EINVAL; return -EINVAL;
/* On 32-bit platforms, off_t is a signed 32-bit type (since it tracks pointer
* width), and consequently can never exceed UINT32_MAX. Skip the upper-bound
* check on 32-bit platforms to avoid a compiler warning. */
#if __SIZEOF_POINTER__ > 4
if (offset > (int64_t)UINT32_MAX)
return -EINVAL;
#endif
if (size < sizeof(struct spa_pod) || if (size < sizeof(struct spa_pod) ||
size > maxsize || size > maxsize ||
maxsize - size < (uint32_t)offset) maxsize - size < (uint32_t)offset)

View file

@ -80,6 +80,13 @@ SPA_API_POD_COMPARE int spa_pod_compare_value(uint32_t type, const void *r1, con
return 0; return 0;
} }
SPA_API_POD_COMPARE int spa_pod_memcmp(const struct spa_pod *a,
const struct spa_pod *b)
{
return ((a == b) || (a && b && SPA_POD_SIZE(a) == SPA_POD_SIZE(b) &&
memcmp(a, b, SPA_POD_SIZE(b)) == 0)) ? 0 : 1;
}
SPA_API_POD_COMPARE int spa_pod_compare(const struct spa_pod *pod1, SPA_API_POD_COMPARE int spa_pod_compare(const struct spa_pod *pod1,
const struct spa_pod *pod2) const struct spa_pod *pod2)
{ {
@ -149,12 +156,8 @@ SPA_API_POD_COMPARE int spa_pod_compare(const struct spa_pod *pod1,
break; break;
} }
case SPA_TYPE_Array: case SPA_TYPE_Array:
{ res = spa_pod_memcmp(pod1, pod2);
if (pod1->size != pod2->size)
return -EINVAL;
res = memcmp(SPA_POD_BODY(pod1), SPA_POD_BODY(pod2), pod2->size);
break; break;
}
default: default:
if (pod1->size != pod2->size) if (pod1->size != pod2->size)
return -EINVAL; return -EINVAL;

View file

@ -70,8 +70,10 @@ SPA_API_POD_DYNAMIC void spa_pod_dynamic_builder_continue(struct spa_pod_dynamic
SPA_API_POD_DYNAMIC void spa_pod_dynamic_builder_clean(struct spa_pod_dynamic_builder *builder) SPA_API_POD_DYNAMIC void spa_pod_dynamic_builder_clean(struct spa_pod_dynamic_builder *builder)
{ {
if (builder->data != builder->b.data) if (builder->data != builder->b.data) {
free(builder->b.data); free(builder->b.data);
builder->b.data = NULL;
}
} }
SPA_DEFINE_AUTO_CLEANUP(spa_pod_dynamic_builder, struct spa_pod_dynamic_builder, { SPA_DEFINE_AUTO_CLEANUP(spa_pod_dynamic_builder, struct spa_pod_dynamic_builder, {

View file

@ -339,9 +339,7 @@ SPA_API_POD_FILTER int spa_pod_filter_part(struct spa_pod_builder *b,
default: default:
if (pf != NULL) { if (pf != NULL) {
if (pp->size != pf->size) if (spa_pod_memcmp(pp, pf) != 0)
return -EINVAL;
if (memcmp(pp, pf, pp->size) != 0)
return -EINVAL; return -EINVAL;
do_advance = true; do_advance = true;
} }

View file

@ -62,6 +62,7 @@ struct spa_cpu { struct spa_interface iface; };
#define SPA_CPU_FLAG_BMI2 (1<<18) /**< Bit Manipulation Instruction Set 2 */ #define SPA_CPU_FLAG_BMI2 (1<<18) /**< Bit Manipulation Instruction Set 2 */
#define SPA_CPU_FLAG_AVX512 (1<<19) /**< AVX-512 */ #define SPA_CPU_FLAG_AVX512 (1<<19) /**< AVX-512 */
#define SPA_CPU_FLAG_SLOW_UNALIGNED (1<<20) /**< unaligned loads/stores are slow */ #define SPA_CPU_FLAG_SLOW_UNALIGNED (1<<20) /**< unaligned loads/stores are slow */
#define SPA_CPU_FLAG_SLOW_GATHER (1<<21) /**< gather functions are slow */
/* PPC specific */ /* PPC specific */
#define SPA_CPU_FLAG_ALTIVEC (1<<0) /**< standard */ #define SPA_CPU_FLAG_ALTIVEC (1<<0) /**< standard */

View file

@ -41,7 +41,7 @@ struct itimerspec;
#define SPA_TYPE_INTERFACE_System SPA_TYPE_INFO_INTERFACE_BASE "System" #define SPA_TYPE_INTERFACE_System SPA_TYPE_INFO_INTERFACE_BASE "System"
#define SPA_TYPE_INTERFACE_DataSystem SPA_TYPE_INFO_INTERFACE_BASE "DataSystem" #define SPA_TYPE_INTERFACE_DataSystem SPA_TYPE_INFO_INTERFACE_BASE "DataSystem"
#define SPA_VERSION_SYSTEM 0 #define SPA_VERSION_SYSTEM 1
struct spa_system { struct spa_interface iface; }; struct spa_system { struct spa_interface iface; };
/* IO events */ /* IO events */
@ -59,11 +59,18 @@ struct spa_system { struct spa_interface iface; };
struct spa_poll_event { struct spa_poll_event {
uint32_t events; uint32_t events;
void *data; union {
void *data;
uint64_t data_u64;
};
#ifdef __x86_64__
} __attribute__((packed));
#else
}; };
#endif
struct spa_system_methods { struct spa_system_methods {
#define SPA_VERSION_SYSTEM_METHODS 0 #define SPA_VERSION_SYSTEM_METHODS 1
uint32_t version; uint32_t version;
/* read/write/ioctl */ /* read/write/ioctl */
@ -151,7 +158,7 @@ SPA_API_SYSTEM int spa_system_pollfd_del(struct spa_system *object, int pfd, int
SPA_API_SYSTEM int spa_system_pollfd_wait(struct spa_system *object, int pfd, SPA_API_SYSTEM int spa_system_pollfd_wait(struct spa_system *object, int pfd,
struct spa_poll_event *ev, int n_ev, int timeout) struct spa_poll_event *ev, int n_ev, int timeout)
{ {
return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_wait, 0, pfd, ev, n_ev, timeout); return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_wait, 1, pfd, ev, n_ev, timeout);
} }
SPA_API_SYSTEM int spa_system_timerfd_create(struct spa_system *object, int clockid, int flags) SPA_API_SYSTEM int spa_system_timerfd_create(struct spa_system *object, int clockid, int flags)

View file

@ -386,6 +386,16 @@ struct spa_error_location {
} \ } \
} while(false) } while(false)
#define spa_goto_if_fail(expr, label) \
do { \
if (SPA_UNLIKELY(!(expr))) { \
fprintf(stderr, "'%s' failed at %s:%u %s()\n", \
#expr , __FILE__, __LINE__, __func__); \
goto label; \
} \
} while(false)
/* spa_assert_se() is an assert which guarantees side effects of x, /* spa_assert_se() is an assert which guarantees side effects of x,
* i.e. is never optimized away, regardless of NDEBUG or FASTPATH. */ * i.e. is never optimized away, regardless of NDEBUG or FASTPATH. */
#ifndef __COVERITY__ #ifndef __COVERITY__
@ -457,6 +467,16 @@ struct spa_error_location {
_strp; \ _strp; \
}) })
#define spa_alloca(n, size, max_size) \
({ \
void *_res = NULL; \
if ((size_t)n > (size_t)max_size / (size_t)size) \
errno = ENOMEM; \
else \
_res = alloca((size_t)n * (size_t)size); \
_res; \
})
/** /**
* \} * \}
*/ */

View file

@ -5,7 +5,7 @@
#ifndef SPA_ENDIAN_H #ifndef SPA_ENDIAN_H
#define SPA_ENDIAN_H #define SPA_ENDIAN_H
#if defined(__FreeBSD__) || defined(__MidnightBSD__) #if defined(__MidnightBSD__)
#include <sys/endian.h> #include <sys/endian.h>
#define bswap_16 bswap16 #define bswap_16 bswap16
#define bswap_32 bswap32 #define bswap_32 bswap32

View file

@ -89,10 +89,10 @@ extern "C" {
* \code{.c} * \code{.c}
* *
* static void bar_stdout(struct foo *f, const char *msg) { * static void bar_stdout(struct foo *f, const char *msg) {
* printf(msg); * printf("%s", msg);
* } * }
* static void bar_stderr(struct foo *f, const char *msg) { * static void bar_stderr(struct foo *f, const char *msg) {
* fprintf(stderr, msg); * fprintf(stderr, "%s", msg);
* } * }
* *
* struct foo* get_foo_from_somewhere() { * struct foo* get_foo_from_somewhere() {

View file

@ -0,0 +1,445 @@
/* Simple Plugin API */
/* SPDX-FileCopyrightText: Copyright © 2026 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#ifndef SPA_UTILS_JSON_BUILDER_H
#define SPA_UTILS_JSON_BUILDER_H
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <inttypes.h>
#include <limits.h>
#include <spa/utils/defs.h>
#include <spa/utils/ansi.h>
#include <spa/utils/json.h>
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
#ifndef SPA_API_JSON_BUILDER
#ifdef SPA_API_IMPL
#define SPA_API_JSON_BUILDER SPA_API_IMPL
#else
#define SPA_API_JSON_BUILDER static inline
#endif
#endif
/** \defgroup spa_json_builder JSON builder
* JSON builder functions
*/
/**
* \addtogroup spa_json_builder
* \{
*/
struct spa_json_builder {
FILE *f;
#define SPA_JSON_BUILDER_FLAG_CLOSE (1<<0)
#define SPA_JSON_BUILDER_FLAG_INDENT (1<<1)
#define SPA_JSON_BUILDER_FLAG_SPACE (1<<2)
#define SPA_JSON_BUILDER_FLAG_PRETTY (SPA_JSON_BUILDER_FLAG_INDENT|SPA_JSON_BUILDER_FLAG_SPACE)
#define SPA_JSON_BUILDER_FLAG_COLOR (1<<3)
#define SPA_JSON_BUILDER_FLAG_SIMPLE (1<<4)
#define SPA_JSON_BUILDER_FLAG_RAW (1<<5)
uint32_t flags;
uint32_t indent_off;
uint32_t level;
uint32_t indent;
uint32_t count;
const char *delim;
const char *comma;
const char *key_sep;
#define SPA_JSON_BUILDER_COLOR_NORMAL 0
#define SPA_JSON_BUILDER_COLOR_KEY 1
#define SPA_JSON_BUILDER_COLOR_LITERAL 2
#define SPA_JSON_BUILDER_COLOR_NUMBER 3
#define SPA_JSON_BUILDER_COLOR_STRING 4
#define SPA_JSON_BUILDER_COLOR_CONTAINER 5
const char *color[8];
};
SPA_API_JSON_BUILDER int spa_json_builder_file(struct spa_json_builder *b, FILE *f, uint32_t flags)
{
bool color = flags & SPA_JSON_BUILDER_FLAG_COLOR;
bool simple = flags & SPA_JSON_BUILDER_FLAG_SIMPLE;
bool space = flags & SPA_JSON_BUILDER_FLAG_SPACE;
spa_zero(*b);
b->f = f;
b->flags = flags;
b->indent = 2;
b->delim = "";
b->comma = simple ? space ? "" : " " : ",";
b->key_sep = simple ? space ? " =" : "=" : ":";
b->color[0] = (color ? SPA_ANSI_RESET : "");
b->color[1] = (color ? SPA_ANSI_BRIGHT_BLUE : "");
b->color[2] = (color ? SPA_ANSI_BRIGHT_MAGENTA : "");
b->color[3] = (color ? SPA_ANSI_BRIGHT_CYAN : "");
b->color[4] = (color ? SPA_ANSI_BRIGHT_GREEN : "");
b->color[5] = (color ? SPA_ANSI_BRIGHT_YELLOW : "");
return 0;
}
SPA_API_JSON_BUILDER int spa_json_builder_memstream(struct spa_json_builder *b,
char **mem, size_t *size, uint32_t flags)
{
FILE *f;
spa_zero(*b);
if ((f = open_memstream(mem, size)) == NULL)
return -errno;
return spa_json_builder_file(b, f, flags | SPA_JSON_BUILDER_FLAG_CLOSE);
}
SPA_API_JSON_BUILDER int spa_json_builder_membuf(struct spa_json_builder *b,
char *mem, size_t size, uint32_t flags)
{
FILE *f;
spa_zero(*b);
if ((f = fmemopen(mem, size, "w")) == NULL)
return -errno;
return spa_json_builder_file(b, f, flags | SPA_JSON_BUILDER_FLAG_CLOSE);
}
SPA_API_JSON_BUILDER void spa_json_builder_close(struct spa_json_builder *b)
{
if (b->flags & SPA_JSON_BUILDER_FLAG_CLOSE)
fclose(b->f);
}
SPA_API_JSON_BUILDER int spa_json_builder_encode_string(struct spa_json_builder *b,
bool raw, const char *before, const char *val, int size, const char *after)
{
FILE *f = b->f;
int i, len;
if (raw) {
len = fprintf(f, "%s%.*s%s", before, size, val, after) - 1;
} else {
len = fprintf(f, "%s\"", before);
for (i = 0; i < size && val[i]; i++) {
char v = val[i];
switch (v) {
case '\n': len += fprintf(f, "\\n"); break;
case '\r': len += fprintf(f, "\\r"); break;
case '\b': len += fprintf(f, "\\b"); break;
case '\t': len += fprintf(f, "\\t"); break;
case '\f': len += fprintf(f, "\\f"); break;
case '\\':
case '"': len += fprintf(f, "\\%c", v); break;
default:
if (v > 0 && v < 0x20)
len += fprintf(f, "\\u%04x", v);
else
len += fprintf(f, "%c", v);
break;
}
}
len += fprintf(f, "\"%s", after);
}
return len-1;
}
SPA_API_JSON_BUILDER
void spa_json_builder_add_simple(struct spa_json_builder *b, const char *key, int key_len,
char type, const char *val, int val_len)
{
bool indent = b->indent_off == 0 && (b->flags & SPA_JSON_BUILDER_FLAG_INDENT);
bool space = b->flags & SPA_JSON_BUILDER_FLAG_SPACE;
bool force_raw = b->flags & SPA_JSON_BUILDER_FLAG_RAW;
bool raw = true, simple = b->flags & SPA_JSON_BUILDER_FLAG_SIMPLE;
int color;
if (val == NULL || val_len == 0) {
val = "null";
val_len = 4;
type = 'l';
}
if (type == 0) {
if (spa_json_is_container(val, val_len))
type = simple ? 'C' : 'S';
else if (val_len > 0 && (*val == '}' || *val == ']'))
type = 'e';
else if (spa_json_is_null(val, val_len) ||
spa_json_is_bool(val, val_len))
type = 'l';
else if (spa_json_is_string(val, val_len))
type = 's';
else if (spa_json_is_json_number(val, val_len))
type = 'd';
else if (simple && (spa_json_is_float(val, val_len) ||
spa_json_is_int(val, val_len)))
type = 'd';
else
type = 'S';
}
switch (type) {
case 'e':
b->level -= b->indent;
b->delim = "";
break;
}
fprintf(b->f, "%s%s%*s", b->delim, b->count == 0 ? "" : indent ? "\n" : space ? " " : "",
indent ? b->level : 0, "");
if (key) {
bool key_raw = force_raw || (simple && spa_json_make_simple_string(&key, &key_len)) ||
spa_json_is_string(key, key_len);
spa_json_builder_encode_string(b, key_raw,
b->color[1], key, key_len, b->color[0]);
fprintf(b->f, "%s%s", b->key_sep, space ? " " : "");
}
b->delim = b->comma;
switch (type) {
case 'c':
color = SPA_JSON_BUILDER_COLOR_NORMAL;
val_len = 1;
b->delim = "";
b->level += b->indent;
if (val[1] == '-') b->indent_off++;
break;
case 'e':
color = SPA_JSON_BUILDER_COLOR_NORMAL;
val_len = 1;
if (val[1] == '-') b->indent_off--;
break;
case 'l':
color = SPA_JSON_BUILDER_COLOR_LITERAL;
break;
case 'd':
color = SPA_JSON_BUILDER_COLOR_NUMBER;
break;
case 's':
color = SPA_JSON_BUILDER_COLOR_STRING;
break;
case 'C':
color = SPA_JSON_BUILDER_COLOR_CONTAINER;
break;
default:
color = SPA_JSON_BUILDER_COLOR_STRING;
raw = force_raw || (simple && spa_json_make_simple_string(&val, &val_len));
break;
}
spa_json_builder_encode_string(b, raw, b->color[color], val, val_len, b->color[0]);
b->count++;
}
SPA_API_JSON_BUILDER void spa_json_builder_object_push(struct spa_json_builder *b,
const char *key, const char *val)
{
spa_json_builder_add_simple(b, key, INT_MAX, 'c', val, INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_pop(struct spa_json_builder *b,
const char *val)
{
spa_json_builder_add_simple(b, NULL, 0, 'e', val, INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_null(struct spa_json_builder *b,
const char *key)
{
spa_json_builder_add_simple(b, key, INT_MAX, 'l', "null", 4);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_bool(struct spa_json_builder *b,
const char *key, bool val)
{
spa_json_builder_add_simple(b, key, INT_MAX, 'l', val ? "true" : "false", INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_int(struct spa_json_builder *b,
const char *key, int64_t val)
{
char str[128];
snprintf(str, sizeof(str), "%" PRIi64, val);
spa_json_builder_add_simple(b, key, INT_MAX, 'd', str, INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_uint(struct spa_json_builder *b,
const char *key, uint64_t val)
{
char str[128];
snprintf(str, sizeof(str), "%" PRIu64, val);
spa_json_builder_add_simple(b, key, INT_MAX, 'd', str, INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_double(struct spa_json_builder *b,
const char *key, double val)
{
char str[64];
spa_json_format_float(str, sizeof(str), (float)val);
spa_json_builder_add_simple(b, key, INT_MAX, 'd', str, INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_string(struct spa_json_builder *b,
const char *key, const char *val)
{
spa_json_builder_add_simple(b, key, INT_MAX, 'S', val, INT_MAX);
}
SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(3,0)
void spa_json_builder_object_stringv(struct spa_json_builder *b,
const char *key, const char *fmt, va_list va)
{
char *val;
if (vasprintf(&val, fmt, va) > 0) {
spa_json_builder_object_string(b, key, val);
free(val);
}
}
SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(3,4)
void spa_json_builder_object_stringf(struct spa_json_builder *b,
const char *key, const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
spa_json_builder_object_stringv(b, key, fmt, va);
va_end(va);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_value_iter(struct spa_json_builder *b,
struct spa_json *it, const char *key, int key_len, const char *val, int len)
{
struct spa_json sub;
if (spa_json_is_array(val, len)) {
spa_json_builder_add_simple(b, key, key_len, 'c', "[", 1);
spa_json_enter(it, &sub);
while ((len = spa_json_next(&sub, &val)) > 0)
spa_json_builder_object_value_iter(b, &sub, NULL, 0, val, len);
spa_json_builder_pop(b, "]");
}
else if (spa_json_is_object(val, len)) {
const char *k;
int kl;
spa_json_builder_add_simple(b, key, key_len, 'c', "{", 1);
spa_json_enter(it, &sub);
while ((kl = spa_json_next(&sub, &k)) > 0) {
if ((len = spa_json_next(&sub, &val)) < 0)
break;
spa_json_builder_object_value_iter(b, &sub, k, kl, val, len);
}
spa_json_builder_pop(b, "}");
}
else {
spa_json_builder_add_simple(b, key, key_len, 0, val, len);
}
}
SPA_API_JSON_BUILDER void spa_json_builder_object_value_full(struct spa_json_builder *b,
bool recurse, const char *key, int key_len, const char *val, int val_len)
{
if (!recurse || val == NULL) {
spa_json_builder_add_simple(b, key, key_len, 0, val, val_len);
} else {
struct spa_json it[1];
const char *v;
if (spa_json_begin(&it[0], val, val_len, &v) >= 0)
spa_json_builder_object_value_iter(b, &it[0], key, key_len, val, val_len);
}
}
SPA_API_JSON_BUILDER void spa_json_builder_object_value(struct spa_json_builder *b,
bool recurse, const char *key, const char *val)
{
spa_json_builder_object_value_full(b, recurse, key, key ? strlen(key) : 0,
val, val ? strlen(val) : 0);
}
SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(4,5)
void spa_json_builder_object_valuef(struct spa_json_builder *b,
bool recurse, const char *key, const char *fmt, ...)
{
va_list va;
char *val;
va_start(va, fmt);
if (vasprintf(&val, fmt, va) > 0) {
spa_json_builder_object_value(b, recurse, key, val);
free(val);
}
va_end(va);
}
/* array functions */
SPA_API_JSON_BUILDER void spa_json_builder_array_push(struct spa_json_builder *b,
const char *val)
{
spa_json_builder_object_push(b, NULL, val);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_null(struct spa_json_builder *b)
{
spa_json_builder_object_null(b, NULL);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_bool(struct spa_json_builder *b,
bool val)
{
spa_json_builder_object_bool(b, NULL, val);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_int(struct spa_json_builder *b,
int64_t val)
{
spa_json_builder_object_int(b, NULL, val);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_uint(struct spa_json_builder *b,
uint64_t val)
{
spa_json_builder_object_uint(b, NULL, val);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_double(struct spa_json_builder *b,
double val)
{
spa_json_builder_object_double(b, NULL, val);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_string(struct spa_json_builder *b,
const char *val)
{
spa_json_builder_object_string(b, NULL, val);
}
SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(2,3)
void spa_json_builder_array_stringf(struct spa_json_builder *b,
const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
spa_json_builder_object_stringv(b, NULL, fmt, va);
va_end(va);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_value(struct spa_json_builder *b,
bool recurse, const char *val)
{
spa_json_builder_object_value(b, recurse, NULL, val);
}
SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(3,4)
void spa_json_builder_array_valuef(struct spa_json_builder *b, bool recurse, const char *fmt, ...)
{
va_list va;
char *val;
va_start(va, fmt);
if (vasprintf(&val, fmt, va) > 0) {
spa_json_builder_object_value(b, recurse, NULL, val);
free(val);
}
va_end(va);
}
SPA_API_JSON_BUILDER char *spa_json_builder_reformat(const char *json, uint32_t flags)
{
struct spa_json_builder b;
char *mem;
size_t size;
int res;
if ((res = spa_json_builder_memstream(&b, &mem, &size, flags)) < 0) {
errno = -res;
return NULL;
}
spa_json_builder_array_value(&b, true, json);
spa_json_builder_close(&b);
return mem;
}
/**
* \}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SPA_UTILS_JSON_BUILDER_H */

View file

@ -232,7 +232,7 @@ SPA_API_JSON int spa_json_next(struct spa_json * iter, const char **value)
switch (cur) { switch (cur) {
case '\0': case '\0':
case '\t': case ' ': case '\r': case '\n': case '\t': case ' ': case '\r': case '\n':
case '"': case '#': case '"': case '#': case '{': case '[':
case ':': case ',': case '=': case ']': case '}': case ':': case ',': case '=': case ']': case '}':
iter->state = __STRUCT | flag; iter->state = __STRUCT | flag;
if (iter->depth > 0) if (iter->depth > 0)
@ -399,6 +399,10 @@ SPA_API_JSON int spa_json_is_container(const char *val, int len)
{ {
return len > 0 && (*val == '{' || *val == '['); return len > 0 && (*val == '{' || *val == '[');
} }
SPA_API_JSON int spa_json_is_container_end(const char *val, int len)
{
return len > 0 && (*val == '}' || *val == ']');
}
/* object */ /* object */
SPA_API_JSON int spa_json_is_object(const char *val, int len) SPA_API_JSON int spa_json_is_object(const char *val, int len)
@ -421,20 +425,11 @@ SPA_API_JSON bool spa_json_is_null(const char *val, int len)
/* float */ /* float */
SPA_API_JSON int spa_json_parse_float(const char *val, int len, float *result) SPA_API_JSON int spa_json_parse_float(const char *val, int len, float *result)
{ {
char buf[96]; char buf[96], *end;
char *end;
int pos;
if (len <= 0 || len >= (int)sizeof(buf)) if (len <= 0 || len >= (int)sizeof(buf))
return 0; return 0;
for (pos = 0; pos < len; ++pos) {
switch (val[pos]) {
case '+': case '-': case '0' ... '9': case '.': case 'e': case 'E': break;
default: return 0;
}
}
memcpy(buf, val, len); memcpy(buf, val, len);
buf[len] = '\0'; buf[len] = '\0';
@ -462,8 +457,7 @@ SPA_API_JSON char *spa_json_format_float(char *str, int size, float val)
/* int */ /* int */
SPA_API_JSON int spa_json_parse_int(const char *val, int len, int *result) SPA_API_JSON int spa_json_parse_int(const char *val, int len, int *result)
{ {
char buf[64]; char buf[64], *end;
char *end;
if (len <= 0 || len >= (int)sizeof(buf)) if (len <= 0 || len >= (int)sizeof(buf))
return 0; return 0;
@ -480,6 +474,39 @@ SPA_API_JSON bool spa_json_is_int(const char *val, int len)
return spa_json_parse_int(val, len, &dummy); return spa_json_parse_int(val, len, &dummy);
} }
SPA_API_JSON bool spa_json_is_json_number(const char *val, int len)
{
static const int8_t trans[9][7] = {
/* '1-9' '0' '-' '+' '.' 'eE' other */
/* 0 */ {-1, -1, -1, -1, 6, 7, -1 }, /* after '0' */
/* 1 */ { 1, 1, -1, -1, 6, 7, -1 }, /* in integer */
/* 2 */ { 2, 2, -1, -1, -1, 7, -1 }, /* in fraction */
/* 3 */ { 3, 3, -1, -1, -1, -1, -1 }, /* in exponent */
/* 4 */ { 1, 0, 5, -1, -1, -1, -1 }, /* start */
/* 5 */ { 1, 0, -1, -1, -1, -1, -1 }, /* after '-' */
/* 6 */ { 2, 2, -1, -1, -1, -1, -1 }, /* after '.' */
/* 7 */ { 3, 3, 8, 8, -1, -1, -1 }, /* after 'e'/'E' */
/* 8 */ { 3, 3, -1, -1, -1, -1, -1 }, /* after exp sign */
};
static const int8_t char_class[128] = {
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 0-15 */
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 16-31 */
6,6,6,6,6,6,6,6,6,6,6,3,6,2,4,6, /* 32-47: + - . */
1,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6, /* 48-63: 0-9 */
6,6,6,6,6,5,6,6,6,6,6,6,6,6,6,6, /* 64-79: E */
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 80-95 */
6,6,6,6,6,5,6,6,6,6,6,6,6,6,6,6, /* 96-111: e */
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 112-127 */
};
int i, state = 4;
for (i = 0; i < len; i++) {
if ((state = trans[state][char_class[val[i]&0x7f]]) < 0)
return false;
}
return state < 4;
}
/* bool */ /* bool */
SPA_API_JSON bool spa_json_is_true(const char *val, int len) SPA_API_JSON bool spa_json_is_true(const char *val, int len)
{ {
@ -510,6 +537,46 @@ SPA_API_JSON bool spa_json_is_string(const char *val, int len)
{ {
return len > 1 && *val == '"'; return len > 1 && *val == '"';
} }
SPA_API_JSON bool spa_json_is_simple_string(const char *val, int size)
{
int i;
static const char *REJECT = "\"\\'=:,{}[]()#";
for (i = 0; i < size && val[i]; i++) {
if (val[i] <= 0x20 || strchr(REJECT, val[i]) != NULL)
return false;
}
return true;
}
SPA_API_JSON bool spa_json_make_simple_string(const char **val, int *len)
{
int i, l = *len;
const char *v = *val;
static const char *REJECT = "\"\\'=:,{}[]()#";
int trimmed = 0, bad = 0;
for (i = 0; i < l && v[i]; i++) {
if (i == 0 && v[0] == '\"')
trimmed++;
else if ((i+1 == l || !v[i+1]) && v[i] == '\"')
trimmed++;
else if (v[i] <= 0x20 || strchr(REJECT, v[i]) != NULL)
bad++;
}
if (trimmed == 0 && bad == 0 && i > 0)
return true;
else if (trimmed == 2) {
if (bad == 0 && i > 2 &&
!spa_json_is_null(&v[1], i-2) &&
!spa_json_is_bool(&v[1], i-2) &&
!spa_json_is_float(&v[1], i-2) &&
!spa_json_is_container(&v[1], i-2) &&
!spa_json_is_container_end(&v[1], i-2)) {
(*len) = i-2;
(*val)++;
}
return true;
}
return false;
}
SPA_API_JSON int spa_json_parse_hex(const char *p, int num, uint32_t *res) SPA_API_JSON int spa_json_parse_hex(const char *p, int num, uint32_t *res)
{ {

View file

@ -121,7 +121,10 @@ SPA_API_JSON_POD int spa_json_to_pod_part(struct spa_pod_builder *b, uint32_t fl
spa_pod_builder_none(b); spa_pod_builder_none(b);
} }
else { else {
char *val = (char*)alloca(len+1); char *val;
if (len > 8192)
return -ENOSPC;
val = (char*)alloca(len+1);
spa_json_parse_stringn(value, len, val, len+1); spa_json_parse_stringn(value, len, val, len+1);
switch (info ? info->parent : (uint32_t)SPA_TYPE_Struct) { switch (info ? info->parent : (uint32_t)SPA_TYPE_Struct) {
case SPA_TYPE_Id: case SPA_TYPE_Id:

View file

@ -98,15 +98,6 @@ extern "C" {
#define SPA_KEY_API_LIBCAMERA_ROTATION "api.libcamera.rotation" /**< rotation of the camera: #define SPA_KEY_API_LIBCAMERA_ROTATION "api.libcamera.rotation" /**< rotation of the camera:
* "0", "90", "180" or "270" */ * "0", "90", "180" or "270" */
/** info from libcamera_capability */
#define SPA_KEY_API_LIBCAMERA_CAP_DRIVER "api.libcamera.cap.driver" /**< driver from capbility */
#define SPA_KEY_API_LIBCAMERA_CAP_CARD "api.libcamera.cap.card" /**< caps from capability */
#define SPA_KEY_API_LIBCAMERA_CAP_BUS_INFO "api.libcamera.cap.bus_info"/**< bus_info from capability */
#define SPA_KEY_API_LIBCAMERA_CAP_VERSION "api.libcamera.cap.version" /**< version from capability as %u.%u.%u */
#define SPA_KEY_API_LIBCAMERA_CAP_CAPABILITIES \
"api.libcamera.cap.capabilities" /**< capabilities from capability */
#define SPA_KEY_API_LIBCAMERA_CAP_DEVICE_CAPS \
"api.libcamera.cap.device-caps" /**< device_caps from capability */
/** info from v4l2_capability */ /** info from v4l2_capability */
#define SPA_KEY_API_V4L2_CAP_DRIVER "api.v4l2.cap.driver" /**< driver from capbility */ #define SPA_KEY_API_V4L2_CAP_DRIVER "api.v4l2.cap.driver" /**< driver from capbility */
#define SPA_KEY_API_V4L2_CAP_CARD "api.v4l2.cap.card" /**< caps from capability */ #define SPA_KEY_API_V4L2_CAP_CARD "api.v4l2.cap.card" /**< caps from capability */

View file

@ -0,0 +1,40 @@
/* Simple Plugin API */
/* SPDX-FileCopyrightText: Copyright © 2026 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#ifndef SPA_UTILS_OVERFLOW_H
#define SPA_UTILS_OVERFLOW_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Check for addition overflow
*
* Computes \a a + \a b and stores the result in \a *res.
* \return true if the addition overflowed, false otherwise
*/
#define spa_overflow_add(a, b, res) __builtin_add_overflow(a, b, res)
/**
* \brief Check for subtraction overflow
*
* Computes \a a - \a b and stores the result in \a *res.
* \return true if the subtraction overflowed, false otherwise
*/
#define spa_overflow_sub(a, b, res) __builtin_sub_overflow(a, b, res)
/**
* \brief Check for multiplication overflow
*
* Computes \a a * \a b and stores the result in \a *res.
* \return true if the multiplication overflowed, false otherwise
*/
#define spa_overflow_mul(a, b, res) __builtin_mul_overflow(a, b, res)
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SPA_UTILS_OVERFLOW_H */

View file

@ -380,17 +380,26 @@ SPA_API_STRING void spa_strbuf_init(struct spa_strbuf *buf, char *buffer, size_t
buf->buffer[0] = '\0'; buf->buffer[0] = '\0';
} }
SPA_PRINTF_FUNC(2, 0)
SPA_API_STRING int spa_strbuf_appendv(struct spa_strbuf *buf, const char *fmt, va_list args)
{
size_t remain = buf->maxsize - buf->pos;
int written = vsnprintf(&buf->buffer[buf->pos], remain, fmt, args);
if (written > 0)
buf->pos += SPA_MIN(remain, (size_t)written);
return written;
}
SPA_PRINTF_FUNC(2, 3) SPA_PRINTF_FUNC(2, 3)
SPA_API_STRING int spa_strbuf_append(struct spa_strbuf *buf, const char *fmt, ...) SPA_API_STRING int spa_strbuf_append(struct spa_strbuf *buf, const char *fmt, ...)
{ {
size_t remain = buf->maxsize - buf->pos;
ssize_t written;
va_list args; va_list args;
int written;
va_start(args, fmt); va_start(args, fmt);
written = vsnprintf(&buf->buffer[buf->pos], remain, fmt, args); written = spa_strbuf_appendv(buf, fmt, args);
va_end(args); va_end(args);
if (written > 0)
buf->pos += SPA_MIN(remain, (size_t)written);
return written; return written;
} }

View file

@ -5,6 +5,7 @@
#ifndef SPA_TYPE_INFO_H #ifndef SPA_TYPE_INFO_H
#define SPA_TYPE_INFO_H #define SPA_TYPE_INFO_H
#include <spa/utils/defs.h> #include <spa/utils/defs.h>
#include <spa/utils/type.h> #include <spa/utils/type.h>
#include <spa/utils/enum-types.h> #include <spa/utils/enum-types.h>
@ -78,6 +79,8 @@ static const struct spa_type_info spa_types[] = {
{ SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Latency, spa_type_param_latency }, { SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Latency, spa_type_param_latency },
{ SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_ProcessLatency, spa_type_param_process_latency }, { SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_ProcessLatency, spa_type_param_process_latency },
{ SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Tag, spa_type_param_tag }, { SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Tag, spa_type_param_tag },
{ SPA_TYPE_OBJECT_PeerParam, SPA_TYPE_Object, SPA_TYPE_INFO_PeerParam, spa_type_peer_param },
{ SPA_TYPE_OBJECT_ParamDict, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Dict, spa_type_param_dict },
{ 0, 0, NULL, NULL } { 0, 0, NULL, NULL }
}; };

View file

@ -88,6 +88,8 @@ enum {
SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_OBJECT_ParamLatency,
SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_OBJECT_ParamProcessLatency,
SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_OBJECT_ParamTag,
SPA_TYPE_OBJECT_PeerParam,
SPA_TYPE_OBJECT_ParamDict,
_SPA_TYPE_OBJECT_LAST, /**< not part of ABI */ _SPA_TYPE_OBJECT_LAST, /**< not part of ABI */
/* vendor extensions */ /* vendor extensions */

View file

@ -2,7 +2,6 @@
#undef SPA_AUDIO_MAX_CHANNELS #undef SPA_AUDIO_MAX_CHANNELS
#define SPA_API_IMPL SPA_EXPORT #define SPA_API_IMPL SPA_EXPORT
#include <spa/utils/defs.h>
#include <spa/buffer/alloc.h> #include <spa/buffer/alloc.h>
#include <spa/buffer/buffer.h> #include <spa/buffer/buffer.h>
#include <spa/buffer/type-info.h> #include <spa/buffer/type-info.h>
@ -126,16 +125,17 @@
#include <spa/param/video/raw-types.h> #include <spa/param/video/raw-types.h>
#include <spa/param/video/raw-utils.h> #include <spa/param/video/raw-utils.h>
#include <spa/param/video/type-info.h> #include <spa/param/video/type-info.h>
#include <spa/pod/body.h>
#include <spa/pod/builder.h> #include <spa/pod/builder.h>
#include <spa/pod/command.h> #include <spa/pod/command.h>
#include <spa/pod/compare.h> #include <spa/pod/compare.h>
#include <spa/pod/dynamic.h> #include <spa/pod/dynamic.h>
#include <spa/pod/event.h> #include <spa/pod/event.h>
#include <spa/pod/filter.h> #include <spa/pod/filter.h>
#include <spa/pod/body.h>
#include <spa/pod/iter.h> #include <spa/pod/iter.h>
#include <spa/pod/parser.h> #include <spa/pod/parser.h>
#include <spa/pod/pod.h> #include <spa/pod/pod.h>
#include <spa/pod/simplify.h>
#include <spa/pod/vararg.h> #include <spa/pod/vararg.h>
#include <spa/support/cpu.h> #include <spa/support/cpu.h>
#include <spa/support/dbus.h> #include <spa/support/dbus.h>
@ -158,6 +158,7 @@
#include <spa/utils/hook.h> #include <spa/utils/hook.h>
#include <spa/utils/json-core.h> #include <spa/utils/json-core.h>
#include <spa/utils/json.h> #include <spa/utils/json.h>
#include <spa/utils/json-builder.h>
#include <spa/utils/json-pod.h> #include <spa/utils/json-pod.h>
#include <spa/utils/keys.h> #include <spa/utils/keys.h>
#include <spa/utils/list.h> #include <spa/utils/list.h>

View file

@ -39,7 +39,7 @@ struct impl_data {
std::unique_ptr<float *[]> play_buffer, rec_buffer, out_buffer; std::unique_ptr<float *[]> play_buffer, rec_buffer, out_buffer;
}; };
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.eac.webrtc"); SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.aec.webrtc");
#undef SPA_LOG_TOPIC_DEFAULT #undef SPA_LOG_TOPIC_DEFAULT
#define SPA_LOG_TOPIC_DEFAULT &log_topic #define SPA_LOG_TOPIC_DEFAULT &log_topic
@ -221,6 +221,11 @@ static int webrtc_init2(void *object, const struct spa_dict *args,
}}; }};
#endif #endif
if (out_info->channels != 1 && rec_info->channels != out_info->channels) {
spa_log_error(impl->log, "Source channels must be equal to capture channels or 1");
return -EINVAL;
}
#if defined(HAVE_WEBRTC) #if defined(HAVE_WEBRTC)
auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessing::Create(config)); auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessing::Create(config));
#elif defined(HAVE_WEBRTC1) #elif defined(HAVE_WEBRTC1)

View file

@ -187,6 +187,11 @@ ATTRS{idVendor}=="1395", ATTRS{idProduct}=="0300", ENV{ACP_PROFILE_SET}="usb-gam
# Sennheiser GSP 670 USB headset # Sennheiser GSP 670 USB headset
ATTRS{idVendor}=="1395", ATTRS{idProduct}=="008a", ENV{ACP_PROFILE_SET}="usb-gaming-headset.conf" ATTRS{idVendor}=="1395", ATTRS{idProduct}=="008a", ENV{ACP_PROFILE_SET}="usb-gaming-headset.conf"
# JBL Quantum One
ATTRS{idVendor}=="0ecb", ATTRS{idProduct}=="203a", ENV{ACP_PROFILE_SET}="usb-gaming-headset-gamefirst.conf"
# JBL Quantum 810 Wireless
ATTRS{idVendor}=="0ecb", ATTRS{idProduct}=="2069", ENV{ACP_PROFILE_SET}="usb-gaming-headset-gamefirst.conf"
# Audioengine HD3 powered speakers support IEC958 but don't actually # Audioengine HD3 powered speakers support IEC958 but don't actually
# have any digital outputs. # have any digital outputs.
ATTRS{idVendor}=="0a12", ATTRS{idProduct}=="4007", ENV{ACP_PROFILE_SET}="analog-only.conf" ATTRS{idVendor}=="0a12", ATTRS{idProduct}=="4007", ENV{ACP_PROFILE_SET}="analog-only.conf"

View file

@ -10,7 +10,9 @@
#include <time.h> #include <time.h>
#include <stdbool.h> #include <stdbool.h>
#include <getopt.h> #include <getopt.h>
#ifdef __linux__
#include <alloca.h> #include <alloca.h>
#endif
#include <spa/debug/context.h> #include <spa/debug/context.h>
#include <spa/utils/string.h> #include <spa/utils/string.h>

View file

@ -227,7 +227,8 @@ static void init_device(pa_card *impl, pa_alsa_device *dev, pa_alsa_direction_t
dev->device.format.rate_mask = m->sample_spec.rate; dev->device.format.rate_mask = m->sample_spec.rate;
dev->device.format.channels = m->channel_map.channels; dev->device.format.channels = m->channel_map.channels;
dev->device.format.map = calloc(m->channel_map.channels, sizeof(uint32_t)); dev->device.format.map = calloc(m->channel_map.channels, sizeof(uint32_t));
channelmap_to_acp(&m->channel_map, dev->device.format.map); if (dev->device.format.map != NULL)
channelmap_to_acp(&m->channel_map, dev->device.format.map);
pa_cvolume_reset(&dev->real_volume, dev->device.format.channels); pa_cvolume_reset(&dev->real_volume, dev->device.format.channels);
pa_cvolume_reset(&dev->soft_volume, dev->device.format.channels); pa_cvolume_reset(&dev->soft_volume, dev->device.format.channels);
dev->direction = direction; dev->direction = direction;
@ -485,13 +486,11 @@ static int add_pro_profile(pa_card *impl, uint32_t index)
if ((n_capture == 1 && n_playback == 1) || is_firewire) { if ((n_capture == 1 && n_playback == 1) || is_firewire) {
PA_IDXSET_FOREACH(m, ap->output_mappings, idx) { PA_IDXSET_FOREACH(m, ap->output_mappings, idx) {
pa_proplist_setf(m->output_proplist, "node.group", "pro-audio-%u", index); pa_proplist_setf(m->output_proplist, "node.group", "pro-audio-%u", index);
pa_proplist_setf(m->output_proplist, "node.link-group", "pro-audio-%u", index);
pa_proplist_setf(m->output_proplist, "api.alsa.auto-link", "true"); pa_proplist_setf(m->output_proplist, "api.alsa.auto-link", "true");
pa_proplist_setf(m->output_proplist, "api.alsa.disable-tsched", "true"); pa_proplist_setf(m->output_proplist, "api.alsa.disable-tsched", "true");
} }
PA_IDXSET_FOREACH(m, ap->input_mappings, idx) { PA_IDXSET_FOREACH(m, ap->input_mappings, idx) {
pa_proplist_setf(m->input_proplist, "node.group", "pro-audio-%u", index); pa_proplist_setf(m->input_proplist, "node.group", "pro-audio-%u", index);
pa_proplist_setf(m->input_proplist, "node.link-group", "pro-audio-%u", index);
pa_proplist_setf(m->input_proplist, "api.alsa.auto-link", "true"); pa_proplist_setf(m->input_proplist, "api.alsa.auto-link", "true");
pa_proplist_setf(m->input_proplist, "api.alsa.disable-tsched", "true"); pa_proplist_setf(m->input_proplist, "api.alsa.disable-tsched", "true");
} }
@ -1137,7 +1136,7 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask)
} }
old_position = pa_proplist_gets(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED); old_position = pa_proplist_gets(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED);
if (eld.speakers == 0) { if (eld.speakers == 0 || eld.lpcm_channels == 0) {
changed |= old_position != NULL; changed |= old_position != NULL;
pa_proplist_unset(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED); pa_proplist_unset(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED);
} else { } else {
@ -1146,32 +1145,38 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask)
struct spa_strbuf b; struct spa_strbuf b;
int i = 0; int i = 0;
#define _ADD_CHANNEL_POSITION(pos) \
{ \
if (i < eld.lpcm_channels) \
positions[i++] = pos; \
}
if (eld.speakers & 0x01) { if (eld.speakers & 0x01) {
positions[i++] = ACP_CHANNEL_FL; _ADD_CHANNEL_POSITION(ACP_CHANNEL_FL);
positions[i++] = ACP_CHANNEL_FR; _ADD_CHANNEL_POSITION(ACP_CHANNEL_FR);
} }
if (eld.speakers & 0x02) { if (eld.speakers & 0x02) {
positions[i++] = ACP_CHANNEL_LFE; _ADD_CHANNEL_POSITION(ACP_CHANNEL_LFE);
} }
if (eld.speakers & 0x04) { if (eld.speakers & 0x04) {
positions[i++] = ACP_CHANNEL_FC; _ADD_CHANNEL_POSITION(ACP_CHANNEL_FC);
} }
if (eld.speakers & 0x08) { if (eld.speakers & 0x08) {
positions[i++] = ACP_CHANNEL_RL; _ADD_CHANNEL_POSITION(ACP_CHANNEL_RL);
positions[i++] = ACP_CHANNEL_RR; _ADD_CHANNEL_POSITION(ACP_CHANNEL_RR);
} }
/* The rest are out of order in order of what channels we would prefer to use/expose first */ /* The rest are out of order in order of what channels we would prefer to use/expose first */
if (eld.speakers & 0x40) { if (eld.speakers & 0x40) {
/* Use SL/SR instead of RLC/RRC */ /* Use SL/SR instead of RLC/RRC */
positions[i++] = ACP_CHANNEL_SL; _ADD_CHANNEL_POSITION(ACP_CHANNEL_SL);
positions[i++] = ACP_CHANNEL_SR; _ADD_CHANNEL_POSITION(ACP_CHANNEL_SR);
} }
if (eld.speakers & 0x20) { if (eld.speakers & 0x20) {
positions[i++] = ACP_CHANNEL_RLC; _ADD_CHANNEL_POSITION(ACP_CHANNEL_RLC);
positions[i++] = ACP_CHANNEL_RRC; _ADD_CHANNEL_POSITION(ACP_CHANNEL_RRC);
} }
if (eld.speakers & 0x10) { if (eld.speakers & 0x10) {
positions[i++] = ACP_CHANNEL_RC; _ADD_CHANNEL_POSITION(ACP_CHANNEL_RC);
} }
while (i < eld.lpcm_channels) while (i < eld.lpcm_channels)
positions[i++] = ACP_CHANNEL_UNKNOWN; positions[i++] = ACP_CHANNEL_UNKNOWN;
@ -1740,6 +1745,8 @@ static int device_enable(pa_card *impl, pa_alsa_mapping *mapping, pa_alsa_device
dev->device.format.channels = atoi(channels); dev->device.format.channels = atoi(channels);
free(dev->device.format.map); free(dev->device.format.map);
dev->device.format.map = calloc(dev->device.format.channels, sizeof(uint32_t)); dev->device.format.map = calloc(dev->device.format.channels, sizeof(uint32_t));
if (dev->device.format.map == NULL)
break;
while ((position = pa_split_in_place(positions, ",", &n, &split_state)) != NULL && while ((position = pa_split_in_place(positions, ",", &n, &split_state)) != NULL &&
i < dev->device.format.channels) { i < dev->device.format.channels) {

View file

@ -63,6 +63,7 @@ char *pa_alsa_mixer_id_to_string(char *dst, size_t dst_len, pa_alsa_mixer_id *id
} }
static int alsa_id_decode(const char *src, char *name, int *index) { static int alsa_id_decode(const char *src, char *name, int *index) {
size_t src_len = strlen(src);
char *idx, c; char *idx, c;
int i; int i;
@ -70,7 +71,7 @@ static int alsa_id_decode(const char *src, char *name, int *index) {
c = src[0]; c = src[0];
/* Strip quotes in entries such as 'Speaker',1 or "Speaker",1 */ /* Strip quotes in entries such as 'Speaker',1 or "Speaker",1 */
if (c == '\'' || c == '"') { if (c == '\'' || c == '"') {
strcpy(name, src + 1); memcpy(name, src + 1, src_len);
for (i = 0; name[i] != '\0' && name[i] != c; i++); for (i = 0; name[i] != '\0' && name[i] != c; i++);
idx = NULL; idx = NULL;
if (name[i]) { if (name[i]) {
@ -78,7 +79,7 @@ static int alsa_id_decode(const char *src, char *name, int *index) {
idx = strchr(name + i + 1, ','); idx = strchr(name + i + 1, ',');
} }
} else { } else {
strcpy(name, src); memcpy(name, src, src_len + 1);
idx = strchr(name, ','); idx = strchr(name, ',');
} }
if (idx == NULL) if (idx == NULL)

View file

@ -429,14 +429,14 @@ static PA_PRINTF_FUNC(1,0) inline char *pa_vsprintf_malloc(const char *fmt, va_l
#define pa_fopen_cloexec(f,m) fopen(f,m"e") #define pa_fopen_cloexec(f,m) fopen(f,m"e")
static inline char *pa_path_get_filename(const char *p) static inline const char *pa_path_get_filename(const char *p)
{ {
char *fn; const char *fn;
if (!p) if (!p)
return NULL; return NULL;
if ((fn = strrchr(p, PA_PATH_SEP_CHAR))) if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
return fn+1; return fn+1;
return (char*) p; return p;
} }
static inline bool pa_is_path_absolute(const char *fn) static inline bool pa_is_path_absolute(const char *fn)

View file

@ -990,11 +990,22 @@ static void card_port_available(void *data, uint32_t index,
for (i = 0; i < p->n_devices; i++) { for (i = 0; i < p->n_devices; i++) {
struct acp_device *d = p->devices[i]; struct acp_device *d = p->devices[i];
struct acp_port *active_port = NULL;
uint32_t j;
uint32_t best; uint32_t best;
if (!(d->flags & ACP_DEVICE_ACTIVE)) if (!(d->flags & ACP_DEVICE_ACTIVE))
continue; continue;
for (j = 0; j < d->n_ports; j++) {
if (d->ports[j]->flags & ACP_PORT_ACTIVE) {
active_port = d->ports[j];
break;
}
}
if (active_port != NULL && active_port->available != ACP_AVAILABLE_NO)
continue;
best = acp_device_find_best_port_index(d, NULL); best = acp_device_find_best_port_index(d, NULL);
acp_device_set_port(d, best, 0); acp_device_set_port(d, best, 0);
} }

View file

@ -1971,7 +1971,7 @@ impl_init(const struct spa_handle_factory *factory, struct spa_handle *handle,
if (spa_streq(k, SPA_KEY_API_ALSA_PATH)) { if (spa_streq(k, SPA_KEY_API_ALSA_PATH)) {
snprintf(this->props.device, sizeof(this->props.device), "%s", s); snprintf(this->props.device, sizeof(this->props.device), "%s", s);
if ((res = parse_device(this)) < 0) if ((res = parse_device(this)) < 0)
return res; goto error;
} }
} }

View file

@ -48,6 +48,8 @@ static struct card *ensure_card(uint32_t index, bool ucm, bool ucm_split)
return c; return c;
c = calloc(1, sizeof(*c)); c = calloc(1, sizeof(*c));
if (c == NULL)
return NULL;
c->ref = 1; c->ref = 1;
c->index = index; c->index = index;
@ -714,19 +716,25 @@ int spa_alsa_parse_prop_params(struct state *state, struct spa_pod *params)
static ssize_t log_write(void *cookie, const char *buf, size_t size) static ssize_t log_write(void *cookie, const char *buf, size_t size)
{ {
struct state *state = cookie; struct state *state = cookie;
int len;
while (size > 0) { if (!spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_DEBUG))
len = strcspn(buf, "\n"); return size;
for (size_t left = size; left > 0; ) {
const char *end = memchr(buf, '\n', left);
size_t len = end ? (size_t)(end - buf) : left;
if (len > 0) if (len > 0)
spa_log_debug(state->log, "%.*s", (int)len, buf); spa_log_debug(state->log, "%.*s", (int)len, buf);
buf += len + 1;
size -= len + 1; buf += len + !!end;
left -= len + !!end;
} }
return size; return size;
} }
static cookie_io_functions_t io_funcs = { static const cookie_io_functions_t io_funcs = {
.write = log_write, .write = log_write,
}; };
@ -1066,7 +1074,7 @@ int spa_alsa_init(struct state *state, const struct spa_dict *info)
int spa_alsa_clear(struct state *state) int spa_alsa_clear(struct state *state)
{ {
int err; int err = 0;
struct state *follower; struct state *follower;
spa_list_remove(&state->link); spa_list_remove(&state->link);
@ -1092,12 +1100,8 @@ int spa_alsa_clear(struct state *state)
state->card = NULL; state->card = NULL;
state->card_index = SPA_ID_INVALID; state->card_index = SPA_ID_INVALID;
if ((err = snd_output_close(state->output)) < 0) spa_clear_ptr(state->tag[0], free);
spa_log_warn(state->log, "output close failed: %s", snd_strerror(err)); spa_clear_ptr(state->tag[1], free);
fclose(state->log_file);
free(state->tag[0]);
free(state->tag[1]);
if (state->ctl) { if (state->ctl) {
for (int i = 0; i < state->ctl_n_fds; i++) { for (int i = 0; i < state->ctl_n_fds; i++) {
@ -1119,6 +1123,14 @@ int spa_alsa_clear(struct state *state)
} }
} }
if (state->output) {
if ((err = snd_output_close(state->output)) < 0)
spa_log_warn(state->log, "output close failed: %s", snd_strerror(err));
}
spa_clear_ptr(state->log_file, fclose);
spa_clear_ptr(state->alsa_chmap, free);
return err; return err;
} }
@ -1154,25 +1166,32 @@ static int probe_pitch_ctl(struct state *state)
if (err < 0) { if (err < 0) {
spa_log_debug(state->log, "%s: did not find ctl: %s", spa_log_debug(state->log, "%s: did not find ctl: %s",
elem_name, snd_strerror(err)); elem_name, snd_strerror(err));
snd_ctl_elem_value_free(state->pitch_elem);
state->pitch_elem = NULL;
if (opened) {
snd_ctl_close(state->ctl);
state->ctl = NULL;
}
goto error; goto error;
} }
snd_ctl_elem_value_set_integer(state->pitch_elem, 0, 1000000); snd_ctl_elem_value_set_integer(state->pitch_elem, 0, 1000000);
CHECK(snd_ctl_elem_write(state->ctl, state->pitch_elem), "snd_ctl_elem_write"); err = snd_ctl_elem_write(state->ctl, state->pitch_elem);
if (err < 0) {
spa_log_error(state->log, "snd_ctl_elem_write: %s", snd_strerror(err));
goto error;
}
state->last_rate = 1.0; state->last_rate = 1.0;
spa_log_info(state->log, "found ctl %s", elem_name); spa_log_info(state->log, "found ctl %s", elem_name);
err = 0;
snd_lib_error_set_handler(NULL);
return 0;
error: error:
if (state->pitch_elem != NULL) {
snd_ctl_elem_value_free(state->pitch_elem);
state->pitch_elem = NULL;
}
if (opened) {
snd_ctl_close(state->ctl);
state->ctl = NULL;
}
snd_lib_error_set_handler(NULL); snd_lib_error_set_handler(NULL);
return err; return err;
} }
@ -1664,6 +1683,69 @@ skip_channels:
return 1; return 1;
} }
static snd_pcm_chmap_t *dup_alsa_chmap(snd_pcm_chmap_t *map)
{
snd_pcm_chmap_t *chmap;
size_t sz;
sz = sizeof(*chmap) + sizeof(*chmap->pos) * map->channels;
chmap = calloc(1, sz);
if (chmap)
memcpy(chmap, map, sz);
return chmap;
}
static int get_alsa_chmap(snd_pcm_t *hndl, uint32_t channels, const uint32_t *position,
snd_pcm_chmap_t **chmap)
{
snd_pcm_chmap_query_t **maps = snd_pcm_query_chmaps(hndl);
size_t i, j;
int err = 0;
*chmap = NULL;
if (!maps)
return 0;
/* Find corresponding ALSA chmap */
for (i = 0; maps[i]; ++i) {
spa_autofree snd_pcm_chmap_t *map = NULL;
if (maps[i]->map.channels != channels)
continue;
map = dup_alsa_chmap(&maps[i]->map);
if (!map) {
err = -ENOMEM;
break;
}
sanitize_map(map);
for (j = 0; j < channels; ++j) {
if (position[j] != chmap_position_to_channel(map->pos[j]))
break;
}
if (j < channels)
continue;
*chmap = dup_alsa_chmap(&maps[i]->map);
if (!*chmap)
err = -ENOMEM;
break;
}
if (!maps[i])
err = -ENOENT;
snd_pcm_free_chmaps(maps);
if (err) {
free(*chmap);
*chmap = NULL;
}
return err;
}
static void debug_hw_params(struct state *state, const char *prefix, snd_pcm_hw_params_t *params) static void debug_hw_params(struct state *state, const char *prefix, snd_pcm_hw_params_t *params)
{ {
if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_DEBUG))) { if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_DEBUG))) {
@ -2036,7 +2118,9 @@ static void recalc_headroom(struct state *state)
uint32_t latency; uint32_t latency;
uint32_t rate = 0; uint32_t rate = 0;
if (state->position != NULL) if (state->force_quantum && !state->following)
rate = state->rate;
else if (state->position != NULL)
rate = state->position->clock.target_rate.denom; rate = state->position->clock.target_rate.denom;
if (state->use_period_size_min_as_headroom) if (state->use_period_size_min_as_headroom)
@ -2063,8 +2147,6 @@ static void recalc_headroom(struct state *state)
state->headroom = 0; state->headroom = 0;
latency = SPA_MAX(state->min_delay, SPA_MIN(state->max_delay, state->headroom)); latency = SPA_MAX(state->min_delay, SPA_MIN(state->max_delay, state->headroom));
if (rate != 0 && state->rate != 0)
latency = SPA_SCALE32_UP(latency, rate, state->rate);
if (state->is_firewire) { if (state->is_firewire) {
/* XXX: For ALSA FireWire drivers, unlike for other ALSA drivers, buffer size /* XXX: For ALSA FireWire drivers, unlike for other ALSA drivers, buffer size
@ -2072,6 +2154,8 @@ static void recalc_headroom(struct state *state)
*/ */
latency += state->buffer_frames; latency += state->buffer_frames;
} }
if (rate != 0 && state->rate != 0)
latency = SPA_SCALE32_UP(latency, rate, state->rate);
state->latency[state->port_direction].min_rate = state->latency[state->port_direction].min_rate =
state->latency[state->port_direction].max_rate = latency; state->latency[state->port_direction].max_rate = latency;
@ -2089,7 +2173,9 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_
unsigned int periods; unsigned int periods;
bool match = true, planar = false; bool match = true, planar = false;
char spdif_params[128] = ""; char spdif_params[128] = "";
char alsa_chmap_str[256] = "";
uint32_t default_period; uint32_t default_period;
const uint32_t *position = NULL;
spa_log_debug(state->log, "opened:%d format:%d started:%d", state->opened, spa_log_debug(state->log, "opened:%d format:%d started:%d", state->opened,
state->have_format, state->started); state->have_format, state->started);
@ -2103,6 +2189,8 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_
struct spa_audio_info_raw *f = &fmt->info.raw; struct spa_audio_info_raw *f = &fmt->info.raw;
rrate = f->rate; rrate = f->rate;
rchannels = f->channels; rchannels = f->channels;
if (!SPA_FLAG_IS_SET(f->flags, SPA_AUDIO_FLAG_UNPOSITIONED))
position = f->position;
rformat = spa_format_to_alsa(f->format, &planar); rformat = spa_format_to_alsa(f->format, &planar);
break; break;
} }
@ -2167,6 +2255,7 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_
rrate = f->rate; rrate = f->rate;
rchannels = f->channels; rchannels = f->channels;
position = f->position;
switch (f->interleave) { switch (f->interleave) {
case 4: case 4:
@ -2255,6 +2344,28 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_
planar ? "planar" : "interleaved", rchannels); planar ? "planar" : "interleaved", rchannels);
CHECK(snd_pcm_hw_params_set_format(hndl, params, rformat), "set_format"); CHECK(snd_pcm_hw_params_set_format(hndl, params, rformat), "set_format");
/* prepare channel map */
{
spa_autofree snd_pcm_chmap_t *alsa_chmap = NULL;
if (state->props.use_chmap && position) {
if ((err = get_alsa_chmap(hndl, rchannels, position, &alsa_chmap)) < 0) {
struct channel_map map = { .n_pos = SPA_MIN(MAX_CHANNELS, rchannels) };
char buf[256] = "";
memcpy(map.pos, position, map.n_pos * sizeof(map.pos[0]));
position_to_string(&map, buf, sizeof(buf));
spa_log_warn(state->log, "%s: failed to get ALSA chmap '%s': %s",
state->name, buf, spa_strerror(err));
}
}
SPA_SWAP(state->alsa_chmap, alsa_chmap);
alsa_chmap_str[0] = '\0';
if (state->alsa_chmap)
snd_pcm_chmap_print(state->alsa_chmap, sizeof(alsa_chmap_str), alsa_chmap_str);
}
/* set the count of channels */ /* set the count of channels */
val = rchannels; val = rchannels;
CHECK(snd_pcm_hw_params_set_channels_near(hndl, params, &val), "set_channels"); CHECK(snd_pcm_hw_params_set_channels_near(hndl, params, &val), "set_channels");
@ -2432,13 +2543,13 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_
recalc_headroom(state); recalc_headroom(state);
spa_log_info(state->log, "%s: format:%s access:%s-%s rate:%d channels:%d " spa_log_info(state->log, "%s: format:%s access:%s-%s rate:%d channels:%d chmap:'%s' "
"buffer frames %lu, period frames %lu (min:%lu), periods %u, frame_size %zd " "buffer frames %lu, period frames %lu (min:%lu), periods %u, frame_size %zd "
"headroom %u start-delay:%u batch:%u tsched:%u resample:%u", "headroom %u start-delay:%u batch:%u tsched:%u resample:%u",
state->name, snd_pcm_format_name(state->format), state->name, snd_pcm_format_name(state->format),
state->use_mmap ? "mmap" : "rw", state->use_mmap ? "mmap" : "rw",
planar ? "planar" : "interleaved", planar ? "planar" : "interleaved",
state->rate, state->channels, state->buffer_frames, state->period_frames, state->rate, state->channels, alsa_chmap_str, state->buffer_frames, state->period_frames,
state->period_size_min, periods, state->frame_size, state->headroom, state->period_size_min, periods, state->frame_size, state->headroom,
state->start_delay, state->is_batch, !state->disable_tsched, state->resample); state->start_delay, state->is_batch, !state->disable_tsched, state->resample);
@ -2576,16 +2687,22 @@ static int spa_alsa_silence(struct state *state, snd_pcm_uframes_t silence)
return res; return res;
} }
} else { } else {
uint8_t buffer[silence * state->frame_size]; uint8_t buffer[1024 * 4];
memset(buffer, 0, silence * state->frame_size); void *bufs[state->channels];
snd_pcm_uframes_t chunk, remaining = silence;
snd_pcm_uframes_t max = sizeof(buffer) / state->frame_size;
if (state->planar) { memset(buffer, 0, sizeof(buffer));
void *bufs[state->channels]; for (i = 0; i < state->channels; i++)
for (i = 0; i < state->channels; i++) bufs[i] = buffer;
bufs[i] = buffer;
snd_pcm_writen(hndl, bufs, silence); while (remaining > 0) {
} else { chunk = SPA_MIN(remaining, max);
snd_pcm_writei(hndl, buffer, silence); if (state->planar)
snd_pcm_writen(hndl, bufs, chunk);
else
snd_pcm_writei(hndl, buffer, chunk);
remaining -= chunk;
} }
} }
return 0; return 0;
@ -2629,6 +2746,16 @@ static int do_prepare(struct state *state)
state->name, snd_strerror(err)); state->name, snd_strerror(err));
return err; return err;
} }
if (state->alsa_chmap) {
if ((err = snd_pcm_set_chmap(state->hndl, state->alsa_chmap)) < 0)
spa_log_error(state->log, "%s: snd_pcm_set_chmap error: %s",
state->name, snd_strerror(err));
/* it's enough to set chmap only on the initial prepare after hwparams */
spa_clear_ptr(state->alsa_chmap, free);
}
if (state->stream == SND_PCM_STREAM_PLAYBACK) { if (state->stream == SND_PCM_STREAM_PLAYBACK) {
snd_pcm_uframes_t silence = state->start_delay + state->threshold + state->headroom; snd_pcm_uframes_t silence = state->start_delay + state->threshold + state->headroom;
if (state->disable_tsched) if (state->disable_tsched)
@ -2936,10 +3063,17 @@ static int update_time(struct state *state, uint64_t current_time, snd_pcm_sfram
} }
if (state->rate_match) { if (state->rate_match) {
if (state->stream == SND_PCM_STREAM_PLAYBACK) /* Only set rate_match rate when matching is active. When not matching,
state->rate_match->rate = corr; * set it to 1.0 to indicate no rate adjustment needed, even though DLL
else * may still be running for buffer level management. */
state->rate_match->rate = 1.0/corr; if (state->matching) {
if (state->stream == SND_PCM_STREAM_PLAYBACK)
state->rate_match->rate = corr;
else
state->rate_match->rate = 1.0/corr;
} else {
state->rate_match->rate = 1.0;
}
if (state->pitch_elem && state->matching) if (state->pitch_elem && state->matching)
spa_alsa_update_rate_match(state); spa_alsa_update_rate_match(state);

View file

@ -280,6 +280,8 @@ struct state {
struct spa_list driver_link; struct spa_list driver_link;
struct rt_state rt; struct rt_state rt;
snd_pcm_chmap_t *alsa_chmap;
}; };
struct spa_pod *spa_alsa_enum_propinfo(struct state *state, struct spa_pod *spa_alsa_enum_propinfo(struct state *state,

View file

@ -227,7 +227,7 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f
if (full) if (full)
port->info.change_mask = port->info_all; port->info.change_mask = port->info_all;
if (port->info.change_mask) { if (port->info.change_mask) {
struct spa_dict_item items[6]; struct spa_dict_item items[7];
uint32_t n_items = 0; uint32_t n_items = 0;
int card_id; int card_id;
snd_seq_port_info_t *info; snd_seq_port_info_t *info;
@ -261,7 +261,7 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f
if (spa_strstartswith(pn, client_name)) if (spa_strstartswith(pn, client_name))
pn += strlen(client_name); pn += strlen(client_name);
snprintf(name, sizeof(name), "%s%s%s (%s)", prefix, snprintf(name, sizeof(name), "%s%s:%s (%s)", prefix,
client_name, pn, dir); client_name, pn, dir);
clean_name(name); clean_name(name);
@ -284,6 +284,9 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f
snprintf(card, sizeof(card), "%d", card_id); snprintf(card, sizeof(card), "%d", card_id);
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, card); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, card);
} }
if (this->ump)
items[n_items++] = SPA_DICT_ITEM_INIT("control.ump", "true");
port->info.props = &SPA_DICT_INIT(items, n_items); port->info.props = &SPA_DICT_INIT(items, n_items);
spa_node_emit_port_info(&this->hooks, spa_node_emit_port_info(&this->hooks,
@ -385,13 +388,29 @@ static struct seq_port *alloc_port(struct seq_state *state, struct seq_stream *s
return port; return port;
} }
static int do_port_clear(struct spa_loop *loop, bool async, uint32_t seq,
const void *data, size_t size, void *user_data)
{
struct seq_port *port = user_data;
port->io = NULL;
if (port->mixing) {
spa_list_remove(&port->mix_link);
port->mixing = false;
}
return 0;
}
static void free_port(struct seq_state *state, struct seq_stream *stream, struct seq_port *port) static void free_port(struct seq_state *state, struct seq_stream *stream, struct seq_port *port)
{ {
stream->ports[port->id] = NULL; stream->ports[port->id] = NULL;
spa_list_remove(&port->link); spa_list_remove(&port->link);
spa_loop_locked(state->data_loop,
do_port_clear, SPA_ID_INVALID, NULL, 0, port);
spa_node_emit_port_info(&state->hooks, spa_node_emit_port_info(&state->hooks,
port->direction, port->id, NULL); port->direction, port->id, NULL);
spa_zero(*port); spa_zero(*port);
spa_list_append(&state->free_list, &port->link); spa_list_append(&state->free_list, &port->link);
} }
@ -438,7 +457,7 @@ static void update_stream_port(struct seq_state *state, struct seq_stream *strea
struct seq_port *port = find_port(state, stream, addr); struct seq_port *port = find_port(state, stream, addr);
if (info == NULL) { if (info == NULL) {
spa_log_debug(state->log, "free port %d.%d", addr->client, addr->port); spa_log_debug(state->log, "free port %d.%d %p", addr->client, addr->port, port);
if (port) if (port)
free_port(state, stream, port); free_port(state, stream, port);
} else { } else {
@ -450,7 +469,7 @@ static void update_stream_port(struct seq_state *state, struct seq_stream *strea
init_port(state, port, addr, snd_seq_port_info_get_type(info)); init_port(state, port, addr, snd_seq_port_info_get_type(info));
} else if (port != NULL) { } else if (port != NULL) {
if ((caps & stream->caps) != stream->caps) { if ((caps & stream->caps) != stream->caps) {
spa_log_debug(state->log, "free port %d.%d", addr->client, addr->port); spa_log_debug(state->log, "free port %d.%d %p", addr->client, addr->port, port);
free_port(state, stream, port); free_port(state, stream, port);
} }
else { else {
@ -467,8 +486,8 @@ static int on_port_info(void *data, const snd_seq_addr_t *addr, const snd_seq_po
struct seq_state *state = data; struct seq_state *state = data;
if (info == NULL) { if (info == NULL) {
update_stream_port(state, &state->streams[SPA_DIRECTION_INPUT], addr, 0, info); update_stream_port(state, &state->streams[SPA_DIRECTION_INPUT], addr, 0, NULL);
update_stream_port(state, &state->streams[SPA_DIRECTION_OUTPUT], addr, 0, info); update_stream_port(state, &state->streams[SPA_DIRECTION_OUTPUT], addr, 0, NULL);
} else { } else {
unsigned int caps = snd_seq_port_info_get_capability(info); unsigned int caps = snd_seq_port_info_get_capability(info);
@ -501,6 +520,7 @@ impl_node_port_enum_params(void *object, int seq,
struct seq_state *this = object; struct seq_state *this = object;
struct seq_port *port; struct seq_port *port;
struct spa_pod *param; struct spa_pod *param;
struct spa_pod_frame f[1];
struct spa_pod_builder b = { 0 }; struct spa_pod_builder b = { 0 };
uint8_t buffer[1024]; uint8_t buffer[1024];
struct spa_result_node_params result; struct spa_result_node_params result;
@ -524,10 +544,18 @@ impl_node_port_enum_params(void *object, int seq,
case SPA_PARAM_EnumFormat: case SPA_PARAM_EnumFormat:
if (result.index > 0) if (result.index > 0)
return 0; return 0;
param = spa_pod_builder_add_object(&b, spa_pod_builder_push_object(&b, &f[0],
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(&b,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application), SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control),
0);
if (port->control_types != 0) {
spa_pod_builder_add(&b,
SPA_FORMAT_CONTROL_types, SPA_POD_Int(port->control_types),
0);
}
param = spa_pod_builder_pop(&b, &f[0]);
break; break;
case SPA_PARAM_Format: case SPA_PARAM_Format:
@ -535,10 +563,18 @@ impl_node_port_enum_params(void *object, int seq,
return -EIO; return -EIO;
if (result.index > 0) if (result.index > 0)
return 0; return 0;
param = spa_pod_builder_add_object(&b, spa_pod_builder_push_object(&b, &f[0],
SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(&b,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application), SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control),
0);
if (port->control_types != 0) {
spa_pod_builder_add(&b,
SPA_FORMAT_CONTROL_types, SPA_POD_Int(port->control_types),
0);
}
param = spa_pod_builder_pop(&b, &f[0]);
break; break;
case SPA_PARAM_Buffers: case SPA_PARAM_Buffers:
@ -800,7 +836,7 @@ impl_node_port_set_io(void *object,
info.data = data; info.data = data;
info.size = size; info.size = size;
spa_log_debug(this->log, "%p: io %d.%d %d %p %zd", this, spa_log_debug(this->log, "%p: %p: io %d.%d %d %p %zd", this, port,
direction, port_id, id, data, size); direction, port_id, id, data, size);
switch (id) { switch (id) {
@ -821,7 +857,7 @@ static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t
spa_return_val_if_fail(this != NULL, -EINVAL); spa_return_val_if_fail(this != NULL, -EINVAL);
spa_return_val_if_fail(!CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL);
port = GET_PORT(this, SPA_DIRECTION_OUTPUT, port_id); port = GET_PORT(this, SPA_DIRECTION_OUTPUT, port_id);
@ -955,7 +991,7 @@ impl_init(const struct spa_handle_factory *factory,
this->quantum_limit = 8192; this->quantum_limit = 8192;
this->min_pool_size = 500; this->min_pool_size = 500;
this->max_pool_size = 2000; this->max_pool_size = 2000;
this->ump = true; this->ump = false;
for (i = 0; info && i < info->n_items; i++) { for (i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key; const char *k = info->items[i].key;

View file

@ -620,9 +620,8 @@ static int process_read(struct seq_state *state)
{ {
struct seq_stream *stream = &state->streams[SPA_DIRECTION_OUTPUT]; struct seq_stream *stream = &state->streams[SPA_DIRECTION_OUTPUT];
const bool ump = state->ump; const bool ump = state->ump;
uint32_t *data; void *data;
uint8_t midi1_data[MAX_EVENT_SIZE]; uint8_t midi1_data[MAX_EVENT_SIZE];
uint32_t ump_data[MAX_EVENT_SIZE];
long size; long size;
int res = -1; int res = -1;
struct seq_port *port; struct seq_port *port;
@ -633,9 +632,6 @@ static int process_read(struct seq_state *state)
uint64_t ev_time, diff; uint64_t ev_time, diff;
uint32_t offset; uint32_t offset;
void *event; void *event;
uint8_t *midi1_ptr;
size_t midi1_size = 0;
uint64_t ump_state = 0;
snd_seq_event_type_t SPA_UNUSED type; snd_seq_event_type_t SPA_UNUSED type;
if (ump) { if (ump) {
@ -702,7 +698,7 @@ static int process_read(struct seq_state *state)
#ifdef HAVE_ALSA_UMP #ifdef HAVE_ALSA_UMP
snd_seq_ump_event_t *ev = event; snd_seq_ump_event_t *ev = event;
data = (uint32_t*)&ev->ump[0]; data = &ev->ump[0];
size = spa_ump_message_size(snd_ump_msg_hdr_type(ev->ump[0])) * 4; size = spa_ump_message_size(snd_ump_msg_hdr_type(ev->ump[0])) * 4;
#else #else
spa_assert_not_reached(); spa_assert_not_reached();
@ -715,34 +711,21 @@ static int process_read(struct seq_state *state)
spa_log_warn(state->log, "decode failed: %s", snd_strerror(size)); spa_log_warn(state->log, "decode failed: %s", snd_strerror(size));
continue; continue;
} }
data = midi1_data;
midi1_ptr = midi1_data;
midi1_size = size;
} }
do { spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%ld port:%d.%d",
if (!ump) { type, ev_time, offset, size, addr->client, addr->port);
data = ump_data;
size = spa_ump_from_midi(&midi1_ptr, &midi1_size,
ump_data, sizeof(ump_data), 0, &ump_state);
if (size <= 0)
break;
}
spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%ld port:%d.%d", spa_pod_builder_control(&port->builder, offset, ump ? SPA_CONTROL_UMP : SPA_CONTROL_Midi );
type, ev_time, offset, size, addr->client, addr->port); spa_pod_builder_bytes(&port->builder, data, size);
spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_UMP); /* make sure we can fit at least one control event of max size otherwise
spa_pod_builder_bytes(&port->builder, data, size); * we keep the event in the queue and try to copy it in the next cycle */
if (port->builder.state.offset +
/* make sure we can fit at least one control event of max size otherwise sizeof(struct spa_pod_control) +
* we keep the event in the queue and try to copy it in the next cycle */ MAX_EVENT_SIZE > port->buffer->buf->datas[0].maxsize)
if (port->builder.state.offset + goto done;
sizeof(struct spa_pod_control) +
MAX_EVENT_SIZE > port->buffer->buf->datas[0].maxsize)
goto done;
} while (!ump);
} }
done: done:
@ -819,7 +802,6 @@ static int process_write(struct seq_state *state)
const void *c_body; const void *c_body;
uint64_t out_time; uint64_t out_time;
snd_seq_real_time_t out_rt; snd_seq_real_time_t out_rt;
bool first = true;
if (io->status != SPA_STATUS_HAVE_DATA || if (io->status != SPA_STATUS_HAVE_DATA ||
io->buffer_id >= port->n_buffers) io->buffer_id >= port->n_buffers)
@ -844,9 +826,6 @@ static int process_write(struct seq_state *state)
size_t body_size; size_t body_size;
uint8_t *body; uint8_t *body;
if (c.type != SPA_CONTROL_UMP)
continue;
body = (uint8_t*)c_body; body = (uint8_t*)c_body;
body_size = c.value.size; body_size = c.value.size;
@ -861,6 +840,9 @@ static int process_write(struct seq_state *state)
#ifdef HAVE_ALSA_UMP #ifdef HAVE_ALSA_UMP
snd_seq_ump_event_t ev; snd_seq_ump_event_t ev;
if (c.type != SPA_CONTROL_UMP)
continue;
snd_seq_ump_ev_clear(&ev); snd_seq_ump_ev_clear(&ev);
snd_seq_ev_set_ump_data(&ev, body, SPA_MIN(sizeof(ev.ump), (size_t)body_size)); snd_seq_ev_set_ump_data(&ev, body, SPA_MIN(sizeof(ev.ump), (size_t)body_size));
snd_seq_ev_set_source(&ev, state->event.addr.port); snd_seq_ev_set_source(&ev, state->event.addr.port);
@ -878,26 +860,26 @@ static int process_write(struct seq_state *state)
#endif #endif
} else { } else {
snd_seq_event_t ev; snd_seq_event_t ev;
uint8_t data[MAX_EVENT_SIZE]; int size = 0;
int size; long s;
uint64_t st = 0;
if (c.type != SPA_CONTROL_Midi)
continue;
while (body_size > 0) { while (body_size > 0) {
if ((size = spa_ump_to_midi((const uint32_t **)&body, &body_size, if (size == 0)
data, sizeof(data), &st)) <= 0)
break;
if (first)
snd_seq_ev_clear(&ev); snd_seq_ev_clear(&ev);
if ((size = snd_midi_event_encode(stream->codec, data, size, &ev)) < 0) { if ((s = snd_midi_event_encode(stream->codec, body, body_size, &ev)) < 0) {
spa_log_warn(state->log, "failed to encode event: %s", spa_log_warn(state->log, "failed to encode event: %s",
snd_strerror(size)); snd_strerror(size));
snd_midi_event_reset_encode(stream->codec); snd_midi_event_reset_encode(stream->codec);
first = true; size = 0;
continue; continue;
} }
first = false; body += s;
body_size -= s;
size += s;
if (ev.type == SND_SEQ_EVENT_NONE) if (ev.type == SND_SEQ_EVENT_NONE)
/* this can happen when the event is not complete yet, like /* this can happen when the event is not complete yet, like
* a sysex message and we need to encode some more data. */ * a sysex message and we need to encode some more data. */
@ -913,7 +895,7 @@ static int process_write(struct seq_state *state)
spa_log_warn(state->log, "failed to output event: %s", spa_log_warn(state->log, "failed to output event: %s",
snd_strerror(err)); snd_strerror(err));
} }
first = true; size = 0;
} }
} }
} }

View file

@ -82,6 +82,8 @@ struct seq_port {
struct spa_pod_builder builder; struct spa_pod_builder builder;
struct spa_pod_frame frame; struct spa_pod_frame frame;
uint32_t control_types;
struct spa_audio_info current_format; struct spa_audio_info current_format;
unsigned int have_format:1; unsigned int have_format:1;
unsigned int active:1; unsigned int active:1;

View file

@ -48,6 +48,7 @@ struct card {
unsigned int accessible:1; unsigned int accessible:1;
unsigned int ignored:1; unsigned int ignored:1;
unsigned int emitted:1; unsigned int emitted:1;
unsigned int wireless_disconnected:1;
/* Local SPA object IDs. (Global IDs are produced by PipeWire /* Local SPA object IDs. (Global IDs are produced by PipeWire
* out of this using its registry.) Compress-Offload or PCM * out of this using its registry.) Compress-Offload or PCM
@ -59,6 +60,10 @@ struct card {
* is used because 0 is a valid ALSA card number. */ * is used because 0 is a valid ALSA card number. */
uint32_t pcm_device_id; uint32_t pcm_device_id;
uint32_t compress_offload_device_id; uint32_t compress_offload_device_id;
/* Syspath of the USB interface that has wireless_status (e.g.
* /sys/devices/.../1-5:1.3). Empty string when not applicable. */
char wireless_status_syspath[256];
}; };
static uint32_t calc_pcm_device_id(struct card *card) static uint32_t calc_pcm_device_id(struct card *card)
@ -92,6 +97,8 @@ struct impl {
struct spa_source source; struct spa_source source;
struct spa_source notify; struct spa_source notify;
struct udev_monitor *usb_umonitor;
struct spa_source usb_source;
unsigned int use_acp:1; unsigned int use_acp:1;
unsigned int expose_busy:1; unsigned int expose_busy:1;
int use_ucm; int use_ucm;
@ -353,6 +360,87 @@ static int check_udev_environment(struct udev *udev, const char *devname)
return ret; return ret;
} }
static void check_wireless_status(struct impl *this, struct card *card)
{
char path[PATH_MAX];
char buf[32];
size_t sz;
bool was_disconnected;
if (card->wireless_status_syspath[0] == '\0')
return;
was_disconnected = card->wireless_disconnected;
spa_scnprintf(path, sizeof(path), "%s/wireless_status", card->wireless_status_syspath);
spa_autoptr(FILE) f = fopen(path, "re");
if (f == NULL)
return;
sz = fread(buf, 1, sizeof(buf) - 1, f);
buf[sz] = '\0';
card->wireless_disconnected = spa_strstartswith(buf, "disconnected");
if (card->wireless_disconnected != was_disconnected)
spa_log_info(this->log, "card %u: wireless headset %s",
card->card_nr,
card->wireless_disconnected ? "disconnected" : "connected");
}
static void find_wireless_status(struct impl *this, struct card *card)
{
const char *bus, *parent_syspath, *parent_sysname;
struct udev_device *parent;
struct dirent *entry;
char path[PATH_MAX];
size_t sysname_len;
bus = udev_device_get_property_value(card->udev_device, "ID_BUS");
if (!spa_streq(bus, "usb"))
return;
/* udev_device_get_parent_* returns a borrowed reference owned by the child; do not unref it. */
parent = udev_device_get_parent_with_subsystem_devtype(card->udev_device, "usb", "usb_device");
if (parent == NULL)
return;
parent_syspath = udev_device_get_syspath(parent);
parent_sysname = udev_device_get_sysname(parent);
if (parent_syspath == NULL || parent_sysname == NULL)
return;
sysname_len = strlen(parent_sysname);
spa_autoptr(DIR) dir = opendir(parent_syspath);
if (dir == NULL)
return;
while ((entry = readdir(dir)) != NULL) {
/* USB interface directories are named "<sysname>:<config>.<intf>" */
if (strncmp(entry->d_name, parent_sysname, sysname_len) != 0 ||
entry->d_name[sysname_len] != ':')
continue;
spa_scnprintf(path, sizeof(path), "%s/%s/wireless_status",
parent_syspath, entry->d_name);
if (access(path, R_OK) < 0)
continue;
spa_scnprintf(card->wireless_status_syspath,
sizeof(card->wireless_status_syspath),
"%s/%s", parent_syspath, entry->d_name);
check_wireless_status(this, card);
spa_log_debug(this->log, "card %u: found wireless_status at %s (%s)",
card->card_nr, card->wireless_status_syspath,
card->wireless_disconnected ? "disconnected" : "connected");
return;
}
}
static int check_pcm_device_availability(struct impl *this, struct card *card, static int check_pcm_device_availability(struct impl *this, struct card *card,
int *num_pcm_devices) int *num_pcm_devices)
{ {
@ -538,6 +626,9 @@ static int emit_added_object_info(struct impl *this, struct card *card)
if ((str = udev_device_get_property_value(udev_device, "ACP_PROFILE_SET")) && *str) if ((str = udev_device_get_property_value(udev_device, "ACP_PROFILE_SET")) && *str)
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PROFILE_SET, str); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PROFILE_SET, str);
if ((str = udev_device_get_property_value(udev_device, "ACP_IGNORE_DB")) && *str)
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_IGNORE_DB, str);
if ((str = udev_device_get_property_value(udev_device, "SOUND_CLASS")) && *str) if ((str = udev_device_get_property_value(udev_device, "SOUND_CLASS")) && *str)
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_CLASS, str); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_CLASS, str);
@ -576,9 +667,12 @@ static int emit_added_object_info(struct impl *this, struct card *card)
if (!(str && *str)) { if (!(str && *str)) {
str = udev_device_get_property_value(udev_device, "ID_VENDOR"); str = udev_device_get_property_value(udev_device, "ID_VENDOR");
} else { } else {
char *t = alloca(strlen(str) + 1); size_t slen = strlen(str) + 1;
unescape(str, t); if (slen <= 1024) {
str = t; char *t = alloca(slen);
unescape(str, t);
str = t;
}
} }
} }
if (str && *str) { if (str && *str) {
@ -598,9 +692,12 @@ static int emit_added_object_info(struct impl *this, struct card *card)
if (!(str && *str)) { if (!(str && *str)) {
str = udev_device_get_property_value(udev_device, "ID_MODEL"); str = udev_device_get_property_value(udev_device, "ID_MODEL");
} else { } else {
char *t = alloca(strlen(str) + 1); size_t slen = strlen(str) + 1;
unescape(str, t); if (slen <= 1024) {
str = t; char *t = alloca(slen);
unescape(str, t);
str = t;
}
} }
} }
if (str && *str) if (str && *str)
@ -725,13 +822,17 @@ static bool check_access(struct impl *this, struct card *card)
static void process_card(struct impl *this, enum action action, struct card *card) static void process_card(struct impl *this, enum action action, struct card *card)
{ {
if (card->ignored)
return;
switch (action) { switch (action) {
case ACTION_CHANGE: { case ACTION_CHANGE: {
if (card->ignored)
return;
check_access(this, card); check_access(this, card);
if (card->accessible && !card->emitted) { check_wireless_status(this, card);
bool effective_accessible = card->accessible && !card->wireless_disconnected;
if (effective_accessible && !card->emitted) {
int res = emit_added_object_info(this, card); int res = emit_added_object_info(this, card);
if (res < 0) { if (res < 0) {
if (card->ignored) if (card->ignored)
@ -750,7 +851,7 @@ static void process_card(struct impl *this, enum action action, struct card *car
card->card_nr); card->card_nr);
card->unavailable = false; card->unavailable = false;
} }
} else if (!card->accessible && card->emitted) { } else if (!effective_accessible && card->emitted) {
card->emitted = false; card->emitted = false;
if (card->pcm_device_id != ID_DEVICE_NOT_SUPPORTED) if (card->pcm_device_id != ID_DEVICE_NOT_SUPPORTED)
@ -787,8 +888,11 @@ static void process_udev_device(struct impl *this, enum action action, struct ud
return; return;
card = find_card(this, card_nr); card = find_card(this, card_nr);
if (action == ACTION_CHANGE && !card) if (action == ACTION_CHANGE && !card) {
card = add_card(this, card_nr, udev_device); card = add_card(this, card_nr, udev_device);
if (card)
find_wireless_status(this, card);
}
if (!card) if (!card)
return; return;
@ -915,6 +1019,41 @@ static void impl_on_fd_events(struct spa_source *source)
udev_device_unref(udev_device); udev_device_unref(udev_device);
} }
static void impl_on_usb_events(struct spa_source *source)
{
struct impl *this = source->data;
struct udev_device *udev_device;
const char *action, *syspath;
unsigned int i;
udev_device = udev_monitor_receive_device(this->usb_umonitor);
if (udev_device == NULL)
return;
if ((action = udev_device_get_action(udev_device)) == NULL)
action = "change";
if (!spa_streq(action, "change"))
goto done;
syspath = udev_device_get_syspath(udev_device);
if (syspath == NULL)
goto done;
for (i = 0; i < this->n_cards; i++) {
struct card *card = &this->cards[i];
if (card->wireless_status_syspath[0] == '\0')
continue;
if (!spa_streq(card->wireless_status_syspath, syspath))
continue;
spa_log_debug(this->log, "wireless_status change for card %u", card->card_nr);
process_card(this, ACTION_CHANGE, card);
}
done:
udev_device_unref(udev_device);
}
static int start_monitor(struct impl *this) static int start_monitor(struct impl *this)
{ {
int res; int res;
@ -938,6 +1077,20 @@ static int start_monitor(struct impl *this)
spa_log_debug(this->log, "monitor %p", this->umonitor); spa_log_debug(this->log, "monitor %p", this->umonitor);
spa_loop_add_source(this->main_loop, &this->source); spa_loop_add_source(this->main_loop, &this->source);
this->usb_umonitor = udev_monitor_new_from_netlink(this->udev, "udev");
if (this->usb_umonitor != NULL) {
udev_monitor_filter_add_match_subsystem_devtype(this->usb_umonitor,
"usb", "usb_interface");
udev_monitor_enable_receiving(this->usb_umonitor);
this->usb_source.func = impl_on_usb_events;
this->usb_source.data = this;
this->usb_source.fd = udev_monitor_get_fd(this->usb_umonitor);
this->usb_source.mask = SPA_IO_IN | SPA_IO_ERR;
spa_loop_add_source(this->main_loop, &this->usb_source);
}
if ((res = start_inotify(this)) < 0) if ((res = start_inotify(this)) < 0)
return res; return res;
@ -955,6 +1108,12 @@ static int stop_monitor(struct impl *this)
udev_monitor_unref(this->umonitor); udev_monitor_unref(this->umonitor);
this->umonitor = NULL; this->umonitor = NULL;
if (this->usb_umonitor != NULL) {
spa_loop_remove_source(this->main_loop, &this->usb_source);
udev_monitor_unref(this->usb_umonitor);
this->usb_umonitor = NULL;
}
stop_inotify(this); stop_inotify(this);
return 0; return 0;

View file

@ -40,7 +40,7 @@ struct compress_offload_api_context* compress_offload_api_open(int card_nr, int
snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card_nr, device_nr); snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card_nr, device_nr);
context->fd = open(fn, O_WRONLY); context->fd = open(fn, O_WRONLY | O_CLOEXEC);
if (context->fd < 0) { if (context->fd < 0) {
spa_log_error(context->log, "could not open device \"%s\": %s (%d)", fn, strerror(errno), errno); spa_log_error(context->log, "could not open device \"%s\": %s (%d)", fn, strerror(errno), errno);
goto error; goto error;

View file

@ -0,0 +1,70 @@
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of the
# License, or (at your option) any later version.
#
# PulseAudio is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
; USB gaming headset.
; These headsets usually have two output devices. The first one is meant
; for general audio, and the second one is meant for chat. There is also
; a single input device for chat.
; The purpose of this unusual design is to provide separate volume
; controls for voice and other audio, which can be useful in gaming.
;
; Works with:
; JBL Quantum 810 Wireless
; JBL Quantum One
;
; Based on usb-gaming-headset.conf.
;
; See default.conf for an explanation on the directives used here.
[General]
auto-profiles = yes
[Mapping mono-chat-output]
description-key = gaming-headset-chat
device-strings = hw:%f,1,0
channel-map = mono
paths-output = usb-gaming-headset-output-mono
intended-roles = phone
[Mapping stereo-chat-output]
description-key = gaming-headset-chat
device-strings = hw:%f,1,0
channel-map = left,right
paths-output = usb-gaming-headset-output-stereo
intended-roles = phone
[Mapping mono-chat-input]
description-key = gaming-headset-chat
device-strings = hw:%f,0,0
channel-map = mono
paths-input = usb-gaming-headset-input
intended-roles = phone
[Mapping stereo-game-output]
description-key = gaming-headset-game
device-strings = hw:%f,0,0
channel-map = left,right
paths-output = usb-gaming-headset-output-stereo
direction = output
[Profile output:mono-chat+output:stereo-game+input:mono-chat]
output-mappings = mono-chat-output stereo-game-output
input-mappings = mono-chat-input
priority = 5100
[Profile output:stereo-game+output:stereo-chat+input:mono-chat]
output-mappings = stereo-game-output stereo-chat-output
input-mappings = mono-chat-input
priority = 5100

View file

@ -35,6 +35,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioadapter");
#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1) #define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1)
#define MAX_RETRY 64 #define MAX_RETRY 64
#define MAX_BLOCKS 4096
/** \cond */ /** \cond */
@ -63,7 +64,7 @@ struct impl {
struct spa_node *convert; struct spa_node *convert;
struct spa_hook convert_listener; struct spa_hook convert_listener;
uint64_t convert_port_flags; uint64_t convert_port_flags;
char *convertname; char convertname[64];
uint32_t n_buffers; uint32_t n_buffers;
struct spa_buffer **buffers; struct spa_buffer **buffers;
@ -356,6 +357,7 @@ static void emit_node_info(struct impl *this, bool full)
if (this->info.props) if (this->info.props)
n_items = this->info.props->n_items; n_items = this->info.props->n_items;
n_items = SPA_MIN(n_items, 1024u);
items = alloca((n_items + 2) * sizeof(struct spa_dict_item)); items = alloca((n_items + 2) * sizeof(struct spa_dict_item));
for (i = 0; i < n_items; i++) for (i = 0; i < n_items; i++)
items[i] = this->info.props->items[i]; items[i] = this->info.props->items[i];
@ -513,6 +515,9 @@ static int negotiate_buffers(struct impl *this)
align = SPA_MAX(align, this->max_align); align = SPA_MAX(align, this->max_align);
if (blocks > MAX_BLOCKS)
return -ENOMEM;
datas = alloca(sizeof(struct spa_data) * blocks); datas = alloca(sizeof(struct spa_data) * blocks);
memset(datas, 0, sizeof(struct spa_data) * blocks); memset(datas, 0, sizeof(struct spa_data) * blocks);
aligns = alloca(sizeof(uint32_t) * blocks); aligns = alloca(sizeof(uint32_t) * blocks);
@ -1086,6 +1091,8 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
spa_log_debug(this->log, "%p: suspending", this); spa_log_debug(this->log, "%p: suspending", this);
break; break;
case SPA_NODE_COMMAND_Pause: case SPA_NODE_COMMAND_Pause:
if ((res = negotiate_format(this)) < 0)
return res;
spa_log_debug(this->log, "%p: pausing", this); spa_log_debug(this->log, "%p: pausing", this);
break; break;
case SPA_NODE_COMMAND_Flush: case SPA_NODE_COMMAND_Flush:
@ -1810,6 +1817,9 @@ impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
spa_return_val_if_fail(this != NULL, -EINVAL); spa_return_val_if_fail(this != NULL, -EINVAL);
if (SPA_DIRECTION_OUTPUT != this->direction)
port_id++;
return spa_node_port_reuse_buffer(this->target, port_id, buffer_id); return spa_node_port_reuse_buffer(this->target, port_id, buffer_id);
} }
@ -1937,11 +1947,12 @@ static int load_converter(struct impl *this, const struct spa_dict *info,
struct spa_dict_item *items; struct spa_dict_item *items;
struct spa_dict cinfo; struct spa_dict cinfo;
char direction[16]; char direction[16];
uint32_t i; uint32_t i, n_items;
items = alloca((info->n_items + 1) * sizeof(struct spa_dict_item)); n_items = SPA_MIN(info->n_items, 1024u);
items = alloca((n_items + 1) * sizeof(struct spa_dict_item));
cinfo = SPA_DICT(items, 0); cinfo = SPA_DICT(items, 0);
for (i = 0; i < info->n_items; i++) for (i = 0; i < n_items; i++)
items[cinfo.n_items++] = info->items[i]; items[cinfo.n_items++] = info->items[i];
snprintf(direction, sizeof(direction), "%s", snprintf(direction, sizeof(direction), "%s",
@ -1985,7 +1996,7 @@ static int load_converter(struct impl *this, const struct spa_dict *info,
this->hnd_convert = hnd_convert; this->hnd_convert = hnd_convert;
this->convert = iface_conv; this->convert = iface_conv;
this->unload_handle = unload_handle; this->unload_handle = unload_handle;
this->convertname = strdup(factory_name); snprintf(this->convertname, sizeof(this->convertname), "%s", factory_name);
return 0; return 0;
} }
@ -2118,7 +2129,6 @@ static int impl_clear(struct spa_handle *handle)
spa_handle_clear(this->hnd_convert); spa_handle_clear(this->hnd_convert);
free(this->hnd_convert); free(this->hnd_convert);
} }
free(this->convertname);
} }
clear_buffers(this); clear_buffers(this);

View file

@ -7,15 +7,18 @@
#include <stdio.h> #include <stdio.h>
#include <limits.h> #include <limits.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <math.h>
#include <spa/support/plugin.h> #include <spa/support/plugin.h>
#include <spa/support/cpu.h> #include <spa/support/cpu.h>
#include <spa/support/loop.h> #include <spa/support/loop.h>
#include <spa/support/log.h> #include <spa/support/log.h>
#include <spa/support/plugin-loader.h> #include <spa/support/plugin-loader.h>
#include <spa/utils/overflow.h>
#include <spa/utils/result.h> #include <spa/utils/result.h>
#include <spa/utils/list.h> #include <spa/utils/list.h>
#include <spa/utils/json.h> #include <spa/utils/json.h>
#include <spa/utils/json-builder.h>
#include <spa/utils/names.h> #include <spa/utils/names.h>
#include <spa/utils/string.h> #include <spa/utils/string.h>
#include <spa/utils/ratelimit.h> #include <spa/utils/ratelimit.h>
@ -100,6 +103,7 @@ struct props {
unsigned int mix_disabled:1; unsigned int mix_disabled:1;
unsigned int resample_disabled:1; unsigned int resample_disabled:1;
unsigned int resample_quality; unsigned int resample_quality;
struct resample_config resample_config;
double rate; double rate;
char wav_path[512]; char wav_path[512];
unsigned int lock_volumes:1; unsigned int lock_volumes:1;
@ -122,6 +126,7 @@ static void props_reset(struct props *props)
props->mix_disabled = false; props->mix_disabled = false;
props->resample_disabled = false; props->resample_disabled = false;
props->resample_quality = RESAMPLE_DEFAULT_QUALITY; props->resample_quality = RESAMPLE_DEFAULT_QUALITY;
spa_zero(props->resample_config);
props->rate = 1.0; props->rate = 1.0;
spa_zero(props->wav_path); spa_zero(props->wav_path);
props->lock_volumes = false; props->lock_volumes = false;
@ -266,6 +271,7 @@ struct impl {
struct spa_list active_graphs; struct spa_list active_graphs;
struct filter_graph graphs[MAX_GRAPH]; struct filter_graph graphs[MAX_GRAPH];
struct spa_process_latency_info latency; struct spa_process_latency_info latency;
char *graph_descs[MAX_GRAPH];
int in_filter_props; int in_filter_props;
int filter_props_count; int filter_props_count;
@ -720,6 +726,34 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_params, SPA_POD_Bool(true)); SPA_PROP_INFO_params, SPA_POD_Bool(true));
break; break;
case 19: case 19:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.center-level"),
SPA_PROP_INFO_description, SPA_POD_String("Center up/downmix level"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
this->mix.center_level, 0.0, 10.0),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 20:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.surround-level"),
SPA_PROP_INFO_description, SPA_POD_String("Surround up/downmix level"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
this->mix.surround_level, 0.0, 10.0),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 21:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.lfe-level"),
SPA_PROP_INFO_description, SPA_POD_String("LFE up/downmix level"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
this->mix.lfe_level, 0.0, 10.0),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 22:
*param = spa_pod_builder_add_object(b, *param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id, SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.hilbert-taps"), SPA_PROP_INFO_name, SPA_POD_String("channelmix.hilbert-taps"),
@ -728,7 +762,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
this->mix.hilbert_taps, 0, MAX_TAPS), this->mix.hilbert_taps, 0, MAX_TAPS),
SPA_PROP_INFO_params, SPA_POD_Bool(true)); SPA_PROP_INFO_params, SPA_POD_Bool(true));
break; break;
case 20: case 23:
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_PropInfo, id); spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(b, spa_pod_builder_add(b,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix-method"), SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix-method"),
@ -747,14 +781,14 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_pop(b, &f[1]); spa_pod_builder_pop(b, &f[1]);
*param = spa_pod_builder_pop(b, &f[0]); *param = spa_pod_builder_pop(b, &f[0]);
break; break;
case 21: case 24:
*param = spa_pod_builder_add_object(b, *param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id, SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_rate), SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_rate),
SPA_PROP_INFO_description, SPA_POD_String("Rate scaler"), SPA_PROP_INFO_description, SPA_POD_String("Rate scaler"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Double(p->rate, 0.0, 10.0)); SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Double(p->rate, 0.0, 10.0));
break; break;
case 22: case 25:
*param = spa_pod_builder_add_object(b, *param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id, SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_quality), SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_quality),
@ -763,7 +797,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->resample_quality, 0, 14), SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->resample_quality, 0, 14),
SPA_PROP_INFO_params, SPA_POD_Bool(true)); SPA_PROP_INFO_params, SPA_POD_Bool(true));
break; break;
case 23: case 26:
*param = spa_pod_builder_add_object(b, *param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id, SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("resample.disable"), SPA_PROP_INFO_name, SPA_POD_String("resample.disable"),
@ -771,7 +805,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->resample_disabled), SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->resample_disabled),
SPA_PROP_INFO_params, SPA_POD_Bool(true)); SPA_PROP_INFO_params, SPA_POD_Bool(true));
break; break;
case 24: case 27:
*param = spa_pod_builder_add_object(b, *param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id, SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("dither.noise"), SPA_PROP_INFO_name, SPA_POD_String("dither.noise"),
@ -779,7 +813,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dir[1].conv.noise_bits, 0, 16), SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dir[1].conv.noise_bits, 0, 16),
SPA_PROP_INFO_params, SPA_POD_Bool(true)); SPA_PROP_INFO_params, SPA_POD_Bool(true));
break; break;
case 25: case 28:
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_PropInfo, id); spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(b, spa_pod_builder_add(b,
SPA_PROP_INFO_name, SPA_POD_String("dither.method"), SPA_PROP_INFO_name, SPA_POD_String("dither.method"),
@ -797,7 +831,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_pop(b, &f[1]); spa_pod_builder_pop(b, &f[1]);
*param = spa_pod_builder_pop(b, &f[0]); *param = spa_pod_builder_pop(b, &f[0]);
break; break;
case 26: case 29:
*param = spa_pod_builder_add_object(b, *param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id, SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("debug.wav-path"), SPA_PROP_INFO_name, SPA_POD_String("debug.wav-path"),
@ -805,7 +839,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_String(p->wav_path), SPA_PROP_INFO_type, SPA_POD_String(p->wav_path),
SPA_PROP_INFO_params, SPA_POD_Bool(true)); SPA_PROP_INFO_params, SPA_POD_Bool(true));
break; break;
case 27: case 30:
*param = spa_pod_builder_add_object(b, *param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id, SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.lock-volumes"), SPA_PROP_INFO_name, SPA_POD_String("channelmix.lock-volumes"),
@ -813,7 +847,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->lock_volumes), SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->lock_volumes),
SPA_PROP_INFO_params, SPA_POD_Bool(true)); SPA_PROP_INFO_params, SPA_POD_Bool(true));
break; break;
case 28: case 31:
*param = spa_pod_builder_add_object(b, *param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id, SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.disable"), SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.disable"),
@ -821,7 +855,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->filter_graph_disabled), SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->filter_graph_disabled),
SPA_PROP_INFO_params, SPA_POD_Bool(true)); SPA_PROP_INFO_params, SPA_POD_Bool(true));
break; break;
case 29: case 32:
*param = spa_pod_builder_add_object(b, *param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id, SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.N"), SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.N"),
@ -832,7 +866,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
default: default:
if (this->filter_graph[0] && this->filter_graph[0]->graph) { if (this->filter_graph[0] && this->filter_graph[0]->graph) {
return spa_filter_graph_enum_prop_info(this->filter_graph[0]->graph, return spa_filter_graph_enum_prop_info(this->filter_graph[0]->graph,
index - 30, b, param); index - 33, b, param);
} }
return 0; return 0;
} }
@ -844,6 +878,7 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
{ {
struct props *p = &this->props; struct props *p = &this->props;
struct spa_pod_frame f[2]; struct spa_pod_frame f[2];
struct filter_graph *g;
switch (index) { switch (index) {
case 0: case 0:
@ -898,6 +933,12 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_float(b, this->mix.rear_delay); spa_pod_builder_float(b, this->mix.rear_delay);
spa_pod_builder_string(b, "channelmix.stereo-widen"); spa_pod_builder_string(b, "channelmix.stereo-widen");
spa_pod_builder_float(b, this->mix.widen); spa_pod_builder_float(b, this->mix.widen);
spa_pod_builder_string(b, "channelmix.center-level");
spa_pod_builder_float(b, this->mix.center_level);
spa_pod_builder_string(b, "channelmix.surround-level");
spa_pod_builder_float(b, this->mix.surround_level);
spa_pod_builder_string(b, "channelmix.lfe-level");
spa_pod_builder_float(b, this->mix.lfe_level);
spa_pod_builder_string(b, "channelmix.hilbert-taps"); spa_pod_builder_string(b, "channelmix.hilbert-taps");
spa_pod_builder_int(b, this->mix.hilbert_taps); spa_pod_builder_int(b, this->mix.hilbert_taps);
spa_pod_builder_string(b, "channelmix.upmix-method"); spa_pod_builder_string(b, "channelmix.upmix-method");
@ -916,8 +957,12 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_bool(b, p->lock_volumes); spa_pod_builder_bool(b, p->lock_volumes);
spa_pod_builder_string(b, "audioconvert.filter-graph.disable"); spa_pod_builder_string(b, "audioconvert.filter-graph.disable");
spa_pod_builder_bool(b, p->filter_graph_disabled); spa_pod_builder_bool(b, p->filter_graph_disabled);
spa_pod_builder_string(b, "audioconvert.filter-graph"); spa_list_for_each(g, &this->active_graphs, link) {
spa_pod_builder_string(b, ""); char key[64];
snprintf(key, sizeof(key), "audioconvert.filter-graph.%d", g->order);
spa_pod_builder_string(b, key);
spa_pod_builder_string(b, this->graph_descs[g->order]);
}
spa_pod_builder_pop(b, &f[1]); spa_pod_builder_pop(b, &f[1]);
*param = spa_pod_builder_pop(b, &f[0]); *param = spa_pod_builder_pop(b, &f[0]);
break; break;
@ -951,7 +996,7 @@ static int impl_node_enum_params(void *object, int seq,
struct impl *this = object; struct impl *this = object;
struct spa_pod *param; struct spa_pod *param;
struct spa_pod_builder b = { 0 }; struct spa_pod_builder b = { 0 };
uint8_t buffer[4096]; uint8_t buffer[16384];
struct spa_result_node_params result; struct spa_result_node_params result;
uint32_t count = 0; uint32_t count = 0;
int res = 0; int res = 0;
@ -1017,9 +1062,8 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
this->io_position = data; this->io_position = data;
if (this->io_position && this->io_clock && if (this->io_position && this->io_clock &&
this->io_position->clock.target_rate.denom != this->io_clock->target_rate.denom && this->io_position->clock.target_rate.denom != this->io_clock->target_rate.denom) {
!this->props.resample_disabled) { spa_log_debug(this->log, "driver %d changed rate:%u -> %u", this->io_position->clock.id,
spa_log_warn(this->log, "driver %d changed rate:%u -> %u", this->io_position->clock.id,
this->io_clock->target_rate.denom, this->io_clock->target_rate.denom,
this->io_position->clock.target_rate.denom); this->io_position->clock.target_rate.denom);
@ -1241,24 +1285,28 @@ static int ensure_tmp(struct impl *this)
uint32_t maxsize = this->maxsize, maxports = this->maxports; uint32_t maxsize = this->maxsize, maxports = this->maxports;
uint32_t i; uint32_t i;
float *empty, *scratch, *tmp[2]; float *empty, *scratch, *tmp[2];
size_t alloc_size;
if (spa_overflow_add((size_t)maxsize, (size_t)MAX_ALIGN, &alloc_size))
return -ENOMEM;
if (maxsize > this->scratch_size) { if (maxsize > this->scratch_size) {
spa_log_info(this->log, "resize tmp %d -> %d", this->scratch_size, maxsize); spa_log_info(this->log, "resize tmp %d -> %d", this->scratch_size, maxsize);
if ((empty = realloc(this->empty, maxsize + MAX_ALIGN)) != NULL) if ((empty = realloc(this->empty, alloc_size)) != NULL)
this->empty = empty; this->empty = empty;
if ((scratch = realloc(this->scratch, maxsize + MAX_ALIGN)) != NULL) if ((scratch = realloc(this->scratch, alloc_size)) != NULL)
this->scratch = scratch; this->scratch = scratch;
if (empty == NULL || scratch == NULL) { if (empty == NULL || scratch == NULL) {
free_tmp(this); free_tmp(this);
return -ENOMEM; return -ENOMEM;
} }
memset(this->empty, 0, maxsize + MAX_ALIGN); memset(this->empty, 0, alloc_size);
for (i = 0; i < this->scratch_ports; i++) { for (i = 0; i < this->scratch_ports; i++) {
if ((tmp[0] = realloc(this->tmp[0][i], maxsize + MAX_ALIGN)) != NULL) if ((tmp[0] = realloc(this->tmp[0][i], alloc_size)) != NULL)
this->tmp[0][i] = tmp[0]; this->tmp[0][i] = tmp[0];
if ((tmp[1] = realloc(this->tmp[1][i], maxsize + MAX_ALIGN)) != NULL) if ((tmp[1] = realloc(this->tmp[1][i], alloc_size)) != NULL)
this->tmp[1][i] = tmp[1]; this->tmp[1][i] = tmp[1];
if (tmp[0] == NULL || tmp[1] == NULL) { if (tmp[0] == NULL || tmp[1] == NULL) {
free_tmp(this); free_tmp(this);
@ -1273,16 +1321,16 @@ static int ensure_tmp(struct impl *this)
spa_log_info(this->log, "resize ports %d -> %d", this->scratch_ports, maxports); spa_log_info(this->log, "resize ports %d -> %d", this->scratch_ports, maxports);
for (i = this->scratch_ports; i < maxports; i++) { for (i = this->scratch_ports; i < maxports; i++) {
if ((tmp[0] = malloc(maxsize + MAX_ALIGN)) != NULL) if ((tmp[0] = malloc(alloc_size)) != NULL)
this->tmp[0][i] = tmp[0]; this->tmp[0][i] = tmp[0];
if ((tmp[1] = malloc(maxsize + MAX_ALIGN)) != NULL) if ((tmp[1] = malloc(alloc_size)) != NULL)
this->tmp[1][i] = tmp[1]; this->tmp[1][i] = tmp[1];
if (tmp[0] == NULL || tmp[1] == NULL) { if (tmp[0] == NULL || tmp[1] == NULL) {
free_tmp(this); free_tmp(this);
return -ENOMEM; return -ENOMEM;
} }
this->tmp_datas[0][i] = SPA_PTR_ALIGN(this->tmp[0][i], MAX_ALIGN, void); this->tmp_datas[0][i] = SPA_PTR_ALIGN(this->tmp[0][i], MAX_ALIGN, void);
this->tmp_datas[0][i] = SPA_PTR_ALIGN(this->tmp[0][i], MAX_ALIGN, void); this->tmp_datas[1][i] = SPA_PTR_ALIGN(this->tmp[1][i], MAX_ALIGN, void);
} }
this->scratch_ports = maxports; this->scratch_ports = maxports;
} }
@ -1342,6 +1390,14 @@ static int do_sync_filter_graph(struct spa_loop *loop, bool async, uint32_t seq,
return 0; return 0;
} }
static void sync_filter_graph(struct impl *impl)
{
if (impl->data_loop)
spa_loop_locked(impl->data_loop, do_sync_filter_graph, 0, NULL, 0, impl);
else
do_sync_filter_graph(NULL, false, 0, NULL, 0, impl);
}
static void clean_filter_handles(struct impl *impl, bool force) static void clean_filter_handles(struct impl *impl, bool force)
{ {
struct filter_graph *g, *t; struct filter_graph *g, *t;
@ -1399,6 +1455,7 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order)
g->removing = true; g->removing = true;
spa_log_info(impl->log, "removing filter-graph order:%d", order); spa_log_info(impl->log, "removing filter-graph order:%d", order);
} }
free(impl->graph_descs[order]);
} }
if (graph != NULL && graph[0] != '\0') { if (graph != NULL && graph[0] != '\0') {
@ -1424,12 +1481,14 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order)
spa_list_remove(&pending->link); spa_list_remove(&pending->link);
insert_graph(&impl->active_graphs, pending); insert_graph(&impl->active_graphs, pending);
impl->graph_descs[order] = spa_json_builder_reformat(graph, 0);
spa_log_info(impl->log, "loading filter-graph order:%d", order); spa_log_info(impl->log, "loading filter-graph order:%d", order);
} }
if (impl->setup) if (impl->setup)
res = setup_filter_graphs(impl, false); res = setup_filter_graphs(impl, false);
spa_loop_locked(impl->data_loop, do_sync_filter_graph, 0, NULL, 0, impl); sync_filter_graph(impl);
if (impl->in_filter_props == 0) if (impl->in_filter_props == 0)
clean_filter_handles(impl, false); clean_filter_handles(impl, false);
@ -1470,6 +1529,12 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
spa_atof(s, &this->mix.rear_delay); spa_atof(s, &this->mix.rear_delay);
else if (spa_streq(k, "channelmix.stereo-widen")) else if (spa_streq(k, "channelmix.stereo-widen"))
spa_atof(s, &this->mix.widen); spa_atof(s, &this->mix.widen);
else if (spa_streq(k, "channelmix.center-level"))
spa_atof(s, &this->mix.center_level);
else if (spa_streq(k, "channelmix.surround-level"))
spa_atof(s, &this->mix.surround_level);
else if (spa_streq(k, "channelmix.lfe-level"))
spa_atof(s, &this->mix.lfe_level);
else if (spa_streq(k, "channelmix.hilbert-taps")) else if (spa_streq(k, "channelmix.hilbert-taps"))
spa_atou32(s, &this->mix.hilbert_taps, 0); spa_atou32(s, &this->mix.hilbert_taps, 0);
else if (spa_streq(k, "channelmix.upmix-method")) else if (spa_streq(k, "channelmix.upmix-method"))
@ -1478,6 +1543,16 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
this->props.resample_quality = atoi(s); this->props.resample_quality = atoi(s);
else if (spa_streq(k, "resample.disable")) else if (spa_streq(k, "resample.disable"))
this->props.resample_disabled = spa_atob(s); this->props.resample_disabled = spa_atob(s);
else if (spa_streq(k, "resample.window"))
this->props.resample_config.window = resample_window_from_label(s);
else if (spa_streq(k, "resample.cutoff"))
spa_atod(s, &this->props.resample_config.cutoff);
else if (spa_streq(k, "resample.n-taps"))
spa_atou32(s, &this->props.resample_config.n_taps, 0);
else if (spa_strstartswith(k, "resample.param.")) {
uint32_t idx = resample_param_from_label(k+strlen("resample.param."));
spa_atod(s, &this->props.resample_config.params[idx]);
}
else if (spa_streq(k, "dither.noise")) else if (spa_streq(k, "dither.noise"))
spa_atou32(s, &this->dir[1].conv.noise_bits, 0); spa_atou32(s, &this->dir[1].conv.noise_bits, 0);
else if (spa_streq(k, "dither.method")) else if (spa_streq(k, "dither.method"))
@ -1488,6 +1563,10 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
} }
else if (spa_streq(k, "channelmix.lock-volumes")) else if (spa_streq(k, "channelmix.lock-volumes"))
this->props.lock_volumes = spa_atob(s); this->props.lock_volumes = spa_atob(s);
else if (spa_streq(k, "audioconvert.filter-graph.disable")) {
if (!*disable_filter)
*disable_filter = spa_atob(s);
}
else if (spa_strstartswith(k, "audioconvert.filter-graph.")) { else if (spa_strstartswith(k, "audioconvert.filter-graph.")) {
int order = atoi(k + strlen("audioconvert.filter-graph.")); int order = atoi(k + strlen("audioconvert.filter-graph."));
if ((res = load_filter_graph(this, s, order)) < 0) { if ((res = load_filter_graph(this, s, order)) < 0) {
@ -1495,10 +1574,6 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
order, spa_strerror(res)); order, spa_strerror(res));
} }
} }
else if (spa_streq(k, "audioconvert.filter-graph.disable")) {
if (!*disable_filter)
*disable_filter = spa_atob(s);
}
else else
return 0; return 0;
return 1; return 1;
@ -1617,7 +1692,7 @@ static struct spa_pod *generate_ramp_seq(struct impl *this, struct volume_ramp_p
while (1) { while (1) {
float pos = (samples == 0) ? end : float pos = (samples == 0) ? end :
SPA_CLAMP(start + (end - start) * offs / samples, SPA_CLAMPF(start + (end - start) * offs / samples,
SPA_MIN(start, end), SPA_MAX(start, end)); SPA_MIN(start, end), SPA_MAX(start, end));
float vas = get_volume_at_scale(vrp, pos); float vas = get_volume_at_scale(vrp, pos);
@ -2095,7 +2170,7 @@ static int setup_in_convert(struct impl *this)
return res; return res;
spa_log_debug(this->log, "%p: got converter features %08x:%08x passthrough:%d remap:%d %s", this, spa_log_debug(this->log, "%p: got converter features %08x:%08x passthrough:%d remap:%d %s", this,
this->cpu_flags, in->conv.cpu_flags, in->conv.is_passthrough, this->cpu_flags, in->conv.func_cpu_flags, in->conv.is_passthrough,
remap, in->conv.func_name); remap, in->conv.func_name);
return 0; return 0;
@ -2190,10 +2265,12 @@ static void set_volume(struct impl *this)
static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position) static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position)
{ {
uint32_t i, idx = 0; uint32_t i;
char buf[8]; char buf[8];
struct spa_strbuf b;
spa_strbuf_init(&b, str, len);
for (i = 0; i < channels; i++) for (i = 0; i < channels; i++)
idx += snprintf(str + idx, len - idx, "%s%s", i == 0 ? "" : " ", spa_strbuf_append(&b, "%s%s", i == 0 ? "" : " ",
spa_type_audio_channel_make_short_name(position[i], spa_type_audio_channel_make_short_name(position[i],
buf, sizeof(buf), "UNK")); buf, sizeof(buf), "UNK"));
return str; return str;
@ -2252,7 +2329,7 @@ static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *posi
set_volume(this); set_volume(this);
spa_log_debug(this->log, "%p: got channelmix features %08x:%08x flags:%08x %s", spa_log_debug(this->log, "%p: got channelmix features %08x:%08x flags:%08x %s",
this, this->cpu_flags, this->mix.cpu_flags, this, this->cpu_flags, this->mix.func_cpu_flags,
this->mix.flags, this->mix.func_name); this->mix.flags, this->mix.func_name);
return 0; return 0;
} }
@ -2289,6 +2366,7 @@ static int setup_resample(struct impl *this)
this->resample.o_rate = out->format.info.raw.rate; this->resample.o_rate = out->format.info.raw.rate;
this->resample.log = this->log; this->resample.log = this->log;
this->resample.quality = this->props.resample_quality; this->resample.quality = this->props.resample_quality;
this->resample.config = this->props.resample_config;
this->resample.cpu_flags = this->cpu_flags; this->resample.cpu_flags = this->cpu_flags;
this->rate_adjust = this->props.rate != 1.0; this->rate_adjust = this->props.rate != 1.0;
@ -2299,7 +2377,7 @@ static int setup_resample(struct impl *this)
res = resample_native_init(&this->resample); res = resample_native_init(&this->resample);
spa_log_debug(this->log, "%p: got resample features %08x:%08x %s", spa_log_debug(this->log, "%p: got resample features %08x:%08x %s",
this, this->cpu_flags, this->resample.cpu_flags, this, this->cpu_flags, this->resample.func_cpu_flags,
this->resample.func_name); this->resample.func_name);
return res; return res;
} }
@ -2391,7 +2469,7 @@ static int setup_out_convert(struct impl *this)
spa_log_debug(this->log, "%p: got converter features %08x:%08x quant:%d:%d" spa_log_debug(this->log, "%p: got converter features %08x:%08x quant:%d:%d"
" passthrough:%d remap:%d %s", this, " passthrough:%d remap:%d %s", this,
this->cpu_flags, out->conv.cpu_flags, out->conv.method, this->cpu_flags, out->conv.func_cpu_flags, out->conv.method,
out->conv.noise_bits, out->conv.is_passthrough, remap, out->conv.func_name); out->conv.noise_bits, out->conv.is_passthrough, remap, out->conv.func_name);
return 0; return 0;
@ -2425,7 +2503,7 @@ static uint32_t resample_update_rate_match(struct impl *this, bool passthrough,
match_size = resample_out_len(&this->resample, size); match_size = resample_out_len(&this->resample, size);
} }
delay = (uint32_t)round(fdelay); delay = lround(fdelay);
delay_frac = (int32_t)((fdelay - delay) * 1e9); delay_frac = (int32_t)((fdelay - delay) * 1e9);
} }
match_size -= SPA_MIN(match_size, queued); match_size -= SPA_MIN(match_size, queued);
@ -2532,6 +2610,8 @@ static int setup_convert(struct impl *this)
this->setup = true; this->setup = true;
this->recalc = true; this->recalc = true;
sync_filter_graph(this);
return 0; return 0;
} }
@ -2548,6 +2628,7 @@ static void reset_node(struct impl *this)
resample_reset(&this->resample); resample_reset(&this->resample);
this->in_offset = 0; this->in_offset = 0;
this->out_offset = 0; this->out_offset = 0;
this->setup = false;
} }
static int impl_node_send_command(void *object, const struct spa_command *command) static int impl_node_send_command(void *object, const struct spa_command *command)
@ -2568,9 +2649,11 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
break; break;
case SPA_NODE_COMMAND_Suspend: case SPA_NODE_COMMAND_Suspend:
reset_node(this); reset_node(this);
this->setup = false; this->started = false;
SPA_FALLTHROUGH; break;
case SPA_NODE_COMMAND_Pause: case SPA_NODE_COMMAND_Pause:
if ((res = setup_convert(this)) < 0)
return res;
this->started = false; this->started = false;
break; break;
case SPA_NODE_COMMAND_Flush: case SPA_NODE_COMMAND_Flush:
@ -4211,6 +4294,7 @@ static void free_dir(struct dir *dir)
static int impl_clear(struct spa_handle *handle) static int impl_clear(struct spa_handle *handle)
{ {
struct impl *this; struct impl *this;
int i;
spa_return_val_if_fail(handle != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL);
@ -4222,6 +4306,10 @@ static int impl_clear(struct spa_handle *handle)
free_tmp(this); free_tmp(this);
clean_filter_handles(this, true); clean_filter_handles(this, true);
for (i = 0; i < MAX_GRAPH; i++) {
if (this->graph_descs[i])
free(this->graph_descs[i]);
}
if (this->resample.free) if (this->resample.free)
resample_free(&this->resample); resample_free(&this->resample);
@ -4276,18 +4364,13 @@ impl_init(const struct spa_handle_factory *factory,
struct filter_graph *g = &this->graphs[i]; struct filter_graph *g = &this->graphs[i];
g->impl = this; g->impl = this;
spa_list_append(&this->free_graphs, &g->link); spa_list_append(&this->free_graphs, &g->link);
this->graph_descs[i] = NULL;
} }
this->rate_limit.interval = 2 * SPA_NSEC_PER_SEC; this->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
this->rate_limit.burst = 1; this->rate_limit.burst = 1;
this->mix.options = CHANNELMIX_OPTION_UPMIX | CHANNELMIX_OPTION_MIX_LFE; channelmix_reset(&this->mix);
this->mix.upmix = CHANNELMIX_UPMIX_NONE;
this->mix.log = this->log;
this->mix.lfe_cutoff = 0.0f;
this->mix.fc_cutoff = 0.0f;
this->mix.rear_delay = 0.0f;
this->mix.widen = 0.0f;
for (i = 0; info && i < info->n_items; i++) { for (i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key; const char *k = info->items[i].key;

View file

@ -51,9 +51,9 @@ static void run_test1(const char *name, const char *impl, bool in_packed, bool o
void *op[n_channels]; void *op[n_channels];
struct timespec ts; struct timespec ts;
uint64_t count, t1, t2; uint64_t count, t1, t2;
struct convert conv; struct convert conv = {
.n_channels = n_channels,
conv.n_channels = n_channels; };
for (j = 0; j < n_channels; j++) { for (j = 0; j < n_channels; j++) {
ip[j] = &samp_in[j * n_samples * 4]; ip[j] = &samp_in[j * n_samples * 4];

View file

@ -39,7 +39,7 @@ static const int in_rates[] = { 44100, 44100, 48000, 96000, 22050, 96000 };
static const int out_rates[] = { 44100, 48000, 44100, 48000, 48000, 44100 }; static const int out_rates[] = { 44100, 48000, 44100, 48000, 48000, 44100 };
#define MAX_RESAMPLER 5 #define MAX_RESAMPLER 10
#define MAX_SIZES SPA_N_ELEMENTS(sample_sizes) #define MAX_SIZES SPA_N_ELEMENTS(sample_sizes)
#define MAX_RATES SPA_N_ELEMENTS(in_rates) #define MAX_RATES SPA_N_ELEMENTS(in_rates)
#define MAX_RESULTS MAX_RESAMPLER * MAX_SIZES * MAX_RATES #define MAX_RESULTS MAX_RESAMPLER * MAX_SIZES * MAX_RATES
@ -123,7 +123,9 @@ int main(int argc, char *argv[])
r.o_rate = out_rates[i]; r.o_rate = out_rates[i];
r.quality = RESAMPLE_DEFAULT_QUALITY; r.quality = RESAMPLE_DEFAULT_QUALITY;
resample_native_init(&r); resample_native_init(&r);
run_test("native", "c", &r); run_test("full", "c", &r);
resample_update_rate(&r, 1.001);
run_test("inter", "c", &r);
resample_free(&r); resample_free(&r);
} }
#if defined (HAVE_SSE) #if defined (HAVE_SSE)
@ -136,7 +138,9 @@ int main(int argc, char *argv[])
r.o_rate = out_rates[i]; r.o_rate = out_rates[i];
r.quality = RESAMPLE_DEFAULT_QUALITY; r.quality = RESAMPLE_DEFAULT_QUALITY;
resample_native_init(&r); resample_native_init(&r);
run_test("native", "sse", &r); run_test("full", "sse", &r);
resample_update_rate(&r, 1.001);
run_test("inter", "sse", &r);
resample_free(&r); resample_free(&r);
} }
} }
@ -151,22 +155,26 @@ int main(int argc, char *argv[])
r.o_rate = out_rates[i]; r.o_rate = out_rates[i];
r.quality = RESAMPLE_DEFAULT_QUALITY; r.quality = RESAMPLE_DEFAULT_QUALITY;
resample_native_init(&r); resample_native_init(&r);
run_test("native", "ssse3", &r); run_test("full", "ssse3", &r);
resample_update_rate(&r, 1.001);
run_test("inter", "ssse3", &r);
resample_free(&r); resample_free(&r);
} }
} }
#endif #endif
#if defined (HAVE_AVX) && defined(HAVE_FMA) #if defined (HAVE_AVX2) && defined(HAVE_FMA)
if (SPA_FLAG_IS_SET(cpu_flags, SPA_CPU_FLAG_AVX | SPA_CPU_FLAG_FMA3)) { if (SPA_FLAG_IS_SET(cpu_flags, SPA_CPU_FLAG_AVX2 | SPA_CPU_FLAG_FMA3)) {
for (i = 0; i < SPA_N_ELEMENTS(in_rates); i++) { for (i = 0; i < SPA_N_ELEMENTS(in_rates); i++) {
spa_zero(r); spa_zero(r);
r.channels = 2; r.channels = 2;
r.cpu_flags = SPA_CPU_FLAG_AVX | SPA_CPU_FLAG_FMA3; r.cpu_flags = SPA_CPU_FLAG_AVX2 | SPA_CPU_FLAG_FMA3;
r.i_rate = in_rates[i]; r.i_rate = in_rates[i];
r.o_rate = out_rates[i]; r.o_rate = out_rates[i];
r.quality = RESAMPLE_DEFAULT_QUALITY; r.quality = RESAMPLE_DEFAULT_QUALITY;
resample_native_init(&r); resample_native_init(&r);
run_test("native", "avx", &r); run_test("full", "avx2", &r);
resample_update_rate(&r, 1.001);
run_test("inter", "avx2", &r);
resample_free(&r); resample_free(&r);
} }
} }

View file

@ -0,0 +1,63 @@
/* Spa */
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#include "channelmix-ops.h"
#include <immintrin.h>
#include <float.h>
#include <math.h>
static inline void clear_avx(float *d, uint32_t n_samples)
{
memset(d, 0, n_samples * sizeof(float));
}
static inline void copy_avx(float *d, const float *s, uint32_t n_samples)
{
spa_memcpy(d, s, n_samples * sizeof(float));
}
static inline void vol_avx(float *d, const float *s, float vol, uint32_t n_samples)
{
uint32_t n, unrolled;
if (vol == 0.0f) {
clear_avx(d, n_samples);
} else if (vol == 1.0f) {
copy_avx(d, s, n_samples);
} else {
__m256 t[4];
const __m256 v = _mm256_set1_ps(vol);
if (SPA_IS_ALIGNED(d, 32) &&
SPA_IS_ALIGNED(s, 32))
unrolled = n_samples & ~31;
else
unrolled = 0;
for(n = 0; n < unrolled; n += 32) {
t[0] = _mm256_load_ps(&s[n]);
t[1] = _mm256_load_ps(&s[n+8]);
t[2] = _mm256_load_ps(&s[n+16]);
t[3] = _mm256_load_ps(&s[n+24]);
_mm256_store_ps(&d[n], _mm256_mul_ps(t[0], v));
_mm256_store_ps(&d[n+8], _mm256_mul_ps(t[1], v));
_mm256_store_ps(&d[n+16], _mm256_mul_ps(t[2], v));
_mm256_store_ps(&d[n+24], _mm256_mul_ps(t[3], v));
}
for(; n < n_samples; n++) {
__m128 v = _mm_set1_ps(vol);
_mm_store_ss(&d[n], _mm_mul_ss(_mm_load_ss(&s[n]), v));
}
}
}
void channelmix_copy_avx(struct channelmix *mix, void * SPA_RESTRICT dst[],
const void * SPA_RESTRICT src[], uint32_t n_samples)
{
uint32_t i, n_dst = mix->dst_chan;
float **d = (float **)dst;
const float **s = (const float **)src;
for (i = 0; i < n_dst; i++)
vol_avx(d[i], s[i], mix->matrix[i][i], n_samples);
}

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