Projects
home:zaitor:branches:Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
We truncated the diff of some files because they were too big. If you want to see the full diff for every file,
click here
.
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Difference Between Revision 2 and
Essentials
/
pipewire-aptx
View file
pipewire-aptx.spec
Changed
@@ -5,10 +5,10 @@ %define spaversion 0.2 %define soversion 0_2 -%define minimum_version 1.5.0 +%define minimum_version 1.6.0 Name: pipewire-aptx -Version: 1.5.84 +Version: 1.6.0 Release: 0 Summary: PipeWire Bluetooth aptX codec plugin License: MIT
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/resample-native-avx.c
Deleted
@@ -1,74 +0,0 @@ -/* Spa */ -/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ -/* SPDX-License-Identifier: MIT */ - -#include "resample-native-impl.h" - -#include <assert.h> -#include <immintrin.h> - -static inline void inner_product_avx(float *d, const float * SPA_RESTRICT s, - const float * SPA_RESTRICT taps, uint32_t n_taps) -{ - __m256 sy2 = { _mm256_setzero_ps(), _mm256_setzero_ps() }, ty; - __m128 sx2, tx; - uint32_t i = 0; - uint32_t n_taps4 = n_taps & ~0xf; - - for (; i < n_taps4; i += 16) { - ty = _mm256_loadu_ps(s + i + 0); - sy0 = _mm256_fmadd_ps(ty, _mm256_load_ps(taps + i + 0), sy0); - ty = _mm256_loadu_ps(s + i + 8); - sy1 = _mm256_fmadd_ps(ty, _mm256_load_ps(taps + i + 8), sy1); - } - sy0 = _mm256_add_ps(sy1, sy0); - sx1 = _mm256_extractf128_ps(sy0, 1); - sx0 = _mm256_extractf128_ps(sy0, 0); - for (; i < n_taps; i += 8) { - tx = _mm_loadu_ps(s + i + 0); - sx0 = _mm_fmadd_ps(tx, _mm_load_ps(taps + i + 0), sx0); - tx = _mm_loadu_ps(s + i + 4); - sx1 = _mm_fmadd_ps(tx, _mm_load_ps(taps + i + 4), sx1); - } - sx0 = _mm_add_ps(sx0, sx1); - sx0 = _mm_hadd_ps(sx0, sx0); - sx0 = _mm_hadd_ps(sx0, sx0); - _mm_store_ss(d, sx0); -} - -static inline void inner_product_ip_avx(float *d, const float * SPA_RESTRICT s, - const float * SPA_RESTRICT t0, const float * SPA_RESTRICT t1, float x, - uint32_t n_taps) -{ - __m256 sy2 = { _mm256_setzero_ps(), _mm256_setzero_ps() }, ty; - __m128 sx2, tx; - uint32_t i, n_taps4 = n_taps & ~0xf; - - for (i = 0; i < n_taps4; i += 16) { - ty = _mm256_loadu_ps(s + i + 0); - sy0 = _mm256_fmadd_ps(ty, _mm256_load_ps(t0 + i + 0), sy0); - sy1 = _mm256_fmadd_ps(ty, _mm256_load_ps(t1 + i + 0), sy1); - ty = _mm256_loadu_ps(s + i + 8); - sy0 = _mm256_fmadd_ps(ty, _mm256_load_ps(t0 + i + 8), sy0); - sy1 = _mm256_fmadd_ps(ty, _mm256_load_ps(t1 + i + 8), sy1); - } - sx0 = _mm_add_ps(_mm256_extractf128_ps(sy0, 0), _mm256_extractf128_ps(sy0, 1)); - sx1 = _mm_add_ps(_mm256_extractf128_ps(sy1, 0), _mm256_extractf128_ps(sy1, 1)); - - for (; i < n_taps; i += 8) { - tx = _mm_loadu_ps(s + i + 0); - sx0 = _mm_fmadd_ps(tx, _mm_load_ps(t0 + i + 0), sx0); - sx1 = _mm_fmadd_ps(tx, _mm_load_ps(t1 + i + 0), sx1); - tx = _mm_loadu_ps(s + i + 4); - sx0 = _mm_fmadd_ps(tx, _mm_load_ps(t0 + i + 4), sx0); - sx1 = _mm_fmadd_ps(tx, _mm_load_ps(t1 + i + 4), sx1); - } - sx1 = _mm_mul_ps(_mm_sub_ps(sx1, sx0), _mm_load1_ps(&x)); - sx0 = _mm_add_ps(sx0, sx1); - sx0 = _mm_hadd_ps(sx0, sx0); - sx0 = _mm_hadd_ps(sx0, sx0); - _mm_store_ss(d, sx0); -} - -MAKE_RESAMPLER_FULL(avx); -MAKE_RESAMPLER_INTER(avx);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audiomixer/mix-ops-avx.c
Deleted
@@ -1,68 +0,0 @@ -/* Spa */ -/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ -/* SPDX-License-Identifier: MIT */ - -#include <string.h> -#include <stdio.h> -#include <math.h> - -#include <spa/utils/defs.h> - -#include "mix-ops.h" - -#include <immintrin.h> - -void -mix_f32_avx(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - n_samples *= ops->n_channels; - - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(float)); - else if (n_src == 1) { - if (dst != src0) - spa_memcpy(dst, src0, n_samples * sizeof(float)); - } else { - uint32_t i, n, unrolled; - const float **s = (const float **)src; - float *d = dst; - - if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 32))) { - unrolled = n_samples & ~31; - for (i = 0; i < n_src; i++) { - if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 32))) { - unrolled = 0; - break; - } - } - } else - unrolled = 0; - - for (n = 0; n < unrolled; n += 32) { - __m256 in4; - - in0 = _mm256_load_ps(&s0n + 0); - in1 = _mm256_load_ps(&s0n + 8); - in2 = _mm256_load_ps(&s0n + 16); - in3 = _mm256_load_ps(&s0n + 24); - for (i = 1; i < n_src; i++) { - in0 = _mm256_add_ps(in0, _mm256_load_ps(&sin + 0)); - in1 = _mm256_add_ps(in1, _mm256_load_ps(&sin + 8)); - in2 = _mm256_add_ps(in2, _mm256_load_ps(&sin + 16)); - in3 = _mm256_add_ps(in3, _mm256_load_ps(&sin + 24)); - } - _mm256_store_ps(&dn + 0, in0); - _mm256_store_ps(&dn + 8, in1); - _mm256_store_ps(&dn + 16, in2); - _mm256_store_ps(&dn + 24, in3); - } - for (; n < n_samples; n++) { - __m128 in1; - in0 = _mm_load_ss(&s0n); - for (i = 1; i < n_src; i++) - in0 = _mm_add_ss(in0, _mm_load_ss(&sin)); - _mm_store_ss(&dn, in0); - } - } -}
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/filter-graph/audio-dsp-avx.c
Deleted
@@ -1,327 +0,0 @@ -/* Spa */ -/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */ -/* SPDX-License-Identifier: MIT */ - -#include "config.h" - -#include <string.h> -#include <stdio.h> -#include <math.h> - -#include <spa/utils/defs.h> - -#ifndef HAVE_FFTW -#include "pffft.h" -#endif -#include "audio-dsp-impl.h" - -#include <immintrin.h> - -static void dsp_add_avx(void *obj, float *dst, const float * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - uint32_t n, i, unrolled; - __m256 in4; - const float **s = (const float **)src; - float *d = dst; - - if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 32))) { - unrolled = n_samples & ~31; - for (i = 0; i < n_src; i++) { - if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 32))) { - unrolled = 0; - break; - } - } - } else - unrolled = 0; - - for (n = 0; n < unrolled; n += 32) { - in0 = _mm256_load_ps(&s0n+ 0); - in1 = _mm256_load_ps(&s0n+ 8); - in2 = _mm256_load_ps(&s0n+16); - in3 = _mm256_load_ps(&s0n+24); - - for (i = 1; i < n_src; i++) { - in0 = _mm256_add_ps(in0, _mm256_load_ps(&sin+ 0)); - in1 = _mm256_add_ps(in1, _mm256_load_ps(&sin+ 8)); - in2 = _mm256_add_ps(in2, _mm256_load_ps(&sin+16)); - in3 = _mm256_add_ps(in3, _mm256_load_ps(&sin+24)); - } - _mm256_store_ps(&dn+ 0, in0); - _mm256_store_ps(&dn+ 8, in1); - _mm256_store_ps(&dn+16, in2); - _mm256_store_ps(&dn+24, in3); - } - for (; n < n_samples; n++) { - __m128 in1; - in0 = _mm_load_ss(&s0n); - for (i = 1; i < n_src; i++) - in0 = _mm_add_ss(in0, _mm_load_ss(&sin)); - _mm_store_ss(&dn, in0); - } -} - -static void dsp_add_1_gain_avx(void *obj, float *dst, const float * SPA_RESTRICT src, - uint32_t n_src, float gain, uint32_t n_samples) -{ - uint32_t n, i, unrolled; - __m256 in4, g; - const float **s = (const float **)src; - float *d = dst; - __m128 g1; - - if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 32))) { - unrolled = n_samples & ~31; - for (i = 0; i < n_src; i++) { - if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 32))) { - unrolled = 0; - break; - } - } - } else - unrolled = 0; - - g = _mm256_set1_ps(gain); - g1 = _mm_set_ss(gain); - - for (n = 0; n < unrolled; n += 32) { - in0 = _mm256_load_ps(&s0n+ 0); - in1 = _mm256_load_ps(&s0n+ 8); - in2 = _mm256_load_ps(&s0n+16); - in3 = _mm256_load_ps(&s0n+24); - - for (i = 1; i < n_src; i++) { - in0 = _mm256_add_ps(in0, _mm256_load_ps(&sin+ 0)); - in1 = _mm256_add_ps(in1, _mm256_load_ps(&sin+ 8)); - in2 = _mm256_add_ps(in2, _mm256_load_ps(&sin+16)); - in3 = _mm256_add_ps(in3, _mm256_load_ps(&sin+24)); - } - _mm256_store_ps(&dn+ 0, _mm256_mul_ps(g, in0)); - _mm256_store_ps(&dn+ 8, _mm256_mul_ps(g, in1)); - _mm256_store_ps(&dn+16, _mm256_mul_ps(g, in2)); - _mm256_store_ps(&dn+24, _mm256_mul_ps(g, in3)); - } - for (; n < n_samples; n++) { - __m128 in1; - in0 = _mm_load_ss(&s0n); - for (i = 1; i < n_src; i++) - in0 = _mm_add_ss(in0, _mm_load_ss(&sin)); - _mm_store_ss(&dn, _mm_mul_ss(g1, in0)); - } -} - -static void dsp_add_n_gain_avx(void *obj, float *dst, - const float * SPA_RESTRICT src, uint32_t n_src, - float gain, uint32_t n_gain, uint32_t n_samples) -{ - uint32_t n, i, unrolled; - __m256 in4, g; - const float **s = (const float **)src; - float *d = dst; - - if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 32))) { - unrolled = n_samples & ~31; - for (i = 0; i < n_src; i++) { - if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 32))) { - unrolled = 0; - break; - } - } - } else - unrolled = 0; - - for (n = 0; n < unrolled; n += 32) { - g = _mm256_set1_ps(gain0); - in0 = _mm256_mul_ps(g, _mm256_load_ps(&s0n+ 0)); - in1 = _mm256_mul_ps(g, _mm256_load_ps(&s0n+ 8)); - in2 = _mm256_mul_ps(g, _mm256_load_ps(&s0n+16)); - in3 = _mm256_mul_ps(g, _mm256_load_ps(&s0n+24)); - - for (i = 1; i < n_src; i++) { - g = _mm256_set1_ps(gaini); - in0 = _mm256_add_ps(in0, _mm256_mul_ps(g, _mm256_load_ps(&sin+ 0))); - in1 = _mm256_add_ps(in1, _mm256_mul_ps(g, _mm256_load_ps(&sin+ 8))); - in2 = _mm256_add_ps(in2, _mm256_mul_ps(g, _mm256_load_ps(&sin+16))); - in3 = _mm256_add_ps(in3, _mm256_mul_ps(g, _mm256_load_ps(&sin+24))); - } - _mm256_store_ps(&dn+ 0, in0); - _mm256_store_ps(&dn+ 8, in1); - _mm256_store_ps(&dn+16, in2); - _mm256_store_ps(&dn+24, in3); - } - for (; n < n_samples; n++) { - __m128 in1, g; - g = _mm_set_ss(gain0); - in0 = _mm_mul_ss(g, _mm_load_ss(&s0n)); - for (i = 1; i < n_src; i++) { - g = _mm_set_ss(gaini); - in0 = _mm_add_ss(in0, _mm_mul_ss(g, _mm_load_ss(&sin))); - } - _mm_store_ss(&dn, in0); - } -} - - -void dsp_mix_gain_avx(void *obj, - float * SPA_RESTRICT dst, - const float * SPA_RESTRICT src, uint32_t n_src, - float gain, uint32_t n_gain, uint32_t n_samples) -{ - if (n_src == 0) { - memset(dst, 0, n_samples * sizeof(float)); - } else if (n_src == 1 && gain0 == 1.0f) { - if (dst != src0) - spa_memcpy(dst, src0, n_samples * sizeof(float)); - } else { - if (n_gain == 0) - dsp_add_avx(obj, dst, src, n_src, n_samples); - else if (n_gain < n_src) - dsp_add_1_gain_avx(obj, dst, src, n_src, gain0, n_samples); - else - dsp_add_n_gain_avx(obj, dst, src, n_src, gain, n_gain, n_samples); - } -} - -void dsp_sum_avx(void *obj, float *r, const float *a, const float *b, uint32_t n_samples) -{ - uint32_t n, unrolled; - __m256 in4; - - unrolled = n_samples & ~31; - - if (SPA_LIKELY(SPA_IS_ALIGNED(r, 32)) && - SPA_LIKELY(SPA_IS_ALIGNED(a, 32)) && - SPA_LIKELY(SPA_IS_ALIGNED(b, 32))) { - for (n = 0; n < unrolled; n += 32) { - in0 = _mm256_load_ps(&an+ 0); - in1 = _mm256_load_ps(&an+ 8); - in2 = _mm256_load_ps(&an+16);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/NEWS -> _service:download_files:pipewire-1.6.0.tar.bz2/NEWS
Changed
@@ -1,3 +1,161 @@ +# 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 @@ -43,9 +201,6 @@ - Improve compatiblity with Creative Zen Hybrid Pro by releasing transports simultaneously. - -Older versions: - # PipeWire 1.5.83 (2025-11-06) This is the third 1.6 release candidate that is API and ABI
View file
_service:download_files:pipewire-1.5.84.tar.bz2/doc/dox/config/pipewire-props.7.md -> _service:download_files:pipewire-1.6.0.tar.bz2/doc/dox/config/pipewire-props.7.md
Changed
@@ -547,11 +547,51 @@ \parblock 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 -(much) more CPU consumption. The default quality of 4 has been selected as a good compromise -between quality and performance with no artifacts that are well below the audible range. +(much) more CPU consumption, latency and more ringing. The default quality of 4 has +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. + +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 @PAR@ node-prop resample.disable = false @@ -559,39 +599,86 @@ 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. +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 -The resampler cutoff frequency. A value of 0.0 will use a predefined value based on -the resampler quality. +\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 -The kaiser window stopband attenuation parameter. A default value of 0.0 will use a +\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 @@ -1053,6 +1140,15 @@ @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 Register dummy AVRCP player. Some devices have wrongly functioning volume or playback controls if this is not enabled. Default: false @@ -1075,6 +1171,15 @@ Do not enable this setting if you don't know what all this means, as it won't work. \endparblock +@PAR@ monitor-prop bluez5.hw-offload-datapath # integer +\parblock +HFP/HSP hardware offload data path ID (default: 0). + +This feature configures the SCO hardware‑offload data path for HFP/HSP using the Bluetooth +SIG–specified 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 PipeWire Opus Pro audio profile channel count. @@ -1106,6 +1211,7 @@ PipeWire Opus Pro audio profile duplex frame duration (1/10 ms). @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 Example: ``` @@ -1162,6 +1268,18 @@ @PAR@ monitor-prop bluez5.bap-server-capabilities.sink.supported-contexts # integer 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.
View file
_service:download_files:pipewire-1.5.84.tar.bz2/doc/dox/internals/dma-buf.dox -> _service:download_files:pipewire-1.6.0.tar.bz2/doc/dox/internals/dma-buf.dox
Changed
@@ -301,5 +301,118 @@ the render pipeline or explicitly with drmSyncobjTimelineSignal() on the release 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); + paramsn_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 set to `true`, the full set of formats can be sent using \ref +pw_stream_update_params following by activating the stream using +`pw_stream_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
_service:download_files:pipewire-1.5.84.tar.bz2/doc/dox/internals/driver.dox -> _service:download_files:pipewire-1.6.0.tar.bz2/doc/dox/internals/driver.dox
Changed
@@ -32,8 +32,11 @@ - \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 - 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. + 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::position : Current cycle position, in samples. This is the ideal position of the graph cycle (this is explained in greater detail further below). @@ -52,7 +55,7 @@ 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 \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. The driver node signals the start of the graph cycle by calling \ref spa_node_call_ready @@ -60,6 +63,11 @@ to that function call. That call must happen inside the thread that runs the 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 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 @@ -103,11 +111,12 @@ 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 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 -difference between the expected position and the actual position reaches zero. Keep in -mind the notes above about \ref spa_io_clock::position being the ideal position of the -graph 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. +correction factor causes the \ref spa_io_clock::next_nsec increments (that is, the +durations between the timerfd timeouts) to be adjusted such that, over time, the difference +between the expected position and the actual position reaches zero. Keep in mind the +notes above about \ref spa_io_clock::position being the ideal position of the graph +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 PID controllers and Kalman filters.)
View file
_service:download_files:pipewire-1.5.84.tar.bz2/doc/dox/programs/pw-cat.1.md -> _service:download_files:pipewire-1.6.0.tar.bz2/doc/dox/programs/pw-cat.1.md
Changed
@@ -24,9 +24,9 @@ **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 -supported by `libsndfile` for PCM capture and playback. When capturing -PCM, the filename extension is used to guess the file format with the -WAV file format as the default. +supported by `libsndfile` for PCM capture and playback. When no container +is specified for capturing PCM, the filename extension is used to guess +the file format with the WAV file format as the default. 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 @@ -37,8 +37,15 @@ work with native DSD capable hardware and will produce an error when no such hardware was found. -When the *FILE* is - input and output will be raw data from STDIN and -STDOUT respectively. +When the *FILE* is - input will be from STDIN. If no format is specified, +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 @@ -87,6 +94,11 @@ render the DSD audio. You need a DSD capable device to play DSD content 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 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 @@ -138,6 +150,17 @@ Higher quality uses more CPU. Values between 0 and 15 are allowed, the 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 The sample rate, default 48000. @@ -145,19 +168,38 @@ The number of channels, default 2. \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-31**, **surround-41**, **surround-50**, **surround-51**, -**surround-51r**, **surround-70**, **surround-71** or a comma separated -list of channel names: **FL**, **FR**, **FC**, **LFE**, **SL**, **SR**, -**FLC**, **FRC**, **RC**, **RL**, **RR**, **TC**, **TFL**, **TFC**, -**TFR**, **TRL**, **TRC**, **TRR**, **RLC**, **RRC**, **FLW**, **FRW**, -**LFE2**, **FLH**, **FCH**, **FRH**, **TFLC**, **TFRC**, **TSL**, -**TSR**, **LLFR**, **RLFE**, **BC**, **BLC**, **BRC** +or comma separated array of channel names such as **FL,FR**. +See \--list-layouts and \--list-channel-names to get a complete +list of possible values. + +\par \--list-layouts +List all known channel layouts. One of these can be used as the +\--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 -The sample format to use. One of: **u8**, **s8**, **s16** (default), -**s24**, **s32**, **f32**, **f64**. +The sample format to use. Some possible values include: **u8**, **s8**, +**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 The stream volume, default 1.000. Depending on the locale you have
View file
_service:download_files:pipewire-1.5.84.tar.bz2/doc/dox/programs/pw-loopback.1.md -> _service:download_files:pipewire-1.6.0.tar.bz2/doc/dox/programs/pw-loopback.1.md
Changed
@@ -49,7 +49,7 @@ Wanted properties of capture node (in JSON) \par -o | \--playback-props=PROPS -Wanted properties of capture node (in JSON) +Wanted properties of playback node (in JSON) # AUTHORS
View file
_service:download_files:pipewire-1.5.84.tar.bz2/doc/dox/programs/pw-top.1.md -> _service:download_files:pipewire-1.6.0.tar.bz2/doc/dox/programs/pw-top.1.md
Changed
@@ -13,7 +13,7 @@ A hierarchical view is shown of Driver nodes and follower nodes. 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. The columns presented are as follows:
View file
_service:download_files:pipewire-1.5.84.tar.bz2/meson.build -> _service:download_files:pipewire-1.6.0.tar.bz2/meson.build
Changed
@@ -1,5 +1,5 @@ project('pipewire', 'c' , - version : '1.5.84', + version : '1.6.0', license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' , meson_version : '>= 0.61.1', default_options : 'warning_level=3', @@ -82,6 +82,7 @@ '-fvisibility=hidden', '-fno-strict-aliasing', '-fno-strict-overflow', + '-DSPA_AUDIO_MAX_CHANNELS=128u', '-Werror=suggest-attribute=format', '-Wsign-compare', '-Wpointer-arith', @@ -115,7 +116,6 @@ '-Werror=old-style-definition', '-Werror=missing-parameter-type', '-Werror=strict-prototypes', - '-DSPA_AUDIO_MAX_CHANNELS=128u', add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c') add_project_arguments(cc_native.get_supported_arguments(cc_flags),
View file
_service:download_files:pipewire-1.5.84.tar.bz2/po/sl.po -> _service:download_files:pipewire-1.6.0.tar.bz2/po/sl.po
Changed
@@ -2,23 +2,21 @@ # Copyright (C) 2024 PipeWire's COPYRIGHT HOLDER # 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 "" msgstr "" "Project-Id-Version: PipeWire master\n" -"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" -"issues\n" -"POT-Creation-Date: 2025-11-04 03:33+0000\n" -"PO-Revision-Date: 2025-11-08 15:23+0100\n" +"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues\n" +"POT-Creation-Date: 2026-02-09 12:55+0000\n" +"PO-Revision-Date: 2026-02-15 16:18+0100\n" "Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n" "Language-Team: Slovenian <gnome-si@googlegroups.com>\n" "Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n" -"%100<=4 ? 2 : 3);\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n%100<=4 ? 2 : 3);\n" "X-Generator: Poedit 2.2.1\n" #: src/daemon/pipewire.c:29 @@ -35,8 +33,7 @@ " -h, --help Pokaži to pomoč\n" " -v, --verbose Povečaj opisnost za eno raven\n" " --version Pokaži različico\n" -" -c, --config Naloži prilagoditev config (privzeto " -"%s)\n" +" -c, --config Naloži prilagoditev config (privzeto %s)\n" " -P --properties Določi lastnosti konteksta\n" #: src/daemon/pipewire.desktop.in:3 @@ -62,21 +59,46 @@ msgid "Tunnel for %s@%s" msgstr "Prehod za %s@%s" -#: src/modules/module-zeroconf-discover.c:320 +#: src/modules/module-zeroconf-discover.c:326 msgid "Unknown device" msgstr "Neznana naprava" -#: src/modules/module-zeroconf-discover.c:332 +#: src/modules/module-zeroconf-discover.c:338 #, c-format msgid "%s on %s@%s" msgstr "%s na %s@%s" -#: src/modules/module-zeroconf-discover.c:336 +#: src/modules/module-zeroconf-discover.c:342 #, c-format msgid "%s on %s" msgstr "%s na %s" -#: src/tools/pw-cat.c:1096 +#: 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 msgid "" "%s options <file>|-\n" @@ -92,7 +114,7 @@ "\n" "</file>\n" -#: src/tools/pw-cat.c:1103 +#: src/tools/pw-cat.c:1184 #, c-format msgid "" " -R, --remote Remote daemon name\n" @@ -129,20 +151,23 @@ " -P --properties Nastavi lastnosti vozlišča\n" "\n" -#: src/tools/pw-cat.c:1121 +#: src/tools/pw-cat.c:1202 #, c-format msgid "" -" --rate Sample rate (req. for rec) (default " -"%u)\n" -" --channels Number of channels (req. for rec) " -"(default %u)\n" +" --rate Sample rate (default %u)\n" +" --channels Number of channels (default %u)\n" " --channel-map Channel map\n" -" one of: \"stereo\", " -"\"surround-51\",... or\n" +" a channel layout: \"Stereo\", " +"\"5.1\",... or\n" " comma separated list of channel " "names: eg. \"FL,FR\"\n" -" --format Sample format %s (req. for rec) " -"(default %s)\n" +" --list-layouts List supported channel layouts\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" " -q --quality Resampler quality (0 - 15) (default " "%d)\n" @@ -157,12 +182,17 @@ " --channels Število kanalov (zaht. za snemanje) " "(privzeto %u)\n" " --channel-map Preslikava kanalov\n" -" Ena izmed: \"stereo\", " -"\"surround-51\",... ali\n" +" Ena izmed: \"Stereo\", " +"\"5.1\",... ali\n" " seznam imen kanalov, ločen z " "vejico: npr. \"FL,FR\"\n" -" --format Vzorčne oblike zapisa %s (zahtevano " -"za rec) (privzeto %s)\n" +" —list-layouts Izpiše podprte postavitve kanalov\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" " -q --quality Kakovost prevzorčenja (0 - 15) " "(privzeto %d)\n" @@ -172,7 +202,7 @@ " -n, --sample-count ŠTEVEC Ustavi po ŠTEVEC vzorcih\n" "\n" -#: src/tools/pw-cat.c:1141 +#: src/tools/pw-cat.c:1227 msgid "" " -p, --playback Playback mode\n" " -r, --record Recording mode\n" @@ -192,6 +222,11 @@ " -c, --midi-clip Način posnetka MIDI\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 #, c-format msgid "" @@ -217,7 +252,7 @@ msgstr "Profesionalni zvok" #: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699 -#: spa/plugins/bluez5/bluez5-device.c:1976 +#: spa/plugins/bluez5/bluez5-device.c:2021 msgid "Off" msgstr "Izklopljeno" @@ -249,7 +284,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:2726 #: spa/plugins/alsa/acp/alsa-mixer.c:2810 -#: spa/plugins/bluez5/bluez5-device.c:2374 +#: spa/plugins/bluez5/bluez5-device.c:2422 msgid "Microphone" msgstr "Mikrofon" @@ -315,15 +350,15 @@ msgstr "Brez ojačitve nizkih tonov" #: spa/plugins/alsa/acp/alsa-mixer.c:2741 -#: spa/plugins/bluez5/bluez5-device.c:2380 +#: spa/plugins/bluez5/bluez5-device.c:2428 msgid "Speaker"
View file
_service:download_files:pipewire-1.5.84.tar.bz2/po/zh_CN.po -> _service:download_files:pipewire-1.6.0.tar.bz2/po/zh_CN.po
Changed
@@ -6,15 +6,15 @@ # Cheng-Chia Tseng <pswo10680@gmail.com>, 2010, 2012. # Frank Hill <hxf.prc@gmail.com>, 2015. # Mingye Wang (Arthur2e5) <arthur200126@gmail.com>, 2015. -# lumingzh <lumingzh@qq.com>, 2024-2025. +# lumingzh <lumingzh@qq.com>, 2024-2026. # msgid "" msgstr "" "Project-Id-Version: pipewire.master-tx\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" "issues\n" -"POT-Creation-Date: 2025-11-25 15:35+0000\n" -"PO-Revision-Date: 2025-11-26 10:19+0800\n" +"POT-Creation-Date: 2026-02-11 16:53+0000\n" +"PO-Revision-Date: 2026-02-13 09:36+0800\n" "Last-Translator: lumingzh <lumingzh@qq.com>\n" "Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n" "Language: zh_CN\n" @@ -65,21 +65,46 @@ msgid "Tunnel for %s@%s" msgstr "用于 %s@%s 的隧道" -#: src/modules/module-zeroconf-discover.c:320 +#: src/modules/module-zeroconf-discover.c:326 msgid "Unknown device" msgstr "未知设备" -#: src/modules/module-zeroconf-discover.c:332 +#: src/modules/module-zeroconf-discover.c:338 #, c-format msgid "%s on %s@%s" msgstr "%2$s@%3$s 上的 %1$s" -#: src/modules/module-zeroconf-discover.c:336 +#: src/modules/module-zeroconf-discover.c:342 #, c-format msgid "%s on %s" msgstr "%2$s 上的 %1$s" -#: src/tools/pw-cat.c:1088 +#: src/tools/pw-cat.c:264 +#, c-format +msgid "Supported formats:\n" +msgstr "支持的格式:\n" + +#: src/tools/pw-cat.c:749 +#, c-format +msgid "Supported channel layouts:\n" +msgstr "支持的声道布局:\n" + +#: src/tools/pw-cat.c:759 +#, c-format +msgid "Supported channel layout aliases:\n" +msgstr "支持的声道布局别名:\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 "支持的声道名称:\n" + +#: src/tools/pw-cat.c:1177 #, c-format msgid "" "%s options <file>|-\n" @@ -94,7 +119,7 @@ " -v, --verbose 输出详细操作\n" "\n" -#: src/tools/pw-cat.c:1095 +#: src/tools/pw-cat.c:1184 #, c-format msgid "" " -R, --remote Remote daemon name\n" @@ -126,20 +151,23 @@ " -P --properties 设置节点属性\n" "\n" -#: src/tools/pw-cat.c:1113 +#: src/tools/pw-cat.c:1202 #, c-format msgid "" -" --rate Sample rate (req. for rec) (default " -"%u)\n" -" --channels Number of channels (req. for rec) " -"(default %u)\n" +" --rate Sample rate (default %u)\n" +" --channels Number of channels (default %u)\n" " --channel-map Channel map\n" -" one of: \"Stereo\", \"5.1\",... " -"or\n" +" a channel layout: \"Stereo\", " +"\"5.1\",... or\n" " comma separated list of channel " "names: eg. \"FL,FR\"\n" -" --format Sample format %s (req. for rec) " -"(default %s)\n" +" --list-layouts List supported channel layouts\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" " -q --quality Resampler quality (0 - 15) (default " "%d)\n" @@ -149,15 +177,19 @@ " -n, --sample-count COUNT Stop after COUNT samples\n" "\n" msgstr "" -" --rate 采样率 (录制模式需要) (默认 %u)\n" -" --channels 通道数 (录制模式需要) (默认 %u)\n" -" --channel-map 通道映射\n" -" \"stereo\", \"5.1\",... 中的其一" -"或\n" -" 以英文逗号分隔的通道名列表: 如 " +" --rate 采样率 (默认 %u)\n" +" --channels 声道数 (默认 %u)\n" +" --channel-map 声道映射\n" +" 声道布局:\"stereo\", " +"\"5.1\",... 或\n" +" 以英文逗号分隔的声道名列表: 如 " "\"FL,FR\"\n" -" --format 采样格式 %s (录制模式需要) (默认 " -"%s)\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" @@ -166,7 +198,7 @@ " -n, --sample-count COUNT 计数采样后停止\n" "\n" -#: src/tools/pw-cat.c:1133 +#: src/tools/pw-cat.c:1227 msgid "" " -p, --playback Playback mode\n" " -r, --record Recording mode\n" @@ -186,6 +218,11 @@ " -c, --midi-clip MIDI 剪辑模式\n" "\n" +#: src/tools/pw-cat.c:1827 +#, c-format +msgid "Supported containers and extensions:\n" +msgstr "支持的容器和扩展:\n" + #: src/tools/pw-cli.c:2386 #, c-format msgid "" @@ -209,7 +246,7 @@ msgstr "专业音频" #: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699 -#: spa/plugins/bluez5/bluez5-device.c:1976 +#: spa/plugins/bluez5/bluez5-device.c:2021 msgid "Off" msgstr "关" @@ -241,7 +278,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:2726 #: spa/plugins/alsa/acp/alsa-mixer.c:2810 -#: spa/plugins/bluez5/bluez5-device.c:2374 +#: spa/plugins/bluez5/bluez5-device.c:2422 msgid "Microphone" msgstr "话筒" @@ -307,15 +344,15 @@ msgstr "无重低音增强" #: spa/plugins/alsa/acp/alsa-mixer.c:2741 -#: spa/plugins/bluez5/bluez5-device.c:2380 +#: spa/plugins/bluez5/bluez5-device.c:2428 msgid "Speaker" msgstr "扬声器" #. 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:2820 -#: spa/plugins/bluez5/bluez5-device.c:2386 -#: spa/plugins/bluez5/bluez5-device.c:2453 +#: spa/plugins/bluez5/bluez5-device.c:2434 +#: spa/plugins/bluez5/bluez5-device.c:2501 msgid "Headphones" msgstr "模拟耳机" @@ -425,7 +462,7 @@
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/include/spa/param/audio/layout-types.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/include/spa/param/audio/layout-types.h
Changed
@@ -75,7 +75,7 @@ { "7.1", { SPA_AUDIO_LAYOUT_7_1 } }, { "7.1W", { SPA_AUDIO_LAYOUT_7_1W } }, { "7.1WR", { SPA_AUDIO_LAYOUT_7_1WR } }, - { NULL, }, + { NULL, { 0, { SPA_AUDIO_CHANNEL_UNKNOWN } } }, }; SPA_API_AUDIO_LAYOUT_TYPES int
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/include/spa/param/audio/raw.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/include/spa/param/audio/raw.h
Changed
@@ -156,41 +156,42 @@ SPA_AUDIO_CHANNEL_MONO, /**< mono stream */ - SPA_AUDIO_CHANNEL_FL, /**< front left */ - SPA_AUDIO_CHANNEL_FR, /**< front right */ - SPA_AUDIO_CHANNEL_FC, /**< front center */ - SPA_AUDIO_CHANNEL_LFE, /**< LFE */ - SPA_AUDIO_CHANNEL_SL, /**< side left */ - SPA_AUDIO_CHANNEL_SR, /**< side right */ - SPA_AUDIO_CHANNEL_FLC, /**< front left center */ - SPA_AUDIO_CHANNEL_FRC, /**< front right center */ - SPA_AUDIO_CHANNEL_RC, /**< rear center */ - SPA_AUDIO_CHANNEL_RL, /**< rear left */ - SPA_AUDIO_CHANNEL_RR, /**< rear right */ - SPA_AUDIO_CHANNEL_TC, /**< top center */ - SPA_AUDIO_CHANNEL_TFL, /**< top front left */ - SPA_AUDIO_CHANNEL_TFC, /**< top front center */ - SPA_AUDIO_CHANNEL_TFR, /**< top front right */ - SPA_AUDIO_CHANNEL_TRL, /**< top rear left */ - SPA_AUDIO_CHANNEL_TRC, /**< top rear center */ - SPA_AUDIO_CHANNEL_TRR, /**< top rear right */ - SPA_AUDIO_CHANNEL_RLC, /**< rear left center */ - SPA_AUDIO_CHANNEL_RRC, /**< rear right center */ - SPA_AUDIO_CHANNEL_FLW, /**< front left wide */ - SPA_AUDIO_CHANNEL_FRW, /**< front right wide */ - SPA_AUDIO_CHANNEL_LFE2, /**< LFE 2 */ - SPA_AUDIO_CHANNEL_FLH, /**< front left high */ - SPA_AUDIO_CHANNEL_FCH, /**< front center high */ - SPA_AUDIO_CHANNEL_FRH, /**< front right high */ - SPA_AUDIO_CHANNEL_TFLC, /**< top front left center */ - SPA_AUDIO_CHANNEL_TFRC, /**< top front right center */ - SPA_AUDIO_CHANNEL_TSL, /**< top side left */ - SPA_AUDIO_CHANNEL_TSR, /**< top side right */ - SPA_AUDIO_CHANNEL_LLFE, /**< left LFE */ - SPA_AUDIO_CHANNEL_RLFE, /**< right LFE */ - SPA_AUDIO_CHANNEL_BC, /**< bottom center */ - SPA_AUDIO_CHANNEL_BLC, /**< bottom left center */ - SPA_AUDIO_CHANNEL_BRC, /**< bottom right center */ + /** Azimuth Elevation */ + SPA_AUDIO_CHANNEL_FL, /**< front left 30 0 */ + SPA_AUDIO_CHANNEL_FR, /**< front right -30 0 */ + SPA_AUDIO_CHANNEL_FC, /**< front center 0 0 */ + SPA_AUDIO_CHANNEL_LFE, /**< LFE 0 -30 */ + SPA_AUDIO_CHANNEL_SL, /**< side left 90 0 */ + SPA_AUDIO_CHANNEL_SR, /**< side right -90 0 */ + SPA_AUDIO_CHANNEL_FLC, /**< front left center 22.5 0 */ + SPA_AUDIO_CHANNEL_FRC, /**< front right center -22.5 0 */ + SPA_AUDIO_CHANNEL_RC, /**< rear center 180 0 */ + SPA_AUDIO_CHANNEL_RL, /**< rear left 110 0 */ + SPA_AUDIO_CHANNEL_RR, /**< rear right -110 0 */ + SPA_AUDIO_CHANNEL_TC, /**< top center 0 90 */ + SPA_AUDIO_CHANNEL_TFL, /**< top front left 30 30 */ + SPA_AUDIO_CHANNEL_TFC, /**< top front center 0 30 */ + SPA_AUDIO_CHANNEL_TFR, /**< top front right -30 30 */ + SPA_AUDIO_CHANNEL_TRL, /**< top rear left 110 30 */ + SPA_AUDIO_CHANNEL_TRC, /**< top rear center 180 30 */ + SPA_AUDIO_CHANNEL_TRR, /**< top rear right -110 30 */ + SPA_AUDIO_CHANNEL_RLC, /**< rear left center 135 0 */ + SPA_AUDIO_CHANNEL_RRC, /**< rear right center -135 0 */ + SPA_AUDIO_CHANNEL_FLW, /**< front left wide 60 0 */ + SPA_AUDIO_CHANNEL_FRW, /**< front right wide -60 0 */ + SPA_AUDIO_CHANNEL_LFE2, /**< LFE 2 0 -30 */ + SPA_AUDIO_CHANNEL_FLH, /**< front left high 22.5 30 */ + SPA_AUDIO_CHANNEL_FCH, /**< front center high 0 30 */ + SPA_AUDIO_CHANNEL_FRH, /**< front right high -22.5 30 */ + SPA_AUDIO_CHANNEL_TFLC, /**< top front left center 45 30 */ + SPA_AUDIO_CHANNEL_TFRC, /**< top front right center -45 30 */ + SPA_AUDIO_CHANNEL_TSL, /**< top side left 90 0 */ + SPA_AUDIO_CHANNEL_TSR, /**< top side right -90 0 */ + SPA_AUDIO_CHANNEL_LLFE, /**< left LFE 45 -30 */ + SPA_AUDIO_CHANNEL_RLFE, /**< right LFE -45 -30 */ + SPA_AUDIO_CHANNEL_BC, /**< bottom center 0 -30 */ + 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_AUX0 = SPA_AUDIO_CHANNEL_START_Aux,
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/include/spa/param/format-types.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/include/spa/param/format-types.h
Changed
@@ -181,6 +181,8 @@ { 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_VIDEO_deviceId, SPA_TYPE_Bytes, SPA_TYPE_INFO_FORMAT_BASE "deviceId", NULL }, { 0, 0, NULL, NULL }, };
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/include/spa/param/format.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/include/spa/param/format.h
Changed
@@ -144,6 +144,7 @@ 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_alignment, /**< (Id enum spa_h265_alignment) */ + SPA_FORMAT_VIDEO_deviceId, /**< dev_t identifier (Bytes) */ /* Image Format keys */ SPA_FORMAT_START_Image = 0x30000,
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/include/spa/utils/keys.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/include/spa/utils/keys.h
Changed
@@ -98,15 +98,6 @@ #define SPA_KEY_API_LIBCAMERA_ROTATION "api.libcamera.rotation" /**< rotation of the camera: * "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 */ #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 */
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/alsa/90-pipewire-alsa.rules -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/alsa/90-pipewire-alsa.rules
Changed
@@ -187,6 +187,11 @@ # Sennheiser GSP 670 USB headset 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 # have any digital outputs. ATTRS{idVendor}=="0a12", ATTRS{idProduct}=="4007", ENV{ACP_PROFILE_SET}="analog-only.conf"
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/alsa/acp/acp.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/alsa/acp/acp.c
Changed
@@ -1137,7 +1137,7 @@ } 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; pa_proplist_unset(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED); } else { @@ -1146,32 +1146,38 @@ struct spa_strbuf b; int i = 0; +#define _ADD_CHANNEL_POSITION(pos) \ + { \ + if (i < eld.lpcm_channels) \ + positionsi++ = pos; \ + } + if (eld.speakers & 0x01) { - positionsi++ = ACP_CHANNEL_FL; - positionsi++ = ACP_CHANNEL_FR; + _ADD_CHANNEL_POSITION(ACP_CHANNEL_FL); + _ADD_CHANNEL_POSITION(ACP_CHANNEL_FR); } if (eld.speakers & 0x02) { - positionsi++ = ACP_CHANNEL_LFE; + _ADD_CHANNEL_POSITION(ACP_CHANNEL_LFE); } if (eld.speakers & 0x04) { - positionsi++ = ACP_CHANNEL_FC; + _ADD_CHANNEL_POSITION(ACP_CHANNEL_FC); } if (eld.speakers & 0x08) { - positionsi++ = ACP_CHANNEL_RL; - positionsi++ = ACP_CHANNEL_RR; + _ADD_CHANNEL_POSITION(ACP_CHANNEL_RL); + _ADD_CHANNEL_POSITION(ACP_CHANNEL_RR); } /* The rest are out of order in order of what channels we would prefer to use/expose first */ if (eld.speakers & 0x40) { /* Use SL/SR instead of RLC/RRC */ - positionsi++ = ACP_CHANNEL_SL; - positionsi++ = ACP_CHANNEL_SR; + _ADD_CHANNEL_POSITION(ACP_CHANNEL_SL); + _ADD_CHANNEL_POSITION(ACP_CHANNEL_SR); } if (eld.speakers & 0x20) { - positionsi++ = ACP_CHANNEL_RLC; - positionsi++ = ACP_CHANNEL_RRC; + _ADD_CHANNEL_POSITION(ACP_CHANNEL_RLC); + _ADD_CHANNEL_POSITION(ACP_CHANNEL_RRC); } if (eld.speakers & 0x10) { - positionsi++ = ACP_CHANNEL_RC; + _ADD_CHANNEL_POSITION(ACP_CHANNEL_RC); } while (i < eld.lpcm_channels) positionsi++ = ACP_CHANNEL_UNKNOWN;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/alsa/alsa-pcm.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -1119,6 +1119,8 @@ } } + spa_clear_ptr(state->alsa_chmap, free); + return err; } @@ -1664,6 +1666,69 @@ 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; mapsi; ++i) { + spa_autofree snd_pcm_chmap_t *map = NULL; + + if (mapsi->map.channels != channels) + continue; + + map = dup_alsa_chmap(&mapsi->map); + if (!map) { + err = -ENOMEM; + break; + } + sanitize_map(map); + + for (j = 0; j < channels; ++j) { + if (positionj != chmap_position_to_channel(map->posj)) + break; + } + if (j < channels) + continue; + + *chmap = dup_alsa_chmap(&mapsi->map); + if (!*chmap) + err = -ENOMEM; + break; + } + if (!mapsi) + 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) { if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_DEBUG))) { @@ -2036,7 +2101,9 @@ uint32_t latency; 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; if (state->use_period_size_min_as_headroom) @@ -2063,8 +2130,6 @@ state->headroom = 0; 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) { /* XXX: For ALSA FireWire drivers, unlike for other ALSA drivers, buffer size @@ -2072,6 +2137,8 @@ */ latency += state->buffer_frames; } + if (rate != 0 && state->rate != 0) + latency = SPA_SCALE32_UP(latency, rate, state->rate); state->latencystate->port_direction.min_rate = state->latencystate->port_direction.max_rate = latency; @@ -2089,7 +2156,9 @@ unsigned int periods; bool match = true, planar = false; char spdif_params128 = ""; + char alsa_chmap_str256 = ""; uint32_t default_period; + const uint32_t *position = NULL; spa_log_debug(state->log, "opened:%d format:%d started:%d", state->opened, state->have_format, state->started); @@ -2103,6 +2172,8 @@ struct spa_audio_info_raw *f = &fmt->info.raw; rrate = f->rate; 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); break; } @@ -2167,6 +2238,7 @@ rrate = f->rate; rchannels = f->channels; + position = f->position; switch (f->interleave) { case 4: @@ -2255,6 +2327,28 @@ planar ? "planar" : "interleaved", rchannels); 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 buf256 = ""; + + memcpy(map.pos, position, map.n_pos * sizeof(map.pos0)); + 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_str0 = '\0'; + if (state->alsa_chmap) + snd_pcm_chmap_print(state->alsa_chmap, sizeof(alsa_chmap_str), alsa_chmap_str); + } + /* set the count of channels */ val = rchannels; CHECK(snd_pcm_hw_params_set_channels_near(hndl, params, &val), "set_channels"); @@ -2432,13 +2526,13 @@ 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 " "headroom %u start-delay:%u batch:%u tsched:%u resample:%u", state->name, snd_pcm_format_name(state->format), state->use_mmap ? "mmap" : "rw", 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->start_delay, state->is_batch, !state->disable_tsched, state->resample); @@ -2629,6 +2723,16 @@ state->name, snd_strerror(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) { snd_pcm_uframes_t silence = state->start_delay + state->threshold + state->headroom; if (state->disable_tsched) @@ -2936,10 +3040,17 @@ }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/alsa/alsa-pcm.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/alsa/alsa-pcm.h
Changed
@@ -280,6 +280,8 @@ struct spa_list driver_link; struct rt_state rt; + + snd_pcm_chmap_t *alsa_chmap; }; struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/alsa/alsa-udev.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/alsa/alsa-udev.c
Changed
@@ -725,11 +725,11 @@ static void process_card(struct impl *this, enum action action, struct card *card) { - if (card->ignored) - return; - switch (action) { case ACTION_CHANGE: { + if (card->ignored) + return; + check_access(this, card); if (card->accessible && !card->emitted) { int res = emit_added_object_info(this, card);
View file
_service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/alsa/mixer/profile-sets/usb-gaming-headset-gamefirst.conf
Added
@@ -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
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/audioconvert.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/audioconvert.c
Changed
@@ -1344,6 +1344,14 @@ 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) { struct filter_graph *g, *t; @@ -1431,7 +1439,7 @@ if (impl->setup) 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) clean_filter_handles(impl, false); @@ -1500,6 +1508,10 @@ } else if (spa_streq(k, "channelmix.lock-volumes")) 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.")) { int order = atoi(k + strlen("audioconvert.filter-graph.")); if ((res = load_filter_graph(this, s, order)) < 0) { @@ -1507,10 +1519,6 @@ order, spa_strerror(res)); } } - else if (spa_streq(k, "audioconvert.filter-graph.disable")) { - if (!*disable_filter) - *disable_filter = spa_atob(s); - } else return 0; return 1; @@ -2545,6 +2553,8 @@ this->setup = true; this->recalc = true; + sync_filter_graph(this); + return 0; } @@ -2561,6 +2571,7 @@ resample_reset(&this->resample); this->in_offset = 0; this->out_offset = 0; + this->setup = false; } static int impl_node_send_command(void *object, const struct spa_command *command) @@ -2581,7 +2592,6 @@ break; case SPA_NODE_COMMAND_Suspend: reset_node(this); - this->setup = false; SPA_FALLTHROUGH; case SPA_NODE_COMMAND_Pause: this->started = false;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/benchmark-resample.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/benchmark-resample.c
Changed
@@ -156,17 +156,17 @@ } } #endif -#if defined (HAVE_AVX) && defined(HAVE_FMA) - if (SPA_FLAG_IS_SET(cpu_flags, SPA_CPU_FLAG_AVX | SPA_CPU_FLAG_FMA3)) { +#if defined (HAVE_AVX2) && defined(HAVE_FMA) + 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++) { spa_zero(r); 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_ratesi; r.o_rate = out_ratesi; r.quality = RESAMPLE_DEFAULT_QUALITY; resample_native_init(&r); - run_test("native", "avx", &r); + run_test("native", "avx2", &r); resample_free(&r); } }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/channelmix-ops.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/channelmix-ops.c
Changed
@@ -117,6 +117,7 @@ #define SQRT3_2 1.224744871f /* sqrt(3/2) */ #define SQRT1_2 0.707106781f #define SQRT2 1.414213562f +#define HALF 0.5f #define MATRIX_NORMAL 0 #define MATRIX_DOLBY 1 @@ -127,7 +128,11 @@ #define _MASK(ch) (1ULL << _CH(ch)) #define FRONT (_MASK(FC)) #define STEREO (_MASK(FL)|_MASK(FR)) +#define TSTEREO (_MASK(TFL)|_MASK(TFR)) +#define TCSTEREO (_MASK(TFLC)|_MASK(TFRC)) #define REAR (_MASK(RL)|_MASK(RR)) +#define TREAR (_MASK(TRL)|_MASK(TRR)) +#define CREAR (_MASK(RLC)|_MASK(RRC)) #define SIDE (_MASK(SL)|_MASK(SR)) #define CHANNEL_BITS (64u) @@ -404,7 +409,7 @@ } normalize = true; } else { - spa_log_warn(mix->log, "can't assign RL"); + spa_log_warn(mix->log, "can't assign RL+RR"); } } @@ -499,6 +504,93 @@ spa_log_warn(mix->log, "can't assign LFE"); } } + if (unassigned & TSTEREO){ + if (dst_mask & STEREO) { + spa_log_info(mix->log, "assign TSTEREO to FL+FR (%f)", HALF); + _MATRIX(FL,TFL) += HALF; + _MATRIX(FR,TFR) += HALF; + } else if (dst_mask & _MASK(MONO)){ + spa_log_info(mix->log, "assign TSTEREO to MONO (%f)", 1.0f); + for (i = 0; i < MAX_CHANNELS; i++) { + matrixi_CH(TFL)= 1.0f; + matrixi_CH(TFR)= 1.0f; + } + normalize = true; + } else { + spa_log_warn(mix->log, "can't assign TSTEREO"); + } + } + if (unassigned & TCSTEREO){ + if (dst_mask & STEREO) { + spa_log_info(mix->log, "assign TCSTEREO to FL+FR (%f)", HALF); + _MATRIX(FL,TFLC) += HALF; + _MATRIX(FR,TFRC) += HALF; + } else if (dst_mask & _MASK(MONO)){ + spa_log_info(mix->log, "assign TCSTEREO to MONO (%f)", 1.0f); + for (i = 0; i < MAX_CHANNELS; i++) { + matrixi_CH(TFLC)= 1.0f; + matrixi_CH(TFRC)= 1.0f; + } + normalize = true; + } else { + spa_log_warn(mix->log, "can't assign TCSTEREO"); + } + } + if (unassigned & TREAR){ + if (dst_mask & REAR) { + if (src_mask & _MASK(RL)) { + spa_log_info(mix->log, "assign TREAR to RL+RR (%f)", HALF); + _MATRIX(RL,TRL) += HALF; + _MATRIX(RR,TRR) += HALF; + } else { + spa_log_info(mix->log, "assign TREAR to RL+RR (%f)", 1.0f); + _MATRIX(RL,TRL) += 1.0f; + _MATRIX(RR,TRR) += 1.0f; + } + keep &= ~REAR; + } else if (dst_mask & STEREO) { + spa_log_info(mix->log, "assign TREAR to FL+FR (%f)", HALF); + _MATRIX(FL,TRL) += HALF; + _MATRIX(FR,TRR) += HALF; + } else if (dst_mask & _MASK(MONO)){ + spa_log_info(mix->log, "assign TREAR to MONO (%f)", 1.0f); + for (i = 0; i < MAX_CHANNELS; i++) { + matrixi_CH(TRL)= 1.0f; + matrixi_CH(TRR)= 1.0f; + } + normalize = true; + } else { + spa_log_warn(mix->log, "can't assign TREAR"); + } + } + if (unassigned & CREAR){ + if (dst_mask & REAR) { + if (src_mask & _MASK(RL)) { + spa_log_info(mix->log, "assign CREAR to RL+RR (%f)", SQRT1_2); + _MATRIX(RL,RLC) += SQRT1_2; + _MATRIX(RR,RRC) += SQRT1_2; + } else { + spa_log_info(mix->log, "assign CREAR to RL+RR (%f)", 1.0f); + _MATRIX(RL,RLC) += 1.0f; + _MATRIX(RR,RRC) += 1.0f; + } + keep &= ~REAR; + } else if (dst_mask & STEREO) { + spa_log_info(mix->log, "assign CREAR to FL+FR (%f)", + SQRT1_2); + _MATRIX(FL,RLC) += SQRT1_2; + _MATRIX(FR,RRC) += SQRT1_2; + } else if (dst_mask & _MASK(MONO)){ + spa_log_info(mix->log, "assign CREAR to MONO (%f)", 1.0f); + for (i = 0; i < MAX_CHANNELS; i++) { + matrixi_CH(RLC)= 1.0f; + matrixi_CH(RRC)= 1.0f; + } + normalize = true; + } else { + spa_log_warn(mix->log, "can't assign CREAR"); + } + } unassigned = dst_mask & ~src_mask & keep; @@ -628,7 +720,7 @@ if (src_paired == 0) src_paired = ~0LU; - for (jc = 0, ic = 0, i = 0; i < CHANNEL_BITS; i++) { + for (jc = 0, ic = 0, i = 0; ic < dst_chan; i++) { float sum = 0.0f; char str11024, str21024; struct spa_strbuf sb1, sb2; @@ -636,12 +728,10 @@ spa_strbuf_init(&sb1, str1, sizeof(str1)); spa_strbuf_init(&sb2, str2, sizeof(str2)); - if ((dst_paired & (1UL << i)) == 0) + if (i < CHANNEL_BITS && (dst_paired & (1UL << i)) == 0) continue; - for (jc = 0, j = 0; j < CHANNEL_BITS; j++) { - if ((src_paired & (1UL << j)) == 0) - continue; - if (ic >= dst_chan || jc >= src_chan) + for (jc = 0, j = 0; jc < src_chan; j++) { + if (j < CHANNEL_BITS && (src_paired & (1UL << j)) == 0) continue; if (ic == 0) @@ -660,7 +750,7 @@ if (sb2.pos > 0) spa_log_info(mix->log, " %s", str2); if (sb1.pos > 0) { - spa_log_info(mix->log, "%-4.4s %s %f", + spa_log_info(mix->log, "%03d %-4.4s %s %f", ic, dst_mask == 0 ? "UNK" : spa_debug_type_find_short_name(spa_type_audio_channel, i + _SH), str1, sum); @@ -733,7 +823,6 @@ for (i = 0; i < dst_chan; i++) { for (j = 0; j < src_chan; j++) { float v = mix->matrixij; - spa_log_debug(mix->log, "%d %d: %f", i, j, v); if (i == 0 && j == 0) t = v; else if (t != v) @@ -748,7 +837,31 @@ SPA_FLAG_UPDATE(mix->flags, CHANNELMIX_FLAG_IDENTITY, dst_chan == src_chan && SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_COPY)); - spa_log_debug(mix->log, "flags:%08x", mix->flags); + if (SPA_UNLIKELY(spa_log_level_topic_enabled(mix->log, + SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_DEBUG))) { + char str11024, str21024; + struct spa_strbuf sb1, sb2; + spa_strbuf_init(&sb2, str2, sizeof(str2)); + for (i = 0; i < dst_chan; i++) { + spa_strbuf_init(&sb1, str1, sizeof(str1)); + for (j = 0; j < src_chan; j++) { + float v = mix->matrixij; + if (i == 0) + spa_strbuf_append(&sb2, " %03d ", j); + if (v == 0.0f) + spa_strbuf_append(&sb1, " "); + else + spa_strbuf_append(&sb1, "%1.3f ", v); + } + if (i == 0 && sb2.pos > 0) + spa_log_debug(mix->log, " %s", str2); + if (sb1.pos > 0) + spa_log_debug(mix->log, "%03d %s %03d", i, str1, i); + } + if (sb2.pos > 0) + spa_log_debug(mix->log, " %s", str2); + spa_log_debug(mix->log, "flags:%08x", mix->flags); + } } static void impl_channelmix_free(struct channelmix *mix)
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/fmt-ops-sse2.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/fmt-ops-sse2.c
Changed
@@ -284,7 +284,8 @@ } } -static void +// Non static, referenced by `fmt-ops-sse41.c`. +void conv_s24_to_f32d_2s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_channels, uint32_t n_samples) {
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/fmt-ops-ssse3.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/fmt-ops-ssse3.c
Changed
@@ -6,7 +6,8 @@ #include <tmmintrin.h> -static void +// Non static, referenced by `fmt-ops-sse41.c`. +void conv_s24_to_f32d_4s_ssse3(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_channels, uint32_t n_samples) {
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/meson.build -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/meson.build
Changed
@@ -72,15 +72,15 @@ simd_cargs += '-DHAVE_SSE41' simd_dependencies += audioconvert_sse41 endif -if have_avx and have_fma - audioconvert_avx = static_library('audioconvert_avx', - 'resample-native-avx.c', - c_args : avx_args, fma_args, '-O3', '-DHAVE_AVX', '-DHAVE_FMA', +if have_avx2 and have_fma + audioconvert_avx2_fma = static_library('audioconvert_avx2_fma', + 'resample-native-avx2.c', + c_args : avx2_args, fma_args, '-O3', '-DHAVE_AVX2', '-DHAVE_FMA', dependencies : spa_dep , install : false ) - simd_cargs += '-DHAVE_AVX', '-DHAVE_FMA' - simd_dependencies += audioconvert_avx + simd_cargs += '-DHAVE_AVX2', '-DHAVE_FMA' + simd_dependencies += audioconvert_avx2_fma endif if have_avx2 audioconvert_avx2 = static_library('audioconvert_avx2',
View file
_service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/resample-native-avx2.c
Added
@@ -0,0 +1,74 @@ +/* Spa */ +/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#include "resample-native-impl.h" + +#include <assert.h> +#include <immintrin.h> + +static inline void inner_product_avx2(float *d, const float * SPA_RESTRICT s, + const float * SPA_RESTRICT taps, uint32_t n_taps) +{ + __m256 sy2 = { _mm256_setzero_ps(), _mm256_setzero_ps() }, ty; + __m128 sx2, tx; + uint32_t i = 0; + uint32_t n_taps4 = n_taps & ~0xf; + + for (; i < n_taps4; i += 16) { + ty = _mm256_loadu_ps(s + i + 0); + sy0 = _mm256_fmadd_ps(ty, _mm256_load_ps(taps + i + 0), sy0); + ty = _mm256_loadu_ps(s + i + 8); + sy1 = _mm256_fmadd_ps(ty, _mm256_load_ps(taps + i + 8), sy1); + } + sy0 = _mm256_add_ps(sy1, sy0); + sx1 = _mm256_extractf128_ps(sy0, 1); + sx0 = _mm256_extractf128_ps(sy0, 0); + for (; i < n_taps; i += 8) { + tx = _mm_loadu_ps(s + i + 0); + sx0 = _mm_fmadd_ps(tx, _mm_load_ps(taps + i + 0), sx0); + tx = _mm_loadu_ps(s + i + 4); + sx1 = _mm_fmadd_ps(tx, _mm_load_ps(taps + i + 4), sx1); + } + sx0 = _mm_add_ps(sx0, sx1); + sx0 = _mm_hadd_ps(sx0, sx0); + sx0 = _mm_hadd_ps(sx0, sx0); + _mm_store_ss(d, sx0); +} + +static inline void inner_product_ip_avx2(float *d, const float * SPA_RESTRICT s, + const float * SPA_RESTRICT t0, const float * SPA_RESTRICT t1, float x, + uint32_t n_taps) +{ + __m256 sy2 = { _mm256_setzero_ps(), _mm256_setzero_ps() }, ty; + __m128 sx2, tx; + uint32_t i, n_taps4 = n_taps & ~0xf; + + for (i = 0; i < n_taps4; i += 16) { + ty = _mm256_loadu_ps(s + i + 0); + sy0 = _mm256_fmadd_ps(ty, _mm256_load_ps(t0 + i + 0), sy0); + sy1 = _mm256_fmadd_ps(ty, _mm256_load_ps(t1 + i + 0), sy1); + ty = _mm256_loadu_ps(s + i + 8); + sy0 = _mm256_fmadd_ps(ty, _mm256_load_ps(t0 + i + 8), sy0); + sy1 = _mm256_fmadd_ps(ty, _mm256_load_ps(t1 + i + 8), sy1); + } + sx0 = _mm_add_ps(_mm256_extractf128_ps(sy0, 0), _mm256_extractf128_ps(sy0, 1)); + sx1 = _mm_add_ps(_mm256_extractf128_ps(sy1, 0), _mm256_extractf128_ps(sy1, 1)); + + for (; i < n_taps; i += 8) { + tx = _mm_loadu_ps(s + i + 0); + sx0 = _mm_fmadd_ps(tx, _mm_load_ps(t0 + i + 0), sx0); + sx1 = _mm_fmadd_ps(tx, _mm_load_ps(t1 + i + 0), sx1); + tx = _mm_loadu_ps(s + i + 4); + sx0 = _mm_fmadd_ps(tx, _mm_load_ps(t0 + i + 4), sx0); + sx1 = _mm_fmadd_ps(tx, _mm_load_ps(t1 + i + 4), sx1); + } + sx1 = _mm_mul_ps(_mm_sub_ps(sx1, sx0), _mm_load1_ps(&x)); + sx0 = _mm_add_ps(sx0, sx1); + sx0 = _mm_hadd_ps(sx0, sx0); + sx0 = _mm_hadd_ps(sx0, sx0); + _mm_store_ss(d, sx0); +} + +MAKE_RESAMPLER_FULL(avx2); +MAKE_RESAMPLER_INTER(avx2);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/resample-native-impl.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/resample-native-impl.h
Changed
@@ -59,6 +59,7 @@ float *filter; float *hist_mem; const struct resample_info *info; + bool force_inter; }; #define DEFINE_RESAMPLER(type,arch) \ @@ -175,7 +176,7 @@ DEFINE_RESAMPLER(full,ssse3); DEFINE_RESAMPLER(inter,ssse3); #endif -#if defined (HAVE_AVX) && defined(HAVE_FMA) -DEFINE_RESAMPLER(full,avx); -DEFINE_RESAMPLER(inter,avx); +#if defined (HAVE_AVX2) && defined(HAVE_FMA) +DEFINE_RESAMPLER(full,avx2); +DEFINE_RESAMPLER(inter,avx2); #endif
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/resample-native.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/resample-native.c
Changed
@@ -13,6 +13,9 @@ SPA_LOG_TOPIC_DEFINE(resample_log_topic, "spa.resample"); +#define MAX_TAPS (1u<<18) +#define MAX_PHASES 1024u + #define INHERIT_PARAM(c,q,p) if ((c)->paramsp == 0.0) (c)->paramsp = (q)->paramsp; struct quality { @@ -209,8 +212,8 @@ #if defined (HAVE_NEON) MAKE(F32, copy_c, full_neon, inter_neon, SPA_CPU_FLAG_NEON), #endif -#if defined(HAVE_AVX) && defined(HAVE_FMA) - MAKE(F32, copy_c, full_avx, inter_avx, SPA_CPU_FLAG_AVX | SPA_CPU_FLAG_FMA3), +#if defined(HAVE_AVX2) && defined(HAVE_FMA) + MAKE(F32, copy_c, full_avx2, inter_avx2, SPA_CPU_FLAG_AVX2 | SPA_CPU_FLAG_FMA3), #endif #if defined (HAVE_SSSE3) MAKE(F32, copy_c, full_ssse3, inter_ssse3, SPA_CPU_FLAG_SSSE3 | SPA_CPU_FLAG_SLOW_UNALIGNED), @@ -263,7 +266,7 @@ in_rate = UINT32_TO_FIXP(r->i_rate); out_rate = r->o_rate; - if (rate != 1.0) { + if (rate != 1.0 || data->force_inter) { in_rate.value = (uint64_t)round(in_rate.value / rate); data->func = data->info->process_inter; } @@ -503,16 +506,17 @@ /* multiple of 8 taps to ease simd optimizations */ n_taps = SPA_ROUND_UP_N((uint32_t)ceil(n_taps / scale), 8); - n_taps = SPA_MIN(n_taps, 1u << 18); + n_taps = SPA_MIN(n_taps, MAX_TAPS); /* try to get at least 256 phases so that interpolation is * accurate enough when activated */ - n_phases = out_rate; + n_phases = SPA_MIN(MAX_PHASES, out_rate); oversample = (255 + n_phases) / n_phases; n_phases *= oversample; filter_stride = SPA_ROUND_UP_N(n_taps * sizeof(float), 64); filter_size = filter_stride * (n_phases + 1); + history_stride = SPA_ROUND_UP_N(2 * n_taps * sizeof(float), 64); history_size = r->channels * history_stride; @@ -530,6 +534,7 @@ d->n_phases = n_phases; d->in_rate = UINT32_TO_FIXP(in_rate); d->out_rate = out_rate; + d->force_inter = out_rate > n_phases; d->gcd = gcd; d->pm = (float)n_phases / r->o_rate / FIXP_SCALE; d->filter = SPA_PTROFF_ALIGN(d, sizeof(struct native_data), 64, float);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/resample.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/resample.h
Changed
@@ -8,7 +8,10 @@ #include <spa/support/cpu.h> #include <spa/support/log.h> +#ifndef RESAMPLE_DEFAULT_QUALITY #define RESAMPLE_DEFAULT_QUALITY 4 +#endif + #define RESAMPLE_WINDOW_DEFAULT RESAMPLE_WINDOW_EXP #define RESAMPLE_MAX_PARAMS 16
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audioconvert/spa-resample.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audioconvert/spa-resample.c
Changed
@@ -187,6 +187,8 @@ d->oname, sf_strerror(NULL)); return -EIO; } + sf_command(d->ofile, SFC_SET_CLIPPING, NULL, 1); + if (d->verbose) { fprintf(stdout, "input '%s': channels:%d rate:%d format:%s\n", d->iname, d->iinfo.channels, d->iinfo.samplerate, @@ -296,9 +298,8 @@ if (pout_len > 0) { for (k = 0, i = 0; i < pout_len; i++) { - for (j = 0; j < channels; j++) { + for (j = 0; j < channels; j++) obufk++ = outMAX_SAMPLES * j + i; - } } pout_len = sf_writef_float(d->ofile, obuf, pout_len);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audiomixer/audiomixer.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audiomixer/audiomixer.c
Changed
@@ -790,6 +790,7 @@ uint32_t n_buffers, maxsize; struct buffer **buffers; struct buffer *outb; + struct spa_data *d; const void **datas; uint32_t cycle = this->position->clock.cycle & 1; @@ -856,12 +857,11 @@ outport->n_buffers); return -EPIPE; } + d = outb->buf.datas; - if (n_buffers == 1) { + if (n_buffers == 1 && SPA_FLAG_IS_SET(d0.flags, SPA_DATA_FLAG_DYNAMIC)) { *outb->buffer = *buffers0->buffer; } else { - struct spa_data *d = outb->buf.datas; - *outb->buffer = outb->buf; maxsize = SPA_MIN(maxsize, d0.maxsize);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audiomixer/benchmark-mix-ops.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audiomixer/benchmark-mix-ops.c
Changed
@@ -143,9 +143,9 @@ run_test("test_f32", "sse", mix_f32_sse); } #endif -#if defined (HAVE_AVX) - if (cpu_flags & SPA_CPU_FLAG_AVX) { - run_test("test_f32", "avx", mix_f32_avx); +#if defined (HAVE_AVX2) + if (cpu_flags & SPA_CPU_FLAG_AVX2) { + run_test("test_f32", "avx2", mix_f32_avx2); } #endif }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audiomixer/meson.build -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audiomixer/meson.build
Changed
@@ -35,15 +35,15 @@ simd_cargs += '-DHAVE_SSE2' simd_dependencies += audiomixer_sse2 endif -if have_avx and have_fma - audiomixer_avx = static_library('audiomixer_avx', - 'mix-ops-avx.c', - c_args : avx_args, fma_args, '-O3', '-DHAVE_AVX', '-DHAVE_FMA', +if have_avx2 and have_fma + audiomixer_avx2 = static_library('audiomixer_avx2', + 'mix-ops-avx2.c', + c_args : avx2_args, fma_args, '-O3', '-DHAVE_AVX2', '-DHAVE_FMA', dependencies : spa_dep , install : false ) - simd_cargs += '-DHAVE_AVX', '-DHAVE_FMA' - simd_dependencies += audiomixer_avx + simd_cargs += '-DHAVE_AVX2', '-DHAVE_FMA' + simd_dependencies += audiomixer_avx2 endif audiomixer_lib = static_library('audiomixer',
View file
_service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audiomixer/mix-ops-avx2.c
Added
@@ -0,0 +1,68 @@ +/* Spa */ +/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#include <string.h> +#include <stdio.h> +#include <math.h> + +#include <spa/utils/defs.h> + +#include "mix-ops.h" + +#include <immintrin.h> + +void +mix_f32_avx2(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, + uint32_t n_src, uint32_t n_samples) +{ + n_samples *= ops->n_channels; + + if (n_src == 0) + memset(dst, 0, n_samples * ops->n_channels * sizeof(float)); + else if (n_src == 1) { + if (dst != src0) + spa_memcpy(dst, src0, n_samples * sizeof(float)); + } else { + uint32_t i, n, unrolled; + const float **s = (const float **)src; + float *d = dst; + + if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 32))) { + unrolled = n_samples & ~31; + for (i = 0; i < n_src; i++) { + if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 32))) { + unrolled = 0; + break; + } + } + } else + unrolled = 0; + + for (n = 0; n < unrolled; n += 32) { + __m256 in4; + + in0 = _mm256_load_ps(&s0n + 0); + in1 = _mm256_load_ps(&s0n + 8); + in2 = _mm256_load_ps(&s0n + 16); + in3 = _mm256_load_ps(&s0n + 24); + for (i = 1; i < n_src; i++) { + in0 = _mm256_add_ps(in0, _mm256_load_ps(&sin + 0)); + in1 = _mm256_add_ps(in1, _mm256_load_ps(&sin + 8)); + in2 = _mm256_add_ps(in2, _mm256_load_ps(&sin + 16)); + in3 = _mm256_add_ps(in3, _mm256_load_ps(&sin + 24)); + } + _mm256_store_ps(&dn + 0, in0); + _mm256_store_ps(&dn + 8, in1); + _mm256_store_ps(&dn + 16, in2); + _mm256_store_ps(&dn + 24, in3); + } + for (; n < n_samples; n++) { + __m128 in1; + in0 = _mm_load_ss(&s0n); + for (i = 1; i < n_src; i++) + in0 = _mm_add_ss(in0, _mm_load_ss(&sin)); + _mm_store_ss(&dn, in0); + } + } +}
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audiomixer/mix-ops.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audiomixer/mix-ops.c
Changed
@@ -26,9 +26,9 @@ static struct mix_info mix_table = { /* f32 */ -#if defined(HAVE_AVX) - { SPA_AUDIO_FORMAT_F32, 0, SPA_CPU_FLAG_AVX, 4, mix_f32_avx }, - { SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_AVX, 4, mix_f32_avx }, +#if defined(HAVE_AVX2) + { SPA_AUDIO_FORMAT_F32, 0, SPA_CPU_FLAG_AVX2, 4, mix_f32_avx2 }, + { SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_AVX2, 4, mix_f32_avx2 }, #endif #if defined (HAVE_SSE) { SPA_AUDIO_FORMAT_F32, 0, SPA_CPU_FLAG_SSE, 4, mix_f32_sse },
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audiomixer/mix-ops.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audiomixer/mix-ops.h
Changed
@@ -144,6 +144,6 @@ #if defined(HAVE_SSE2) DEFINE_FUNCTION(f64, sse2); #endif -#if defined(HAVE_AVX) -DEFINE_FUNCTION(f32, avx); +#if defined(HAVE_AVX2) +DEFINE_FUNCTION(f32, avx2); #endif
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audiomixer/test-mix-ops.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audiomixer/test-mix-ops.c
Changed
@@ -220,11 +220,11 @@ run_test("test_f32_4_sse", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_f32_sse); } #endif -#if defined(HAVE_AVX) - if (cpu_flags & SPA_CPU_FLAG_AVX) { - run_test("test_f32_0_avx", NULL, 0, out, sizeof(out), SPA_N_ELEMENTS(out), mix_f32_avx); - run_test("test_f32_1_avx", src, 1, in_1, sizeof(in_1), SPA_N_ELEMENTS(in_1), mix_f32_avx); - run_test("test_f32_4_avx", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_f32_avx); +#if defined(HAVE_AVX2) + if (cpu_flags & SPA_CPU_FLAG_AVX2) { + run_test("test_f32_0_avx", NULL, 0, out, sizeof(out), SPA_N_ELEMENTS(out), mix_f32_avx2); + run_test("test_f32_1_avx", src, 1, in_1, sizeof(in_1), SPA_N_ELEMENTS(in_1), mix_f32_avx2); + run_test("test_f32_4_avx", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_f32_avx2); } #endif }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/audiotestsrc/audiotestsrc.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/audiotestsrc/audiotestsrc.c
Changed
@@ -113,6 +113,7 @@ struct props props; struct spa_io_clock *clock; struct spa_io_position *position; + bool following; struct spa_hook_list hooks; struct spa_callbacks callbacks; @@ -306,6 +307,8 @@ default: return -ENOENT; } + this->following = this->position && this->clock && this->position->clock.id != this->clock->id; + return 0; } @@ -422,7 +425,8 @@ this->sample_count += n_samples; this->elapsed_time = SAMPLES_TO_TIME(this, this->sample_count); - set_timer(this, true); + if (!this->following) + set_timer(this, true); io->buffer_id = b->id; io->status = SPA_STATUS_HAVE_DATA; @@ -475,7 +479,8 @@ this->elapsed_time = 0; this->started = true; - set_timer(this, true); + if (!this->following) + set_timer(this, true); break; } case SPA_NODE_COMMAND_Suspend: @@ -891,7 +896,7 @@ b->outstanding = false; spa_list_append(&port->empty, &b->link); - if (!this->props.live) + if (!this->props.live && !this->following) set_timer(this, true); } @@ -966,7 +971,7 @@ io->buffer_id = SPA_ID_INVALID; } - if (!this->props.live) + if (!this->props.live || this->following) return make_buffer(this); else return SPA_STATUS_OK;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/backend-native.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/backend-native.c
Changed
@@ -63,6 +63,9 @@ #define RFCOMM_MESSAGE_MAX_LENGTH 256 +#define BT_CODEC_CVSD 0x02 +#define BT_CODEC_MSBC 0x05 + enum { HFP_AG_INITIAL_CODEC_SETUP_NONE = 0, HFP_AG_INITIAL_CODEC_SETUP_SEND, @@ -112,6 +115,7 @@ int hfp_default_speaker_volume; struct spa_source sco; + unsigned int hfphsp_sco_datapath; const struct spa_bt_quirks *quirks; @@ -125,6 +129,7 @@ struct spa_source *ring_timer; void *upower; struct spa_bt_telephony *telephony; + bool pts; }; struct transport_data { @@ -296,6 +301,33 @@ return NULL; } +static void sco_offload_btcodec(struct impl *backend, int sock, bool msbc) +{ + int err; + char buffer255; + struct bt_codecs *codecs; + + if (backend->hfphsp_sco_datapath == HFP_SCO_DEFAULT_DATAPATH) + return; + + spa_log_info(backend->log, "sock(%d) msbc(%d)", sock, msbc); + + memset(buffer, 0, sizeof(buffer)); + codecs = (void *)buffer; + if (msbc) + codecs->codecs0.id = BT_CODEC_MSBC; + else + codecs->codecs0.id = BT_CODEC_CVSD; + codecs->num_codecs = 1; + codecs->codecs0.data_path_id = backend->hfphsp_sco_datapath; + + err = setsockopt(sock, SOL_BLUETOOTH, BT_CODEC, codecs, sizeof(buffer)); + if (err < 0) + spa_log_error(backend->log, "ERROR: %s (%d)", strerror(errno), errno); + else + spa_log_info(backend->log, "set offload codec succeeded"); +} + static DBusHandlerResult profile_release(DBusConnection *conn, DBusMessage *m, void *userdata) { if (!reply_with_error(conn, m, BLUEZ_PROFILE_INTERFACE ".Error.NotImplemented", "Method not implemented")) @@ -1438,6 +1470,15 @@ rfcomm_send_error(rfcomm, error); return true; } + } else if (spa_strstartswith(buf, "AT+BLDN") && backend->pts) { + enum cmee_error error; + + /* For PTS tests HFP/AG/OCL/BV-01-C and HFP/AG/OCL/BV-02-C, fake last dial + * number by calling first memory */ + if (!mm_do_call(backend->modemmanager, ">1", rfcomm, &error)) { + rfcomm_send_error(rfcomm, error); + return true; + } } else if (spa_strstartswith(buf, "AT+CHUP")) { enum cmee_error error; @@ -2585,6 +2626,8 @@ } } + sco_offload_btcodec(backend, sock, transparent); + return spa_steal_fd(sock); } @@ -2763,7 +2806,7 @@ goto fail; #ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE - if (!mm_is_available(backend->modemmanager)) + if (!td->rfcomm->device->disable_dummy_call) rfcomm_hfp_ag_set_cind(td->rfcomm, true); #endif @@ -2818,7 +2861,7 @@ spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_IDLE); #ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE - if (!mm_is_available(backend->modemmanager)) + if (!td->rfcomm->device->disable_dummy_call) rfcomm_hfp_ag_set_cind(td->rfcomm, false); #endif @@ -2932,7 +2975,11 @@ /* Find transport for local and remote address */ spa_list_for_each(rfcomm, &backend->rfcomm_list, link) { - if ((rfcomm->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY) && + /* Audio connection is allowed from both side with legacy peer, i.e. HSP or codec negotion not supported + * (except if PTS workaround has been enabled in which case audio coonection is allowed as for HSP), + * or only from the HFP Audio Gateway. */ + if ((((!rfcomm->codec_negotiation_supported || backend->pts) && (rfcomm->profile & SPA_BT_PROFILE_HEADSET_AUDIO)) || + (rfcomm->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY)) && rfcomm->transport && spa_streq(rfcomm->device->address, remote_address) && spa_streq(rfcomm->device->adapter->address, local_address)) { @@ -2946,7 +2993,7 @@ return; } - spa_assert(t->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY); + spa_assert(t->profile & SPA_BT_PROFILE_HEADSET_AUDIO); if (rfcomm->telephony_ag && rfcomm->telephony_ag->transport.rejectSCO) { spa_log_info(backend->log, "rejecting SCO, AudioGatewayTransport1.RejectSCO=true"); @@ -3387,6 +3434,43 @@ } spa_bt_device_add_profile(d, profile); + /* Prevent to connect HSP/HFP in both directions, i.e. HS->AG and AG->HS. + * This may only occur when connecting to a device which provides both + * HS and AG which should not be the case with headsets and phones. */ + spa_list_for_each(rfcomm, &backend->rfcomm_list, link) { + if (spa_streq(rfcomm->device->address, d->address) && + spa_streq(rfcomm->device->adapter->address, d->adapter->address)) { + bool connected = false; + + switch (profile) { + case SPA_BT_PROFILE_HFP_HF: + if (rfcomm->profile == SPA_BT_PROFILE_HFP_AG) + connected = true; + break; + case SPA_BT_PROFILE_HFP_AG: + if (rfcomm->profile == SPA_BT_PROFILE_HFP_HF) + connected = true; + break; + case SPA_BT_PROFILE_HSP_HS: + if (rfcomm->profile == SPA_BT_PROFILE_HSP_AG) + connected = true; + break; + case SPA_BT_PROFILE_HSP_AG: + if (rfcomm->profile == SPA_BT_PROFILE_HSP_HS) + connected = true; + break; + default: + spa_log_warn(backend->log, "Unsupported profile: %s", handler); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (connected) { + spa_log_debug(backend->log, "Already connected in the opposite direction"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + } + dbus_message_iter_next(&it); dbus_message_iter_get_basic(&it, &fd); @@ -4017,6 +4101,16 @@ backend->hfp_disable_nrec = false; } +static void parse_hfp_pts(struct impl *backend, const struct spa_dict *info) +{ + const char *str; + + if ((str = spa_dict_lookup(info, "bluez5.hfphsp-backend-native-pts")) != NULL) + backend->pts = spa_atob(str); + else + backend->pts = false; +} + static void parse_hfp_default_volumes(struct impl *backend, const struct spa_dict *info) { const char *str; @@ -4040,6 +4134,14 @@ backend->hfp_default_speaker_volume = SPA_BT_VOLUME_HS_MAX; } +static void parse_sco_datapath(struct impl *backend, const struct spa_dict *info) +{ + backend->hfphsp_sco_datapath = HFP_SCO_DEFAULT_DATAPATH; + + spa_atou32(spa_dict_lookup(info, "bluez5.hw-offload-datapath"), + &backend->hfphsp_sco_datapath, 10); +} + static const struct spa_bt_backend_implementation backend_impl = { SPA_VERSION_BT_BACKEND_IMPLEMENTATION,
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/bap-codec-caps.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/bap-codec-caps.h
Changed
@@ -153,6 +153,69 @@ #define BT_ISO_QOS_TARGET_LATENCY_BALANCED 0x02 #define BT_ISO_QOS_TARGET_LATENCY_RELIABILITY 0x03 + +#define BT_TMAP_UUID "00001855-0000-1000-8000-00805f9b34fb" + +#define BT_TMAP_ROLE_CG_STR "cg" +#define BT_TMAP_ROLE_CT_STR "ct" +#define BT_TMAP_ROLE_UMS_STR "ums" +#define BT_TMAP_ROLE_UMR_STR "umr" +#define BT_TMAP_ROLE_BMS_STR "bms" +#define BT_TMAP_ROLE_BMR_STR "bmr" + +#define BT_GMAP_ROLE_UGG_STR "ugg" +#define BT_GMAP_ROLE_UGT_STR "ugt" +#define BT_GMAP_ROLE_BGS_STR "bgs" +#define BT_GMAP_ROLE_BGR_STR "bgr" + +#define BT_TMAP_ROLE_LIST(role) \ + role(BT_TMAP_ROLE_CG) \ + role(BT_TMAP_ROLE_CT) \ + role(BT_TMAP_ROLE_UMS) \ + role(BT_TMAP_ROLE_UMR) \ + role(BT_TMAP_ROLE_BMS) \ + role(BT_TMAP_ROLE_BMR) + +#define BT_GMAP_UUID "00001858-0000-1000-8000-00805f9b34fb" + +#define BT_GMAP_UGG_MULTIPLEX_STR "ugg-multiplex" +#define BT_GMAP_UGG_96KBPS_SOURCE_STR "ugg-96kbps-source" +#define BT_GMAP_UGG_MULTISINK_STR "ugg-multisink" + +#define BT_GMAP_UGT_SOURCE_STR "ugt-source" +#define BT_GMAP_UGT_80KBPS_SOURCE_STR "ugt-80kbps-source" +#define BT_GMAP_UGT_SINK_STR "ugt-sink" +#define BT_GMAP_UGT_64KBPS_SINK_STR "ugt-64kbps-sink" +#define BT_GMAP_UGT_MULTIPLEX_STR "ugt-multiplex" +#define BT_GMAP_UGT_MULTISINK_STR "ugt-multisink" +#define BT_GMAP_UGT_MULTISOURCE_STR "ugt-multisource" + +#define BT_GMAP_BGS_96KBPS_STR "bgs-96kbps" + +#define BT_GMAP_BGR_MULTISINK_STR "bgr-multisink" +#define BT_GMAP_BGR_MULTIPLEX_STR "bgr-multiplex" + +#define BT_GMAP_ROLE_LIST(role) \ + role(BT_GMAP_ROLE_UGG) \ + role(BT_GMAP_ROLE_UGT) \ + role(BT_GMAP_ROLE_BGS) \ + role(BT_GMAP_ROLE_BGR) + +#define BT_GMAP_FEATURE_LIST(feature) \ + feature(BT_GMAP_UGG_MULTIPLEX) \ + feature(BT_GMAP_UGG_96KBPS_SOURCE) \ + feature(BT_GMAP_UGG_MULTISINK) \ + feature(BT_GMAP_UGT_SOURCE) \ + feature(BT_GMAP_UGT_80KBPS_SOURCE) \ + feature(BT_GMAP_UGT_SINK) \ + feature(BT_GMAP_UGT_64KBPS_SINK) \ + feature(BT_GMAP_UGT_MULTIPLEX) \ + feature(BT_GMAP_UGT_MULTISINK) \ + feature(BT_GMAP_UGT_MULTISOURCE) \ + feature(BT_GMAP_BGS_96KBPS) \ + feature(BT_GMAP_BGR_MULTISINK) \ + feature(BT_GMAP_BGR_MULTIPLEX) + struct bap_endpoint_qos { uint8_t framing; uint8_t phy;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/bap-codec-lc3.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/bap-codec-lc3.c
Changed
@@ -55,6 +55,7 @@ int latency; int64_t delay; int framing; + char *force_target_latency; }; struct pac_data { @@ -76,6 +77,7 @@ uint16_t latency; uint32_t delay; unsigned int priority; + char *tag; }; typedef struct { @@ -96,50 +98,50 @@ struct settings settings; }; -#define BAP_QOS(name_, rate_, duration_, framing_, framelen_, rtn_, latency_, delay_, priority_) \ +#define BAP_QOS(name_, rate_, duration_, framing_, framelen_, rtn_, latency_, delay_, priority_, tag_) \ ((struct bap_qos){ .name = (name_), .rate = (rate_), .frame_duration = (duration_), .framing = (framing_), \ .framelen = (framelen_), .retransmission = (rtn_), .latency = (latency_), \ - .delay = (delay_), .priority = (priority_) }) + .delay = (delay_), .priority = (priority_), .tag = (tag_) }) static const struct bap_qos bap_qos_configs = { /* Priority: low-latency > high-reliability, 7.5ms > 10ms, * bigger frequency and sdu better */ /* BAP v1.0.1 Table 5.2; low-latency */ - BAP_QOS("8_1_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 2, 8, 40000, 30), /* 8_1_1 */ - BAP_QOS("8_2_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 2, 10, 40000, 20), /* 8_2_1 */ - BAP_QOS("16_1_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 2, 8, 40000, 31), /* 16_1_1 */ - BAP_QOS("16_2_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 2, 10, 40000, 21), /* 16_2_1 (mandatory) */ - BAP_QOS("24_1_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 2, 8, 40000, 32), /* 24_1_1 */ - BAP_QOS("24_2_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 2, 10, 40000, 22), /* 24_2_1 */ - BAP_QOS("32_1_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 2, 8, 40000, 33), /* 32_1_1 */ - BAP_QOS("32_2_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 2, 10, 40000, 23), /* 32_2_1 */ - BAP_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 5, 24, 40000, 34), /* 441_1_1 */ - BAP_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 5, 31, 40000, 24), /* 441_2_1 */ - BAP_QOS("48_1_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 5, 15, 40000, 35), /* 48_1_1 */ - BAP_QOS("48_2_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 5, 20, 40000, 25), /* 48_2_1 */ - BAP_QOS("48_3_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 5, 15, 40000, 36), /* 48_3_1 */ - BAP_QOS("48_4_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 5, 20, 40000, 26), /* 48_4_1 */ - BAP_QOS("48_5_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 5, 15, 40000, 37), /* 48_5_1 */ - BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 5, 20, 40000, 27), /* 48_6_1 */ + BAP_QOS("8_1_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 2, 8, 40000, 30, "low-latency"), /* 8_1_1 */ + BAP_QOS("8_2_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 2, 10, 40000, 20, "low-latency"), /* 8_2_1 */ + BAP_QOS("16_1_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 2, 8, 40000, 31, "low-latency"), /* 16_1_1 */ + BAP_QOS("16_2_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 2, 10, 40000, 21, "low-latency"), /* 16_2_1 (mandatory) */ + BAP_QOS("24_1_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 2, 8, 40000, 32, "low-latency"), /* 24_1_1 */ + BAP_QOS("24_2_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 2, 10, 40000, 22, "low-latency"), /* 24_2_1 */ + BAP_QOS("32_1_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 2, 8, 40000, 33, "low-latency"), /* 32_1_1 */ + BAP_QOS("32_2_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 2, 10, 40000, 23, "low-latency"), /* 32_2_1 */ + BAP_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 5, 24, 40000, 34, "low-latency"), /* 441_1_1 */ + BAP_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 5, 31, 40000, 24, "low-latency"), /* 441_2_1 */ + BAP_QOS("48_1_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 5, 15, 40000, 35, "low-latency"), /* 48_1_1 */ + BAP_QOS("48_2_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 5, 20, 40000, 25, "low-latency"), /* 48_2_1 */ + BAP_QOS("48_3_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 5, 15, 40000, 36, "low-latency"), /* 48_3_1 */ + BAP_QOS("48_4_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 5, 20, 40000, 26, "low-latency"), /* 48_4_1 */ + BAP_QOS("48_5_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 5, 15, 40000, 37, "low-latency"), /* 48_5_1 */ + BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 5, 20, 40000, 27, "low-latency"), /* 48_6_1 */ /* BAP v1.0.1 Table 5.2; high-reliability */ - BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 13, 75, 40000, 10), /* 8_1_2 */ - BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 13, 95, 40000, 0), /* 8_2_2 */ - BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 13, 75, 40000, 11), /* 16_1_2 */ - BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 13, 95, 40000, 1), /* 16_2_2 */ - BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 13, 75, 40000, 12), /* 24_1_2 */ - BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2), /* 24_2_2 */ - BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13), /* 32_1_2 */ - BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3), /* 32_2_2 */ - BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 14), /* 441_1_2 */ - BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 4), /* 441_2_2 */ - BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 15), /* 48_1_2 */ - BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 5), /* 48_2_2 */ - BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 16), /* 48_3_2 */ - BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 6), /* 48_4_2 */ - BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 17), /* 48_5_2 */ - BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 7), /* 48_6_2 */ + BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 13, 75, 40000, 10, "high-reliabilty"), /* 8_1_2 */ + BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 13, 95, 40000, 0, "high-reliabilty"), /* 8_2_2 */ + BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 13, 75, 40000, 11, "high-reliabilty"), /* 16_1_2 */ + BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 13, 95, 40000, 1, "high-reliabilty"), /* 16_2_2 */ + BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 13, 75, 40000, 12, "high-reliabilty"), /* 24_1_2 */ + BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2, "high-reliabilty"), /* 24_2_2 */ + BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13, "high-reliabilty"), /* 32_1_2 */ + BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3, "high-reliabilty"), /* 32_2_2 */ + BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 54, "high-reliabilty"), /* 441_1_2 */ + BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 44, "high-reliabilty"), /* 441_2_2 */ + BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 55, "high-reliabilty"), /* 48_1_2 */ + BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 45, "high-reliabilty"), /* 48_2_2 */ + BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 56, "high-reliabilty"), /* 48_3_2 */ + BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 46, "high-reliabilty"), /* 48_4_2 */ + BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 57, "high-reliabilty"), /* 48_5_2 */ + BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 47, "high-reliabilty"), /* 48_6_2 */ }; static const struct bap_qos bap_bcast_qos_configs = { @@ -147,40 +149,40 @@ * bigger frequency and sdu better */ /* BAP v1.0.1 Table 6.4; low-latency */ - BAP_QOS("8_1_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 2, 8, 40000, 30), /* 8_1_1 */ - BAP_QOS("8_2_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 2, 10, 40000, 20), /* 8_2_1 */ - BAP_QOS("16_1_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 2, 8, 40000, 31), /* 16_1_1 */ - BAP_QOS("16_2_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 2, 10, 40000, 21), /* 16_2_1 (mandatory) */ - BAP_QOS("24_1_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 2, 8, 40000, 32), /* 24_1_1 */ - BAP_QOS("24_2_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 2, 10, 40000, 22), /* 24_2_1 */ - BAP_QOS("32_1_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 2, 8, 40000, 33), /* 32_1_1 */ - BAP_QOS("32_2_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 2, 10, 40000, 23), /* 32_2_1 */ - BAP_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 24, 40000, 34), /* 441_1_1 */ - BAP_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 31, 40000, 24), /* 441_2_1 */ - BAP_QOS("48_1_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 15, 40000, 35), /* 48_1_1 */ - BAP_QOS("48_2_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 20, 40000, 25), /* 48_2_1 */ - BAP_QOS("48_3_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 15, 40000, 36), /* 48_3_1 */ - BAP_QOS("48_4_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 20, 40000, 26), /* 48_4_1 */ - BAP_QOS("48_5_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 15, 40000, 37), /* 48_5_1 */ - BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 20, 40000, 27), /* 48_6_1 */ + BAP_QOS("8_1_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 2, 8, 40000, 30, "low-latency"), /* 8_1_1 */ + BAP_QOS("8_2_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 2, 10, 40000, 20, "low-latency"), /* 8_2_1 */ + BAP_QOS("16_1_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 2, 8, 40000, 31, "low-latency"), /* 16_1_1 */ + BAP_QOS("16_2_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 2, 10, 40000, 21, "low-latency"), /* 16_2_1 (mandatory) */ + BAP_QOS("24_1_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 2, 8, 40000, 32, "low-latency"), /* 24_1_1 */ + BAP_QOS("24_2_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 2, 10, 40000, 22, "low-latency"), /* 24_2_1 */ + BAP_QOS("32_1_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 2, 8, 40000, 33, "low-latency"), /* 32_1_1 */ + BAP_QOS("32_2_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 2, 10, 40000, 23, "low-latency"), /* 32_2_1 */ + BAP_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 24, 40000, 34, "low-latency"), /* 441_1_1 */ + BAP_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 31, 40000, 24, "low-latency"), /* 441_2_1 */ + BAP_QOS("48_1_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 15, 40000, 35, "low-latency"), /* 48_1_1 */ + BAP_QOS("48_2_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 20, 40000, 25, "low-latency"), /* 48_2_1 */ + BAP_QOS("48_3_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 15, 40000, 36, "low-latency"), /* 48_3_1 */ + BAP_QOS("48_4_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 20, 40000, 26, "low-latency"), /* 48_4_1 */ + BAP_QOS("48_5_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 15, 40000, 37, "low-latency"), /* 48_5_1 */ + BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 20, 40000, 27, "low-latency"), /* 48_6_1 */ /* BAP v1.0.1 Table 6.4; high-reliability */ - BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 4, 45, 40000, 10), /* 8_1_2 */ - BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 4, 60, 40000, 0), /* 8_2_2 */ - BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 4, 45, 40000, 11), /* 16_1_2 */ - BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 4, 60, 40000, 1), /* 16_2_2 */ - BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 4, 45, 40000, 12), /* 24_1_2 */ - BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 4, 60, 40000, 2), /* 24_2_2 */ - BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 4, 45, 40000, 13), /* 32_1_2 */ - BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 4, 60, 40000, 3), /* 32_2_2 */ - BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 54, 40000, 14), /* 441_1_2 */ - BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 60, 40000, 4), /* 441_2_2 */ - BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 50, 40000, 15), /* 48_1_2 */ - BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 65, 40000, 5), /* 48_2_2 */ - BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 50, 40000, 16), /* 48_3_2 */ - BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 65, 40000, 6), /* 48_4_2 */ - BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 50, 40000, 17), /* 48_5_2 */ - BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 65, 40000, 7), /* 48_6_2 */ + BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 4, 45, 40000, 10, "high-reliabilty"), /* 8_1_2 */ + BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 4, 60, 40000, 0, "high-reliabilty"), /* 8_2_2 */ + BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 4, 45, 40000, 11, "high-reliabilty"), /* 16_1_2 */ + BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 4, 60, 40000, 1, "high-reliabilty"), /* 16_2_2 */ + BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 4, 45, 40000, 12, "high-reliabilty"), /* 24_1_2 */ + BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 4, 60, 40000, 2, "high-reliabilty"), /* 24_2_2 */ + BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 4, 45, 40000, 13, "high-reliabilty"), /* 32_1_2 */ + BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 4, 60, 40000, 3, "high-reliabilty"), /* 32_2_2 */ + BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 54, 40000, 14, "high-reliabilty"), /* 441_1_2 */ + BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 60, 40000, 4, "high-reliabilty"), /* 441_2_2 */ + BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 50, 40000, 15, "high-reliabilty"), /* 48_1_2 */ + BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 65, 40000, 5, "high-reliabilty"), /* 48_2_2 */ + BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 50, 40000, 16, "high-reliabilty"), /* 48_3_2 */ + BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 65, 40000, 6, "high-reliabilty"), /* 48_4_2 */ + BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 50, 40000, 17, "high-reliabilty"), /* 48_5_2 */ + BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 65, 40000, 7, "high-reliabilty"), /* 48_6_2 */ }; static unsigned int get_rate_mask(uint8_t rate) { @@ -476,6 +478,9 @@ else if (c.priority < conf->priority) continue; + if (s->force_target_latency && !spa_streq(s->force_target_latency, c.tag)) + continue; + if (s->retransmission >= 0) c.retransmission = s->retransmission; if (s->latency >= 0) @@ -844,6 +849,9 @@ if ((str = spa_dict_lookup(settings, "bluez5.bap.preset"))) s->qos_name = strdup(str); + if ((str = spa_dict_lookup(settings, "bluez5.bap.force-target-latency"))) + s->force_target_latency = strdup(str); + if (spa_atou32(spa_dict_lookup(settings, "bluez5.bap.rtn"), &value, 0)) s->retransmission = value; @@ -881,11 +889,11 @@ spa_debugc(&debug_ctx->ctx, "BAP LC3 settings: preset:%s rtn:%d latency:%d delay:%d framing:%d " - "locations:%x chnalloc:%x sink:%d duplex:%d", + "locations:%x chnalloc:%x sink:%d duplex:%d force-target-latency:%s",
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/bluez5-dbus.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/bluez5-dbus.c
Changed
@@ -77,6 +77,11 @@ #define TRANSPORT_ERROR_TIMEOUT (2*BLUEZ_ACTION_RATE_MSEC*SPA_NSEC_PER_MSEC) +struct bap_features { + struct spa_dict dict; + struct spa_dict_item items32; +}; + struct spa_bt_monitor { struct spa_handle handle; struct spa_device device; @@ -124,12 +129,10 @@ struct spa_list bcast_source_config_list; - uint32_t bap_sink_locations; - uint32_t bap_sink_contexts; - uint32_t bap_sink_supported_contexts; - uint32_t bap_source_locations; - uint32_t bap_source_contexts; - uint32_t bap_source_supported_contexts; + struct bap_endpoint_qos bap_sink_qos; + struct bap_endpoint_qos bap_source_qos; + + struct bap_features bap_features; struct spa_bt_quirks *quirks; @@ -145,6 +148,7 @@ struct spa_bt_remote_endpoint { struct spa_list link; struct spa_list device_link; + struct spa_list adapter_link; struct spa_bt_monitor *monitor; char *path; char *transport_path; @@ -152,6 +156,7 @@ char *uuid; unsigned int codec; struct spa_bt_device *device; + struct spa_bt_adapter *adapter; uint8_t *capabilities; size_t capabilities_len; uint8_t *metadata; @@ -161,6 +166,8 @@ struct bap_endpoint_qos qos; + struct bap_features bap_features; + bool asha_right_side; uint64_t hisyncid; }; @@ -189,6 +196,7 @@ }; #define BROADCAST_CODE_LEN 16 +#define BD_ADDR_STR_LEN 17 struct spa_bt_big { struct spa_list link; @@ -197,6 +205,7 @@ struct spa_list bis_list; int big_id; int sync_factor; + char adapterBD_ADDR_STR_LEN + 3; }; /* @@ -658,6 +667,82 @@ codec->fill_caps; } +static bool bap_features_add(struct bap_features *feat, const char *uuid, const char *name) +{ +#define TMAP_ITEM(item) { BT_TMAP_UUID, item ##_STR, BT_TMAP_UUID ":" item ##_STR }, +#define GMAP_ITEM(item) { BT_GMAP_UUID, item ##_STR, BT_GMAP_UUID ":" item ##_STR }, + static const struct { + const char *const uuid; + const char *const name; + const char *const key; + } values = { + BT_TMAP_ROLE_LIST(TMAP_ITEM) + BT_GMAP_ROLE_LIST(GMAP_ITEM) + BT_GMAP_FEATURE_LIST(GMAP_ITEM) + { NULL, NULL, NULL } + }; + SPA_STATIC_ASSERT(SPA_N_ELEMENTS(feat->items) >= SPA_N_ELEMENTS(values)); + size_t n_items = feat->dict.n_items; + size_t i; + + /* Accept only listed features */ + for (i = 0; valuesi.uuid; ++i) + if (spa_streq(valuesi.uuid, uuid) && spa_streq(valuesi.name, name)) + break; + if (!valuesi.uuid) + return false; + + if (spa_dict_lookup(&feat->dict, valuesi.key)) + return false; + + spa_assert(n_items < SPA_N_ELEMENTS(feat->items)); + + /* Add */ + feat->itemsn_items.key = valuesi.key; + feat->itemsn_items.value = valuesi.uuid; + n_items++; + + feat->dict = SPA_DICT(feat->items, n_items); + return true; +} + +/** Get feature uuid at \a i */ +static const char *bap_features_get_uuid(struct bap_features *feat, size_t i) +{ + if (!SPA_FLAG_IS_SET(feat->dict.flags, SPA_DICT_FLAG_SORTED)) + spa_dict_qsort(&feat->dict); + + if (i >= feat->dict.n_items) + return NULL; + return feat->dict.itemsi.value; +} + +/** Get feature name at \a i, or NULL if uuid doesn't match */ +static const char *bap_features_get_name(struct bap_features *feat, size_t i, const char *uuid) +{ + char *pos; + + if (i >= feat->dict.n_items) + return NULL; + if (!spa_streq(feat->dict.itemsi.value, uuid)) + return NULL; + + pos = strchr(feat->dict.itemsi.key, ':'); + if (!pos) + return NULL; + return pos + 1; +} + +static void bap_features_clear(struct bap_features *feat) +{ + spa_zero(*feat); +} + +const struct spa_dict *get_device_codec_settings(struct spa_bt_device *device, bool bap) +{ + return bap ? device->settings : &device->monitor->global_settings; +} + static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) { struct spa_bt_monitor *monitor = userdata; @@ -922,24 +1007,24 @@ spa_assert(dest && size); - if (type != DBUS_TYPE_ARRAY) + if (!check_iter_signature(&it1, "ay")) goto bad_property; dbus_message_iter_recurse(&it1, &it2); - type = dbus_message_iter_get_arg_type(&it2); - if (type != DBUS_TYPE_BYTE) - goto bad_property; - dbus_message_iter_get_fixed_array(&it2, &data, &n); - buf = malloc(n); - if (!buf) - return -ENOMEM; + if (n) { + buf = malloc(n); + if (!buf) + return -ENOMEM; + memcpy(buf, data, n); + } else { + buf = NULL; + } free(*dest); *dest = buf; *size = n; - memcpy(buf, data, n); spa_log_info(monitor->log, "%p: %s size:%zu", monitor, key, *size); spa_debug_log_mem(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', *dest, *size); @@ -1114,6 +1199,8 @@ setting_itemsi++ = SPA_DICT_ITEM_INIT("bluez5.bap.debug", "true"); setting_itemsi++ = SPA_DICT_ITEM_INIT("bluez5.bap.metadata", (void *)ep->metadata); setting_itemsi++ = SPA_DICT_ITEM_INIT("bluez5.bap.metadata-len", metadata_len); + for (j = 0; j < ep->bap_features.dict.n_items && i < SPA_N_ELEMENTS(setting_items); ++i, ++j) + setting_itemsi = ep->bap_features.dict.itemsj; if (ep->device->settings) for (j = 0; j < ep->device->settings->n_items && i < SPA_N_ELEMENTS(setting_items); ++i, ++j) setting_itemsi = ep->device->settings->itemsj; @@ -1556,6 +1643,8 @@ d->monitor = monitor; d->path = strdup(path);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/bluez5-device.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/bluez5-device.c
Changed
@@ -12,6 +12,7 @@ #include <spa/support/log.h> #include <spa/utils/type.h> +#include <spa/utils/json.h> #include <spa/utils/keys.h> #include <spa/utils/names.h> #include <spa/utils/string.h> @@ -161,6 +162,7 @@ unsigned int switching_codec:1; unsigned int switching_codec_other:1; unsigned int save_profile:1; + unsigned int autoswitch_routes:1; uint32_t prev_bt_connected_profiles; struct device_set device_set; @@ -808,17 +810,10 @@ SPA_FLAG_CLEAR(id, DYNAMIC_NODE_ID_FLAG); /* Remote device is the controller */ - if (!node->transport || impl->profile != DEVICE_PROFILE_AG - || !spa_bt_transport_volume_enabled(node->transport)) - return; - - if (id == 0 || id == 2) - volume_id = SPA_BT_VOLUME_ID_RX; - else if (id == 1) - volume_id = SPA_BT_VOLUME_ID_TX; - else + if (!node->transport || !spa_bt_transport_volume_enabled(node->transport)) return; + volume_id = get_volume_id(id); t_volume = &node->transport->volumesvolume_id; if (!t_volume->active) return; @@ -915,6 +910,55 @@ set->sourcei.impl = impl; } +static void device_set_volume_changed(void *data) +{ + struct device_set_member *member = data; + struct impl *impl = member->impl; + struct device_set *dset = &impl->device_set; + bool sink = (member->id & SINK_ID_FLAG); + int id = sink ? DEVICE_ID_SINK_SET : DEVICE_ID_SOURCE_SET; + struct node *node = &impl->nodesid; + int volume_id = get_volume_id(member->id); + struct device_set_member *members = sink ? dset->sink : dset->source; + uint32_t n_members = sink ? dset->sinks : dset->sources; + struct spa_bt_transport_volume *t_volume; + float prev_hw_volume; + unsigned int i; + + if (!node->active || !spa_bt_transport_volume_enabled(member->transport)) + return; + + t_volume = &member->transport->volumesvolume_id; + if (!t_volume->active) + return; + + spa_log_debug(impl->log, "%p device set changed hw volume %d %f", impl, volume_id, t_volume->volume); + + prev_hw_volume = node_get_hw_volume(node); + + for (uint32_t i = 0; i < node->n_channels; ++i) { + node->volumesi = prev_hw_volume > 0.0f + ? node->volumesi * t_volume->volume / prev_hw_volume + : t_volume->volume; + } + + /* CAP v1.0.1 7.3.2.2: spread hw volume to other devices in set */ + if (member->transport->bap_initiator) { + for (i = 0; i < n_members; ++i) + spa_bt_transport_set_volume(membersi.transport, volume_id, t_volume->volume); + } + + node_update_soft_volumes(node, t_volume->volume); + + node->save = true; + + emit_volume(impl, node); + + impl->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS; + impl->paramsIDX_Route.flags ^= SPA_PARAM_INFO_SERIAL; + emit_info(impl, false); +} + static void device_set_transport_destroy(void *data) { struct device_set_member *member = data; @@ -926,6 +970,7 @@ static const struct spa_bt_transport_events device_set_transport_events = { SPA_VERSION_BT_DEVICE_EVENTS, .destroy = device_set_transport_destroy, + .volume_changed = device_set_volume_changed, }; static void device_set_update_asha(struct impl *this, struct device_set *dset) @@ -1440,7 +1485,7 @@ profiles = this->bt_dev->profiles & SPA_BT_PROFILE_BAP_DUPLEX; break; case DEVICE_PROFILE_A2DP: - profiles = this->bt_dev->profiles & SPA_BT_PROFILE_A2DP_DUPLEX; + profiles = this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_DUPLEX; break; default: profiles = 0; @@ -1762,7 +1807,7 @@ media_codec = get_supported_media_codec(this, codec, NULL, device->connected_profiles); if (media_codec && media_codec->duplex_codec) have_input = true; - if (hfp_input_for_a2dp && this->nodesDEVICE_ID_SOURCE.active) + if (hfp_input_for_a2dp && (device->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT)) have_input = true; break; case DEVICE_PROFILE_BAP: @@ -2073,6 +2118,9 @@ } priority = 128; } + + if (this->autoswitch_routes && (device->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT)) + n_source++; break; } case DEVICE_PROFILE_BAP_SINK: @@ -2521,7 +2569,7 @@ if (!profile_has_route(j, route)) continue; - profile_mask = profile_direction_mask(this, j, codec, false); + profile_mask = profile_direction_mask(this, j, codec, this->autoswitch_routes); if (!(profile_mask & (1 << direction))) continue; @@ -2543,7 +2591,8 @@ struct node *node = &this->nodesdev; struct spa_bt_transport_volume *t_volume; - mask = profile_direction_mask(this, this->profile, this->props.codec, true); + mask = profile_direction_mask(this, this->profile, this->props.codec, + this->nodesDEVICE_ID_SOURCE.active); if (!(mask & (1 << direction))) return NULL; @@ -2676,11 +2725,26 @@ static struct spa_pod *build_props(struct impl *this, struct spa_pod_builder *b, uint32_t id) { struct props *p = &this->props; + struct spa_pod_frame f2; + struct spa_pod *param; + + spa_pod_builder_push_object(b, &f0, SPA_TYPE_OBJECT_Props, id); + spa_pod_builder_add(b, + SPA_PROP_bluetoothAudioCodec, SPA_POD_Id(p->codec), + SPA_PROP_bluetoothOffloadActive, SPA_POD_Bool(p->offload_active), + 0); - return spa_pod_builder_add_object(b, - SPA_TYPE_OBJECT_Props, id, - SPA_PROP_bluetoothAudioCodec, SPA_POD_Id(p->codec), - SPA_PROP_bluetoothOffloadActive, SPA_POD_Bool(p->offload_active)); + spa_pod_builder_prop(b, SPA_PROP_params, 0); + spa_pod_builder_push_struct(b, &f1); + spa_pod_builder_string(b, "bluez5.disable-dummy-call"); + spa_pod_builder_bool(b, this->bt_dev->disable_dummy_call); + spa_pod_builder_string(b, "bluez5.autoswitch-routes"); + spa_pod_builder_bool(b, this->autoswitch_routes); + spa_pod_builder_pop(b, &f1); + + param = spa_pod_builder_pop(b, &f0); + + return param; } static int impl_enum_params(void *object, int seq, @@ -2826,6 +2890,8 @@ goto soft_volume; } + spa_log_info(impl->log, "%p device set set hw volume %d %f", impl, volume_id, hw_volume); + node_update_soft_volumes(node, hw_volume); for (i = 0; i < n_members; ++i) spa_bt_transport_set_volume(membersi.transport, volume_id, hw_volume); @@ -3017,6 +3083,60 @@ } } +static int parse_prop_params(struct impl *this, struct spa_pod *params) +{ + struct spa_pod_parser prs; + struct spa_pod_frame f; + int changed = 0; + + if (params == NULL) + return 0; +
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/bt-latency.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/bt-latency.h
Changed
@@ -12,6 +12,7 @@ #include <linux/sockios.h> #include <spa/utils/defs.h> +#include <spa/utils/ratelimit.h> #include <spa/support/log.h> #include "defs.h" @@ -39,10 +40,16 @@ size_t unsent; struct { - int64_t send64; - size_t size64; + struct { + int64_t send; + uint32_t pos; + size_t size; + bool snd; + bool completion; + } pending64; uint32_t pos; int64_t prev_tx; + struct spa_ratelimit rate_limit; } impl; }; @@ -55,6 +62,9 @@ spa_zero(*lat); + lat->impl.rate_limit.interval = 5 * 60 * SPA_NSEC_PER_SEC; + lat->impl.rate_limit.burst = 8; + if (!transport->device->adapter->tx_timestamping_supported) return; @@ -81,6 +91,33 @@ spa_bt_ptp_init(&lat->ptp, lat->ptp.period, lat->ptp.period / 2); } +static inline void spa_bt_latency_clear_pending(struct spa_bt_latency *lat, unsigned int i, + bool snd, bool completion) +{ + i = i % SPA_N_ELEMENTS(lat->impl.pending); + + if (snd && lat->impl.pendingi.snd) { + if (lat->kernel_queue) + lat->kernel_queue--; + + lat->impl.pendingi.snd = false; + } + + if (completion && lat->impl.pendingi.completion) { + if (lat->unsent > lat->impl.pendingi.size) + lat->unsent -= lat->impl.pendingi.size; + else + lat->unsent = 0; + + if (lat->queue > 0) + lat->queue--; + if (!lat->queue) + lat->unsent = 0; + + lat->impl.pendingi.completion = false; + } +} + static inline ssize_t spa_bt_send(int fd, const void *buf, size_t size, struct spa_bt_latency *lat, uint64_t now) { @@ -90,12 +127,16 @@ return res; if (res >= 0) { - lat->impl.sendlat->impl.pos = now; - lat->impl.sizelat->impl.pos = size; - lat->impl.pos++; - if (lat->impl.pos >= SPA_N_ELEMENTS(lat->impl.send)) - lat->impl.pos = 0; + uint32_t i = lat->impl.pos % SPA_N_ELEMENTS(lat->impl.pending); + spa_bt_latency_clear_pending(lat, i, true, true); + lat->impl.pendingi.send = now; + lat->impl.pendingi.size = size; + lat->impl.pendingi.pos = lat->impl.pos % UINT16_MAX; + lat->impl.pendingi.snd = true; + lat->impl.pendingi.completion = true; + + lat->impl.pos++; lat->queue++; lat->kernel_queue++; lat->unsent += size; @@ -104,12 +145,14 @@ return res; } -static inline int spa_bt_latency_recv_errqueue(struct spa_bt_latency *lat, int fd, struct spa_log *log) +static inline int spa_bt_latency_recv_errqueue(struct spa_bt_latency *lat, int fd, int64_t now, + struct spa_log *log) { union { char bufCMSG_SPACE(32 * sizeof(struct scm_timestamping)); struct cmsghdr align; } control; + unsigned int i; if (!lat->enabled) return -EOPNOTSUPP; @@ -126,8 +169,9 @@ .msg_controllen = sizeof(control.buf), }; struct cmsghdr *cmsg; - struct scm_timestamping *tss = NULL; - struct sock_extended_err *serr = NULL; + bool have_tss = false, have_serr = false; + struct scm_timestamping tss; + struct sock_extended_err serr; int res; res = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); @@ -138,47 +182,49 @@ } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) - tss = (void *)CMSG_DATA(cmsg); - else if (cmsg->cmsg_level == SOL_BLUETOOTH && cmsg->cmsg_type == BT_SCM_ERROR) - serr = (void *)CMSG_DATA(cmsg); - else + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) { + memcpy(&tss, CMSG_DATA(cmsg), sizeof(tss)); + have_tss = true; + } else if (cmsg->cmsg_level == SOL_BLUETOOTH && cmsg->cmsg_type == BT_SCM_ERROR) { + memcpy(&serr, CMSG_DATA(cmsg), sizeof(serr)); + have_serr = true; + } else { continue; + } } - if (!tss || !serr || serr->ee_errno != ENOMSG || serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) - return -EINVAL; + if (!have_tss || !have_serr || serr.ee_errno != ENOMSG || serr.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) + continue; - switch (serr->ee_info) { + uint32_t tx_pos = serr.ee_data % SPA_N_ELEMENTS(lat->impl.pending); + + if (serr.ee_data % UINT16_MAX != lat->impl.pendingtx_pos.pos) { + spa_log_debug(log, "fd:%d latency%u bad value %u", fd, tx_pos, serr.ee_data); + continue; + } + + switch (serr.ee_info) { case SCM_TSTAMP_SND: - if (lat->kernel_queue) - lat->kernel_queue--; + spa_bt_latency_clear_pending(lat, tx_pos, true, false); continue; case NEW_SCM_TSTAMP_COMPLETION: + if (!lat->impl.pendingtx_pos.completion) + continue; break; default: continue; } - struct timespec *ts = &tss->ts0; + struct timespec *ts = &tss.ts0; int64_t tx_time = SPA_TIMESPEC_TO_NSEC(ts); - uint32_t tx_pos = serr->ee_data % SPA_N_ELEMENTS(lat->impl.send); - - lat->value = tx_time - lat->impl.sendtx_pos; - if (lat->unsent > lat->impl.sizetx_pos) - lat->unsent -= lat->impl.sizetx_pos; - else - lat->unsent = 0; + lat->value = tx_time - lat->impl.pendingtx_pos.send; if (lat->impl.prev_tx && tx_time > lat->impl.prev_tx) spa_bt_ptp_update(&lat->ptp, lat->value, tx_time - lat->impl.prev_tx); lat->impl.prev_tx = tx_time; - if (lat->queue > 0) - lat->queue--; - if (!lat->queue) - lat->unsent = 0; + spa_bt_latency_clear_pending(lat, tx_pos, false, true); spa_log_trace(log, "fd:%d latency%d nsec:%"PRIu64" range:%d..%d ms", fd, tx_pos, lat->value, @@ -186,6 +232,27 @@ (int)(spa_bt_ptp_valid(&lat->ptp) ? lat->ptp.max / SPA_NSEC_PER_MSEC : -1)); } while (true); + /* Clear too old pending latencies. Controllers (eg. Intel AX210, Realtek 8761CU) + * have known firmware bugs where they fail to report ISO packet completions. This + * will cause completion timestamps to be missing, so we should try to recover
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/decode-buffer.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/decode-buffer.h
Changed
@@ -84,9 +84,10 @@ int64_t duration_ns; int64_t next_nsec; - double rate_diff; int32_t delay; int32_t delay_frac; + uint32_t prev_samples; + double prev_match_rate; double level; @@ -96,6 +97,7 @@ } rx; uint8_t buffering:1; + uint8_t first_cycle:1; }; static inline int spa_bt_decode_buffer_init(struct spa_bt_decode_buffer *this, struct spa_log *log, @@ -109,8 +111,10 @@ this->buffer_size = this->frame_size * quantum_limit * 2; this->buffer_size += this->buffer_reserve; this->corr = 1.0; + this->prev_match_rate = 1.0; this->target = 0; this->buffering = true; + this->first_cycle = true; this->max_extra = INT32_MAX; this->avg_period = BUFFERING_SHORT_MSEC * SPA_NSEC_PER_MSEC; this->rate_diff_max = BUFFERING_RATE_DIFF_MAX; @@ -197,8 +201,6 @@ static inline void spa_bt_decode_buffer_write_packet(struct spa_bt_decode_buffer *this, uint32_t size, uint64_t nsec) { - const int32_t duration = this->duration_ns * this->rate / SPA_NSEC_PER_SEC; - if (nsec) { this->rx.nsec = nsec; this->rx.position = size / this->frame_size; @@ -214,10 +216,25 @@ uint32_t avail = spa_bt_decode_buffer_get_size(this) / this->frame_size; int64_t dt = this->next_nsec - this->rx.nsec; - this->level = dt * this->rate_diff * this->rate / SPA_NSEC_PER_SEC + this->level = dt * this->corr * this->rate / SPA_NSEC_PER_SEC + avail + this->delay + this->delay_frac/1e9 - this->rx.position; + + spa_log_trace_fp(this->log, + "%p level:%f avail:%u dt:%f delay:%f rx-pos:%"PRIu64" rdiff:%f", + this, this->level, avail, + dt * this->corr * this->rate / SPA_NSEC_PER_SEC, + this->delay + this->delay_frac/1e9, + this->rx.position, + this->corr); } else { - this->level = spa_bt_decode_buffer_get_size(this) / this->frame_size - duration; + uint32_t avail = spa_bt_decode_buffer_get_size(this) / this->frame_size; + + this->level = avail + this->delay + this->delay_frac/1e9; + + spa_log_trace_fp(this->log, + "%p level:%f avail:%u delay:%f", + this, this->level, avail, + this->delay + this->delay_frac/1e9); } } @@ -257,14 +274,15 @@ { int32_t target = spa_bt_decode_buffer_get_target_latency(this); - this->rx.nsec = 0; this->corr = 1.0; + spa_bt_rate_control_init(&this->ctl, target * SPA_NSEC_PER_SEC / this->rate); spa_bt_decode_buffer_write_packet(this, 0, 0); + + spa_log_debug(this->log, "%p recover level:%f", this, this->level); } -static inline void spa_bt_decode_buffer_process(struct spa_bt_decode_buffer *this, uint32_t samples, int64_t duration_ns, - double rate_diff, int64_t next_nsec, int32_t delay, int32_t delay_frac) +static inline void spa_bt_decode_buffer_process(struct spa_bt_decode_buffer *this, uint32_t samples, int64_t duration_ns) { const uint32_t data_size = samples * this->frame_size; const int32_t packet_size = SPA_CLAMP(this->packet_size.max, 0, INT32_MAX/8); @@ -272,15 +290,7 @@ uint32_t avail; double level; - this->rate_diff = rate_diff; - this->next_nsec = next_nsec; - this->delay = delay; - this->delay_frac = delay_frac; - - /* The fractional delay is given at the start of current cycle. Make it relative - * to next_nsec used for the level calculations. - */ - this->delay_frac += (int32_t)(1e9 * samples - duration_ns * this->rate * this->rate_diff); + this->prev_samples = samples; if (SPA_UNLIKELY(duration_ns != this->duration_ns)) { this->duration_ns = duration_ns; @@ -330,6 +340,11 @@ this->pos += samples; + this->corr = spa_bt_rate_control_update(&this->ctl, + level * SPA_NSEC_PER_SEC / this->rate, + ((double)target + 0.5/this->rate) * SPA_NSEC_PER_SEC / this->rate, + duration_ns, this->avg_period, this->rate_diff_max); + enum spa_log_level log_level = (this->pos > this->rate) ? SPA_LOG_LEVEL_DEBUG : SPA_LOG_LEVEL_TRACE; if (SPA_UNLIKELY(spa_log_level_topic_enabled(this->log, SPA_LOG_TOPIC_DEFAULT, log_level))) { spa_log_lev(this->log, log_level, @@ -344,11 +359,6 @@ this->pos = 0; } - this->corr = spa_bt_rate_control_update(&this->ctl, - level * SPA_NSEC_PER_SEC / this->rate, - ((double)target + 0.5/this->rate) * SPA_NSEC_PER_SEC / this->rate, - duration_ns, this->avg_period, this->rate_diff_max); - spa_bt_decode_buffer_get_read(this, &avail); if (avail < data_size) { spa_log_debug(this->log, "%p underrun samples:%d", this, @@ -358,6 +368,33 @@ } } +static inline void spa_bt_decode_buffer_set_next(struct spa_bt_decode_buffer *this, int64_t next_nsec, + int32_t delay, int32_t delay_frac, double match_rate, bool delay_at_start) +{ + /* Called after spa_bt_decode_buffer_process() on the same cycle to update + * next_nsec values. + */ + this->next_nsec = next_nsec; + this->delay = delay; + this->delay_frac = delay_frac; + + /* If fractional delay is given at the start of current cycle, make it relative to + * next_nsec used for the level calculations. + */ + if (delay_at_start) { + /* Adjust for no resampler prefill */ + int32_t off = this->first_cycle ? -delay : 0; + + this->delay_frac += (int32_t)(1e9 * (this->prev_samples + off) - this->duration_ns * this->rate / this->prev_match_rate); + } + + this->prev_match_rate = match_rate > 0 ? match_rate : 1.0; + this->first_cycle = false; + + /* Recalculate this->level */ + spa_bt_decode_buffer_write_packet(this, 0, 0); +} + struct spa_bt_recvmsg_data { struct spa_log *log; struct spa_system *data_system;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/defs.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/defs.h
Changed
@@ -135,7 +135,8 @@ #define PROFILE_HFP_AG "/Profile/HFPAG" #define PROFILE_HFP_HF "/Profile/HFPHF" -#define HSP_HS_DEFAULT_CHANNEL 3 +#define HSP_HS_DEFAULT_CHANNEL 3 +#define HFP_SCO_DEFAULT_DATAPATH 0 #define SOURCE_ID_BLUETOOTH 0x1 /* Bluetooth SIG */ #define SOURCE_ID_USB 0x2 /* USB Implementer's Forum */ @@ -378,6 +379,7 @@ unsigned int has_media1_interface:1; unsigned int le_audio_bcast_supported:1; unsigned int tx_timestamping_supported:1; + struct spa_list remote_endpoint_list; }; enum spa_bt_form_factor { @@ -573,6 +575,8 @@ const struct media_codec *preferred_codec; uint32_t preferred_profiles; + + bool disable_dummy_call; }; struct spa_bt_device *spa_bt_device_find(struct spa_bt_monitor *monitor, const char *path);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/iso-io.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/iso-io.c
Changed
@@ -21,12 +21,12 @@ #include "media-codecs.h" #include "defs.h" -#include "decode-buffer.h" SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.iso"); #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic +#include "decode-buffer.h" #include "bt-latency.h" #define IDLE_TIME (500 * SPA_NSEC_PER_MSEC) @@ -41,6 +41,9 @@ #define ISO_BUFFERING_AVG_PERIOD (50 * SPA_NSEC_PER_MSEC) #define ISO_BUFFERING_RATE_DIFF_MAX 0.05 +#define FLUSH_WAIT 3 +#define MIN_FILL 1 + struct clock_sync { /** Reference monotonic time for streams in the group */ int64_t base_time; @@ -67,9 +70,12 @@ int64_t next; int64_t duration_tx; int64_t duration_rx; - bool flush; + uint32_t flush; bool started; + struct spa_bt_ptp kernel_imbalance; + struct spa_bt_ptp stream_imbalance; + struct clock_sync rx_sync; }; @@ -80,6 +86,7 @@ int fd; bool sink; bool idle; + bool ready; spa_bt_iso_io_pull_t pull; @@ -133,21 +140,18 @@ spa_assert_se(res == 0); } -static int stream_silence(struct stream *stream) +static int stream_silence_buf(struct stream *stream, uint8_t *buf, size_t max_size) { static uint8_t emptyEMPTY_BUF_SIZE = {0}; - const size_t max_size = sizeof(stream->this.buf); int res, used, need_flush; size_t encoded; - stream->idle = true; - - res = used = stream->codec->start_encode(stream->this.codec_data, stream->this.buf, max_size, 0, 0); + res = used = stream->codec->start_encode(stream->this.codec_data, buf, max_size, 0, 0); if (res < 0) return res; res = stream->codec->encode(stream->this.codec_data, empty, stream->block_size, - SPA_PTROFF(stream->this.buf, used, void), max_size - used, &encoded, &need_flush); + SPA_PTROFF(buf, used, void), max_size - used, &encoded, &need_flush); if (res < 0) return res; @@ -156,7 +160,21 @@ if (!need_flush) return -EINVAL; - stream->this.size = used; + return used; +} + +static int stream_silence(struct stream *stream) +{ + const size_t max_size = sizeof(stream->this.buf); + int res; + + stream->idle = true; + + res = stream_silence_buf(stream, stream->this.buf, max_size); + if (res < 0) + return res; + + stream->this.size = res; return 0; } @@ -199,55 +217,114 @@ } while (res >= 0); } +static void reset_imbalance(struct group *group) +{ + spa_bt_ptp_init(&group->kernel_imbalance, 2*LATENCY_PERIOD, LATENCY_PERIOD); + spa_bt_ptp_init(&group->stream_imbalance, 2*LATENCY_PERIOD, LATENCY_PERIOD); +} + static bool group_latency_check(struct group *group) { struct stream *stream; - int32_t min_latency = INT32_MAX, max_latency = INT32_MIN; - unsigned int kernel_queue = UINT_MAX; - - spa_list_for_each(stream, &group->streams, link) { - if (!stream->sink) - continue; - if (!stream->tx_latency.enabled) - return false; - - if (kernel_queue == UINT_MAX) - kernel_queue = stream->tx_latency.kernel_queue; + int32_t min_min = INT32_MAX, max_min = INT32_MIN; + int32_t min_kernel = INT32_MAX, max_kernel = INT32_MIN; + + /* + * Packet transport eg. over USB and in kernel (where there is no delay guarantee) + * can introduce delays in controller receiving the packets, and this may desync + * stream playback. From measurements, in steady state kernel+USB introduce +- 3 ms + * jitter. + * + * Since there's currently no way to sync to controller HW clock (as of kernel + * 6.18) and we cannot provide packet timestamps, controllers appear to fall back + * to guessing, and seem to sometimes get stuck in a state where streams are + * desynchronized. + * + * It appears many controllers also have bad implementations of the LE Read ISO TX + * Sync command and always return 0 timestamp, so it is not even possible to + * provide valid packet timestamps on such broken hardware. + * + * Kernel (as of 6.18) does not do any stream synchronization, and its packet + * scheduler can also introduce desync on socket buffer level if controller + * buffers are full. + * + * Consequently, there's currently no fully reliable way to sync even two + * channels. We have to try work around this mess by attempting to detect desyncs, + * and resynchronize if: + * + * - if socket queues are out of balance (kernel packet scheduler out of sync) + * - if controller is reporting packet completion times that seem off between + * different streams, controller is likely out of sync. No way to know, really, + * but let's flush then and hope for the best. + * + * In addition, we have to keep minimal fill level in the controller to avoid it + * running out of packets, as that triggers desyncs on Intel controllers. + */ + + /* Check for ongoing flush */ + if (group->flush) { + spa_list_for_each(stream, &group->streams, link) { + if (!stream->sink) + continue; - if (group->flush && stream->tx_latency.queue) { - spa_log_debug(group->log, "%p: ISO group:%d latency skip: flushing", - group, group->id); - return true; + if (stream->tx_latency.queue) { + spa_log_trace(group->log, "%p: ISO group:%d resync pause: flushing", + group, group->id); + return true; + } } - if (stream->tx_latency.kernel_queue != kernel_queue) { - /* Streams out of sync, try to correct if it persists */ - spa_log_debug(group->log, "%p: ISO group:%d latency skip: imbalance", + + if (--group->flush) { + spa_log_trace(group->log, "%p: ISO group:%d resync pause: flushing wait", group, group->id); - group->flush = true; return true; } } - group->flush = false; - + /* Evaluate TX imbalances */ spa_list_for_each(stream, &group->streams, link) { - if (!stream->sink) + if (!stream->sink || stream->idle) continue; - if (!stream->tx_latency.valid) + if (!stream->tx_latency.enabled || !stream->tx_latency.valid) return false; - min_latency = SPA_MIN(min_latency, stream->tx_latency.ptp.min); - max_latency = SPA_MAX(max_latency, stream->tx_latency.ptp.max); + min_kernel = SPA_MIN(stream->tx_latency.kernel_queue * group->duration_tx, min_kernel); + max_kernel = SPA_MAX(stream->tx_latency.kernel_queue * group->duration_tx, max_kernel); + + min_min = SPA_MIN(min_min, stream->tx_latency.ptp.min); + max_min = SPA_MAX(max_min, stream->tx_latency.ptp.min); }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/iso-io.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/iso-io.h
Changed
@@ -35,6 +35,8 @@ struct spa_audio_info format; /**< Audio format */ void *codec_data; /**< Codec data */ + bool debug_mono; /**< Duplicate packets from first sink to other sinks */ + void *user_data; }; @@ -47,6 +49,8 @@ void spa_bt_iso_io_set_cb(struct spa_bt_iso_io *io, spa_bt_iso_io_pull_t pull, void *user_data); int spa_bt_iso_io_recv_errqueue(struct spa_bt_iso_io *io); +void spa_bt_iso_io_ready(struct spa_bt_iso_io *io); + void spa_bt_iso_io_set_source_buffer(struct spa_bt_iso_io *io, struct spa_bt_decode_buffer *buffer); int32_t spa_bt_iso_io_get_source_target_latency(struct spa_bt_iso_io *io); void spa_bt_iso_io_check_rx_sync(struct spa_bt_iso_io *io, uint64_t position);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/media-sink.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/media-sink.c
Changed
@@ -159,6 +159,7 @@ unsigned int is_duplex:1; unsigned int is_internal:1; + unsigned int iso_debug_mono:1; struct spa_source source; int timerfd; @@ -1315,7 +1316,10 @@ if (spa_bt_iso_io_recv_errqueue(this->transport->iso_io) == 0) return; } else { - if (spa_bt_latency_recv_errqueue(&this->tx_latency, this->flush_source.fd, this->log) == 0) + struct timespec ts; + + spa_system_clock_gettime(this->data_system, CLOCK_REALTIME, &ts); + if (spa_bt_latency_recv_errqueue(&this->tx_latency, this->flush_source.fd, SPA_TIMESPEC_TO_NSEC(&ts), this->log) == 0) return; } @@ -1567,6 +1571,7 @@ this->own_codec_data = false; this->codec_data = this->transport->iso_io->codec_data; this->codec_props_changed = true; + this->transport->iso_io->debug_mono = this->iso_debug_mono; } this->encoder_delay = 0; @@ -2622,6 +2627,9 @@ if (info && (str = spa_dict_lookup(info, "api.bluez5.internal")) != NULL) this->is_internal = spa_atob(str); + if (info && (str = spa_dict_lookup(info, "bluez5.debug.iso-mono")) != NULL) + this->iso_debug_mono = spa_atob(str); + if (info && (str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT))) sscanf(str, "pointer:%p", &this->transport);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/media-source.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/media-source.c
Changed
@@ -173,11 +173,14 @@ int seqnum; uint32_t plc_packets; + bool initial_buffering; uint32_t errqueue_count; + bool bap_latency_warned; + struct delay_info delay; - int64_t delay_sink; + struct spa_latency_info delay_sink; struct spa_source *update_delay_event; struct spa_bt_recvmsg_data recv; @@ -338,31 +341,109 @@ static void emit_node_info(struct impl *this, bool full); +static int get_bap_server_latency(struct impl *this, unsigned int *node_latency) +{ + const uint32_t min_quantum = 128; + struct port *port = &this->port; + uint32_t samples, latency, sink_latency, quantum, packet_latency, min_samples; + uint32_t graph_rate, duration; + float latency_quanta; + float rate_factor; + int64_t packet_ns; + + if (!port->have_format || this->codec->kind != MEDIA_CODEC_BAP || + this->is_input || !this->transport || + this->transport->delay_us == SPA_BT_UNKNOWN_DELAY || + !this->codec->get_interval || !this->codec_data) + return -EINVAL; + + /* Presentation delay for BAP server + * + * This assumes the time when kernel timestamped the packet is (on average) + * the SDU synchronization reference (see Core v5.3 Vol 6/G Sec 3.2.2 Fig. 3.2, + * BAP v1.0 Sec 7.1.1). + * + * XXX: + * + * This is not exactly true, there at least controller->host transport latency in + * between (eg USB maybe ~ ms), but currently kernel does not provide us any + * better information. With current Core v6.2 specification it appers not + * possible to do clock-synchronized playback properly as generic Host using ISO + * over HCI, as we can only clock sync to HCI packet arrival time. Eg. GMAP + * requires +- 62.5 us latency accuracy, and I doubt we can get that it that good. + */ + samples = (uint64_t)this->transport->delay_us * + port->current_format.info.raw.rate / SPA_USEC_PER_SEC; + + graph_rate = this->position ? this->position->clock.rate.denom : 48000; + duration = this->position ? this->position->clock.duration : 1024; + rate_factor = (float)port->current_format.info.raw.rate / graph_rate; + + /* Allow 1/2 packet jitter */ + packet_ns = this->codec->get_interval(this->codec_data) / 2; + packet_latency = packet_ns * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC; + + sink_latency = this->delay_sink.min_ns * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC + + this->delay_sink.min_rate; + + latency = sink_latency + packet_latency; + latency_quanta = this->delay_sink.min_quantum; + + if (!node_latency) { + quantum = duration; + } else { + quantum = this->quantum_limit; + while (quantum > min_quantum && samples < latency + (int)(quantum * rate_factor * (latency_quanta + 1))) + quantum /= 2; + } + + quantum = SPA_MAX(quantum, min_quantum); + min_samples = latency + (int)(quantum * rate_factor * (latency_quanta + 1)); + + if (samples < min_samples) { + if (!this->bap_latency_warned && node_latency) { + this->bap_latency_warned = true; + spa_log_warn(this->log, "%s: too small requested BAP presentation delay %u us < %u + %u + %f*quant us", + this->transport->path, (unsigned int)this->transport->delay_us, + (unsigned int)(this->delay_sink.min_ns / 1000), + (unsigned int)(packet_ns / 1000), + (float)(latency_quanta + 1)); + } + + samples = min_samples; + } + + if (node_latency) { + spa_log_debug(this->log, "%p: adjust BAP presentation delay:%u sink:%u latency:%u quanta:%f quantum:%u/%u", + this, samples, sink_latency, latency, latency_quanta, quantum, graph_rate); + *node_latency = quantum; + } + return samples - sink_latency - (int)(quantum * latency_quanta); +} + static void set_latency(struct impl *this, bool emit_latency) { - if (this->codec->kind == MEDIA_CODEC_BAP && !this->is_input && this->transport && - this->transport->delay_us != SPA_BT_UNKNOWN_DELAY) { + unsigned int node_latency; + int bap_buffer; + + bap_buffer = get_bap_server_latency(this, &node_latency); + if (bap_buffer >= 0) { struct port *port = &this->port; - unsigned int node_latency = 2048; uint64_t rate = port->current_format.info.raw.rate; - unsigned int target = this->transport->delay_us*rate/SPA_USEC_PER_SEC * 1/2; - /* Adjust requested node latency to be somewhat (~1/2) smaller - * than presentation delay. The difference functions as room - * for buffering rate control. - */ - while (node_latency > 64 && node_latency > target) - node_latency /= 2; + spa_log_info(this->log, "BAP presentation delay %d us, node latency %u/%u buffer %d samples", + (int)this->transport->delay_us, node_latency, + (unsigned int)rate, bap_buffer); + /* Adjust requested node latency so that BAP presentation delay can be + * satisfied. + */ if (this->node_latency != node_latency) { this->node_latency = node_latency; + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS; if (emit_latency) emit_node_info(this, false); } - - spa_log_info(this->log, "BAP presentation delay %d us, node latency %u/%u", - (int)this->transport->delay_us, node_latency, - (unsigned int)rate); } } @@ -475,6 +556,8 @@ if (!this->codec->produce_plc) return -ENOTSUP; + if (this->initial_buffering) + return -EINVAL; buf = spa_bt_decode_buffer_get_write(&port->buffer, &avail); res = this->codec->produce_plc(this->codec_data, buf, avail); @@ -493,6 +576,7 @@ static int32_t decode_data(struct impl *this, uint8_t *src, uint32_t src_size, uint8_t *dst, uint32_t dst_size, uint32_t *dst_out, int pkt_seqnum) { + struct port *port = &this->port; ssize_t processed; size_t written, avail; size_t src_avail = src_size; @@ -526,14 +610,19 @@ lost = 0; } - if (lost >= this->plc_packets) { + if (lost >= this->plc_packets) lost -= this->plc_packets; - } else { - /* We already produced PLC audio for this packet. However, this - * only occurs if we are underflowing, so we should retain this - * packet regardless and let rate matching take care of it. + else + this->plc_packets -= lost; + + if (this->plc_packets && + port->buffer.level > spa_bt_decode_buffer_get_target_latency(&port->buffer)) { + /* We already produced PLC audio for this packet, and have enough + * data, so drop the packet. */ - lost = 0; + spa_log_debug(this->log, "%p: PLC drop %u sn:%u", this, this->plc_packets, seqnum); + this->plc_packets--; + return 0; } /* Pad with PLC audio for any missing packets */ @@ -792,6 +881,22 @@ spa_log_trace(this->log, "%p: io:%d->%d status:%d", this, io_status, port->io->status, status); } + /* Use any updated rate correction already for the next cycle */ + this->next_time = (uint64_t)(now_time + duration * SPA_NSEC_PER_SEC / port->buffer.corr / rate); + if (SPA_LIKELY(this->clock)) { + this->clock->rate_diff = port->buffer.corr; + this->clock->next_nsec = this->next_time; + } + + /* Set next position also here in case impl_node_process() fails to be scheduled */ + if (this->transport_started) + spa_bt_decode_buffer_set_next(&port->buffer, + this->position ? this->position->clock.next_nsec : 0, + this->resampling ? this->port.rate_match->delay : 0, + this->resampling ? this->port.rate_match->delay_frac : 0,
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/modemmanager.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/modemmanager.c
Changed
@@ -36,6 +36,8 @@ struct modem modem; struct spa_list call_list; + + bool pts; }; struct dbus_cmd_data { @@ -944,8 +946,13 @@ spa_autofree struct dbus_cmd_data *data = NULL; spa_autoptr(DBusMessage) m = NULL; DBusMessageIter iter, dict; + size_t i = 0; + + /* Allow memory dial for PTS tests HFP/AG/OCM/BV-01-C and HFP/AG/OCM/BV-02-C */ + if (this->pts && number0 == '>') + i++; - for (size_t i = 0; numberi; i++) { + for (; numberi; i++) { if (!is_valid_dial_string_char(numberi)) { spa_log_warn(this->log, "Call creation canceled, invalid character found in dial string: %c", numberi); if (error) @@ -1078,6 +1085,8 @@ { const char *modem_device_str = NULL; bool modem_device_found = false; + const char *pts_str = NULL; + bool pts = false; spa_assert(log); spa_assert(dbus_connection); @@ -1087,6 +1096,9 @@ if (!spa_streq(modem_device_str, "none")) modem_device_found = true; } + if ((pts_str = spa_dict_lookup(info, "bluez5.hfphsp-backend-native-pts")) != NULL) { + pts = spa_atob(pts_str); + } } if (!modem_device_found) { spa_log_info(log, "No modem allowed, doesn't link to ModemManager"); @@ -1104,6 +1116,7 @@ if (modem_device_str && !spa_streq(modem_device_str, "any")) this->allowed_modem_device = strdup(modem_device_str); spa_list_init(&this->call_list); + this->pts = pts; if (add_filters(this) < 0) return NULL;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/bluez5/rate-control.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/bluez5/rate-control.h
Changed
@@ -94,11 +94,14 @@ * The deviation from the buffer level target evolves as * * delta(j) = level(j) - target - * delta(j+1) = delta(j) + r(j) - c(j+1) + * delta(j+1) = delta(j) + r(j) - c(j) * * where r is samples received in one duration, and c corrected rate * (samples per duration). * + * Note that the rate correction calculated on *previous* cycle is what affects the + * current one. + * * The rate correction is in general determined by linear filter f * * c(j+1) = c(j) + \sum_{k=0}^\infty delta(j - k) f(k) @@ -111,7 +114,7 @@ * * delta(z) = G(z) r(z) * c(z) = F(z) delta(z) - * G(z) = (z - 1) / (z - 1)^2 + z f(z) + * G(z) = (z - 1) / (z - 1)^2 + f(z) * F(z) = f(z) / (z - 1) * * We now want: poles of G(z) must be in |z|<1 for stability, F(z) @@ -119,23 +122,22 @@ * * To satisfy the conditions, take * - * (z - 1)^2 + z f(z) = p(z) / q(z) + * (z - 1)^2 + f(z) = p(z) / q(z) * - * where p(z) is polynomial with leading term z^n with wanted root - * structure, and q(z) is any polynomial with leading term z^{n-2}. - * This guarantees f(z) is causal, and G(z) = (z-1) q(z) / p(z). + * where p(z) / q(z) are polynomials such that p(z)/q(z) ~ z^2 - 2 z + O(1) + * in 1/z expansion. This guarantees f(z) is causal, and G(z) = (z-1) q(z) / p(z). * We can choose p(z) and q(z) to improve low-pass properties of F(z). * - * Simplest choice is p(z)=(z-x)^2 and q(z)=1, but that gives flat + * Simplest choice is p(z)=(z-1)^2 and q(z)=1, but that does not supress * high frequency response in F(z). Better choice is p(z) = (z-u)*(z-v)*(z-w) - * and q(z) = z - r. To make F(z) better lowpass, one can cancel - * a resulting 1/z pole in F(z) by setting r=u*v*w. Then, + * and q(z) = z - r. Causality requires r = u + v + w - 2. + * Then, * * G(z) = (z - u*v*w)*(z - 1) / (z - u)*(z - v)*(z - w) * F(z) = (a z + b - a) / (z - 1) * H(z) * H(z) = beta / (z - 1 + beta) - * beta = 1 - u*v*w - * a = (1-u) + (1-v) + (1-w) - beta / beta + * beta = 3 - u - v - w + * a = u*v + u*w + v*w - u - v - w + beta / beta * b = (1-u)*(1-v)*(1-w) / beta * * which corresponds to iteration for c(j): @@ -147,8 +149,10 @@ * which gives the low-pass property for c(j). * * The simplest filter is obtained by putting the poles at - * u=v=w=(1-beta)**(1/3). Since beta << 1, computing the root - * can be avoided by expanding in series. + * u=v=w=(1 - beta/3). Then a=beta/3 and b=beta^2/27 + * + * The same filter is obtained if one uses c(j+1) instead of c(j) + * in the starting point and takes limit beta -> 0. * * Overshoot in impulse response could be reduced by moving one of the * poles closer to z=1, but this increases the step response time. @@ -169,12 +173,8 @@ double target, double duration, double period, double rate_diff_max) { /* - * u = (1 - beta)^(1/3) * x = a / beta * y = b / beta - * a = (2 + u) * (1 - u)^2 / beta - * b = (1 - u)^3 / beta - * beta -> 0 */ const double beta = SPA_CLAMP(duration / period, 0, 0.5); const double x = 1.0/3;
View file
_service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/filter-graph/audio-dsp-avx2.c
Added
@@ -0,0 +1,327 @@ +/* Spa */ +/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#include "config.h" + +#include <string.h> +#include <stdio.h> +#include <math.h> + +#include <spa/utils/defs.h> + +#ifndef HAVE_FFTW +#include "pffft.h" +#endif +#include "audio-dsp-impl.h" + +#include <immintrin.h> + +static void dsp_add_avx2(void *obj, float *dst, const float * SPA_RESTRICT src, + uint32_t n_src, uint32_t n_samples) +{ + uint32_t n, i, unrolled; + __m256 in4; + const float **s = (const float **)src; + float *d = dst; + + if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 32))) { + unrolled = n_samples & ~31; + for (i = 0; i < n_src; i++) { + if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 32))) { + unrolled = 0; + break; + } + } + } else + unrolled = 0; + + for (n = 0; n < unrolled; n += 32) { + in0 = _mm256_load_ps(&s0n+ 0); + in1 = _mm256_load_ps(&s0n+ 8); + in2 = _mm256_load_ps(&s0n+16); + in3 = _mm256_load_ps(&s0n+24); + + for (i = 1; i < n_src; i++) { + in0 = _mm256_add_ps(in0, _mm256_load_ps(&sin+ 0)); + in1 = _mm256_add_ps(in1, _mm256_load_ps(&sin+ 8)); + in2 = _mm256_add_ps(in2, _mm256_load_ps(&sin+16)); + in3 = _mm256_add_ps(in3, _mm256_load_ps(&sin+24)); + } + _mm256_store_ps(&dn+ 0, in0); + _mm256_store_ps(&dn+ 8, in1); + _mm256_store_ps(&dn+16, in2); + _mm256_store_ps(&dn+24, in3); + } + for (; n < n_samples; n++) { + __m128 in1; + in0 = _mm_load_ss(&s0n); + for (i = 1; i < n_src; i++) + in0 = _mm_add_ss(in0, _mm_load_ss(&sin)); + _mm_store_ss(&dn, in0); + } +} + +static void dsp_add_1_gain_avx2(void *obj, float *dst, const float * SPA_RESTRICT src, + uint32_t n_src, float gain, uint32_t n_samples) +{ + uint32_t n, i, unrolled; + __m256 in4, g; + const float **s = (const float **)src; + float *d = dst; + __m128 g1; + + if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 32))) { + unrolled = n_samples & ~31; + for (i = 0; i < n_src; i++) { + if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 32))) { + unrolled = 0; + break; + } + } + } else + unrolled = 0; + + g = _mm256_set1_ps(gain); + g1 = _mm_set_ss(gain); + + for (n = 0; n < unrolled; n += 32) { + in0 = _mm256_load_ps(&s0n+ 0); + in1 = _mm256_load_ps(&s0n+ 8); + in2 = _mm256_load_ps(&s0n+16); + in3 = _mm256_load_ps(&s0n+24); + + for (i = 1; i < n_src; i++) { + in0 = _mm256_add_ps(in0, _mm256_load_ps(&sin+ 0)); + in1 = _mm256_add_ps(in1, _mm256_load_ps(&sin+ 8)); + in2 = _mm256_add_ps(in2, _mm256_load_ps(&sin+16)); + in3 = _mm256_add_ps(in3, _mm256_load_ps(&sin+24)); + } + _mm256_store_ps(&dn+ 0, _mm256_mul_ps(g, in0)); + _mm256_store_ps(&dn+ 8, _mm256_mul_ps(g, in1)); + _mm256_store_ps(&dn+16, _mm256_mul_ps(g, in2)); + _mm256_store_ps(&dn+24, _mm256_mul_ps(g, in3)); + } + for (; n < n_samples; n++) { + __m128 in1; + in0 = _mm_load_ss(&s0n); + for (i = 1; i < n_src; i++) + in0 = _mm_add_ss(in0, _mm_load_ss(&sin)); + _mm_store_ss(&dn, _mm_mul_ss(g1, in0)); + } +} + +static void dsp_add_n_gain_avx2(void *obj, float *dst, + const float * SPA_RESTRICT src, uint32_t n_src, + float gain, uint32_t n_gain, uint32_t n_samples) +{ + uint32_t n, i, unrolled; + __m256 in4, g; + const float **s = (const float **)src; + float *d = dst; + + if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 32))) { + unrolled = n_samples & ~31; + for (i = 0; i < n_src; i++) { + if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 32))) { + unrolled = 0; + break; + } + } + } else + unrolled = 0; + + for (n = 0; n < unrolled; n += 32) { + g = _mm256_set1_ps(gain0); + in0 = _mm256_mul_ps(g, _mm256_load_ps(&s0n+ 0)); + in1 = _mm256_mul_ps(g, _mm256_load_ps(&s0n+ 8)); + in2 = _mm256_mul_ps(g, _mm256_load_ps(&s0n+16)); + in3 = _mm256_mul_ps(g, _mm256_load_ps(&s0n+24)); + + for (i = 1; i < n_src; i++) { + g = _mm256_set1_ps(gaini); + in0 = _mm256_add_ps(in0, _mm256_mul_ps(g, _mm256_load_ps(&sin+ 0))); + in1 = _mm256_add_ps(in1, _mm256_mul_ps(g, _mm256_load_ps(&sin+ 8))); + in2 = _mm256_add_ps(in2, _mm256_mul_ps(g, _mm256_load_ps(&sin+16))); + in3 = _mm256_add_ps(in3, _mm256_mul_ps(g, _mm256_load_ps(&sin+24))); + } + _mm256_store_ps(&dn+ 0, in0); + _mm256_store_ps(&dn+ 8, in1); + _mm256_store_ps(&dn+16, in2); + _mm256_store_ps(&dn+24, in3); + } + for (; n < n_samples; n++) { + __m128 in1, g; + g = _mm_set_ss(gain0); + in0 = _mm_mul_ss(g, _mm_load_ss(&s0n)); + for (i = 1; i < n_src; i++) { + g = _mm_set_ss(gaini); + in0 = _mm_add_ss(in0, _mm_mul_ss(g, _mm_load_ss(&sin))); + } + _mm_store_ss(&dn, in0); + } +} + + +void dsp_mix_gain_avx2(void *obj, + float * SPA_RESTRICT dst, + const float * SPA_RESTRICT src, uint32_t n_src, + float gain, uint32_t n_gain, uint32_t n_samples) +{ + if (n_src == 0) { + memset(dst, 0, n_samples * sizeof(float)); + } else if (n_src == 1 && gain0 == 1.0f) { + if (dst != src0) + spa_memcpy(dst, src0, n_samples * sizeof(float)); + } else { + if (n_gain == 0) + dsp_add_avx2(obj, dst, src, n_src, n_samples); + else if (n_gain < n_src) + dsp_add_1_gain_avx2(obj, dst, src, n_src, gain0, n_samples); + else + dsp_add_n_gain_avx2(obj, dst, src, n_src, gain, n_gain, n_samples); + } +} + +void dsp_sum_avx2(void *obj, float *r, const float *a, const float *b, uint32_t n_samples) +{ + uint32_t n, unrolled; + __m256 in4; + + unrolled = n_samples & ~31; + + if (SPA_LIKELY(SPA_IS_ALIGNED(r, 32)) && + SPA_LIKELY(SPA_IS_ALIGNED(a, 32)) && + SPA_LIKELY(SPA_IS_ALIGNED(b, 32))) { + for (n = 0; n < unrolled; n += 32) { + in0 = _mm256_load_ps(&an+ 0); + in1 = _mm256_load_ps(&an+ 8); + in2 = _mm256_load_ps(&an+16);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/filter-graph/audio-dsp-c.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/filter-graph/audio-dsp-c.c
Changed
@@ -205,9 +205,10 @@ void dsp_delay_c(void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, - uint32_t delay, float *dst, const float *src, uint32_t n_samples) + uint32_t delay, float *dst, const float *src, uint32_t n_samples, + float fb, float ff) { - if (delay == 0) { + if (delay == 0 && fb == 0.0f && ff == 0.0f) { dsp_copy_c(obj, dst, src, n_samples); } else { uint32_t w, o, i; @@ -215,10 +216,20 @@ w = *pos; o = n_buffer - delay; - for (i = 0; i < n_samples; i++) { - bufferw = bufferw + n_buffer = srci; - dsti = bufferw + o; - w = w + 1 >= n_buffer ? 0 : w + 1; + if (fb == 0.0f && ff == 0.0f) { + for (i = 0; i < n_samples; i++) { + bufferw = bufferw + n_buffer = srci; + dsti = bufferw + o; + w = w + 1 >= n_buffer ? 0 : w + 1; + } + } else { + for (i = 0; i < n_samples; i++) { + float d = bufferw + o; + float s = srci; + bufferw = bufferw + n_buffer = s + d * fb; + dsti = ff * s + d; + w = w + 1 >= n_buffer ? 0 : w + 1; + } } *pos = w; }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/filter-graph/audio-dsp-impl.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/filter-graph/audio-dsp-impl.h
Changed
@@ -32,7 +32,7 @@ float * SPA_RESTRICT out, const float * SPA_RESTRICT in, uint32_t n_src, uint32_t n_samples) #define MAKE_DELAY_FUNC(arch) \ void dsp_delay_##arch (void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, \ - uint32_t delay, float *dst, const float *src, uint32_t n_samples) + uint32_t delay, float *dst, const float *src, uint32_t n_samples, float fb, float ff) #define MAKE_FFT_NEW_FUNC(arch) \ void *dsp_fft_new_##arch(void *obj, uint32_t size, bool real) @@ -84,11 +84,11 @@ MAKE_FFT_CMUL_FUNC(sse); MAKE_FFT_CMULADD_FUNC(sse); #endif -#if defined (HAVE_AVX) -MAKE_MIX_GAIN_FUNC(avx); -MAKE_SUM_FUNC(avx); -MAKE_FFT_CMUL_FUNC(avx); -MAKE_FFT_CMULADD_FUNC(avx); +#if defined (HAVE_AVX2) +MAKE_MIX_GAIN_FUNC(avx2); +MAKE_SUM_FUNC(avx2); +MAKE_FFT_CMUL_FUNC(avx2); +MAKE_FFT_CMULADD_FUNC(avx2); #endif #endif /* DSP_OPS_IMPL_H */
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/filter-graph/audio-dsp-sse.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/filter-graph/audio-dsp-sse.c
Changed
@@ -614,34 +614,70 @@ } void dsp_delay_sse(void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, uint32_t delay, - float *dst, const float *src, uint32_t n_samples) + float *dst, const float *src, uint32_t n_samples, float fb, float ff) { - __m128 t1; + __m128 t4; uint32_t w = *pos; uint32_t o = n_buffer - delay; uint32_t n, unrolled; - if (SPA_IS_ALIGNED(src, 16) && - SPA_IS_ALIGNED(dst, 16)) - unrolled = n_samples & ~3; - else - unrolled = 0; + if (fb == 0.0f && ff == 0.0f) { + if (SPA_IS_ALIGNED(src, 16) && + SPA_IS_ALIGNED(dst, 16) && delay >= 4) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + t0 = _mm_load_ps(&srcn); + _mm_storeu_ps(&bufferw, t0); + _mm_storeu_ps(&bufferw+n_buffer, t0); + t0 = _mm_loadu_ps(&bufferw+o); + _mm_store_ps(&dstn, t0); + w = w + 4 >= n_buffer ? 0 : w + 4; + } + for(; n < n_samples; n++) { + t0 = _mm_load_ss(&srcn); + _mm_store_ss(&bufferw, t0); + _mm_store_ss(&bufferw+n_buffer, t0); + t0 = _mm_load_ss(&bufferw+o); + _mm_store_ss(&dstn, t0); + w = w + 1 >= n_buffer ? 0 : w + 1; + } + } else { + __m128 fb0 = _mm_set1_ps(fb); + __m128 ff0 = _mm_set1_ps(ff); - for(n = 0; n < unrolled; n += 4) { - t0 = _mm_load_ps(&srcn); - _mm_storeu_ps(&bufferw, t0); - _mm_storeu_ps(&bufferw+n_buffer, t0); - t0 = _mm_loadu_ps(&bufferw+o); - _mm_store_ps(&dstn, t0); - w = w + 4 >= n_buffer ? 0 : w + 4; - } - for(; n < n_samples; n++) { - t0 = _mm_load_ss(&srcn); - _mm_store_ss(&bufferw, t0); - _mm_store_ss(&bufferw+n_buffer, t0); - t0 = _mm_load_ss(&bufferw+o); - _mm_store_ss(&dstn, t0); - w = w + 1 >= n_buffer ? 0 : w + 1; + if (SPA_IS_ALIGNED(src, 16) && + SPA_IS_ALIGNED(dst, 16) && delay >= 4) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + t0 = _mm_loadu_ps(&bufferw+o); + t1 = _mm_load_ps(&srcn); + t2 = _mm_mul_ps(t0, fb0); + t2 = _mm_add_ps(t2, t1); + _mm_storeu_ps(&bufferw, t2); + _mm_storeu_ps(&bufferw+n_buffer, t2); + t2 = _mm_mul_ps(t1, ff0); + t2 = _mm_add_ps(t2, t0); + _mm_store_ps(&dstn, t2); + w = w + 4 >= n_buffer ? 0 : w + 4; + } + for(; n < n_samples; n++) { + t0 = _mm_load_ss(&bufferw+o); + t1 = _mm_load_ss(&srcn); + t2 = _mm_mul_ss(t0, fb0); + t2 = _mm_add_ss(t2, t1); + _mm_store_ss(&bufferw, t2); + _mm_store_ss(&bufferw+n_buffer, t2); + t2 = _mm_mul_ps(t1, ff0); + t2 = _mm_add_ps(t2, t0); + _mm_store_ss(&dstn, t2); + w = w + 1 >= n_buffer ? 0 : w + 1; + } } *pos = w; }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/filter-graph/audio-dsp.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/filter-graph/audio-dsp.c
Changed
@@ -23,13 +23,13 @@ static const struct dsp_info dsp_table = { -#if defined (HAVE_AVX) - { SPA_CPU_FLAG_AVX, +#if defined (HAVE_AVX2) + { SPA_CPU_FLAG_AVX2, .funcs.clear = dsp_clear_c, .funcs.copy = dsp_copy_c, - .funcs.mix_gain = dsp_mix_gain_avx, + .funcs.mix_gain = dsp_mix_gain_avx2, .funcs.biquad_run = dsp_biquad_run_sse, - .funcs.sum = dsp_sum_avx, + .funcs.sum = dsp_sum_avx2, .funcs.linear = dsp_linear_c, .funcs.mult = dsp_mult_c, .funcs.fft_new = dsp_fft_new_c, @@ -38,8 +38,8 @@ .funcs.fft_memfree = dsp_fft_memfree_c, .funcs.fft_memclear = dsp_fft_memclear_c, .funcs.fft_run = dsp_fft_run_c, - .funcs.fft_cmul = dsp_fft_cmul_avx, - .funcs.fft_cmuladd = dsp_fft_cmuladd_avx, + .funcs.fft_cmul = dsp_fft_cmul_avx2, + .funcs.fft_cmuladd = dsp_fft_cmuladd_avx2, .funcs.delay = dsp_delay_sse, }, #endif
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/filter-graph/audio-dsp.h -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/filter-graph/audio-dsp.h
Changed
@@ -58,7 +58,8 @@ float * SPA_RESTRICT out, const float * SPA_RESTRICT in, uint32_t n_src, uint32_t n_samples); void (*delay) (void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, uint32_t delay, - float *dst, const float *src, uint32_t n_samples); + float *dst, const float *src, uint32_t n_samples, + float fb, float ff); }; static inline void spa_fga_dsp_clear(struct spa_fga_dsp *obj, float * SPA_RESTRICT dst, uint32_t n_samples) @@ -159,10 +160,11 @@ } static inline void spa_fga_dsp_delay(struct spa_fga_dsp *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, uint32_t delay, - float *dst, const float *src, uint32_t n_samples) + float *dst, const float *src, uint32_t n_samples, + float fb, float ff) { spa_api_method_v(spa_fga_dsp, &obj->iface, delay, 0, - buffer, pos, n_buffer, delay, dst, src, n_samples); + buffer, pos, n_buffer, delay, dst, src, n_samples, fb, ff); } #endif /* SPA_FGA_DSP_H */
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/filter-graph/filter-graph.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/filter-graph/filter-graph.c
Changed
@@ -80,7 +80,6 @@ unsigned long *output; unsigned long *control; unsigned long *notify; - float *default_control; }; struct port { @@ -94,6 +93,9 @@ uint32_t n_links; uint32_t external; + bool control_initialized; + + float control_current; float control_dataMAX_HNDL; float *audio_dataMAX_HNDL; void *audio_memMAX_HNDL; @@ -193,6 +195,9 @@ struct volume volume2; + uint32_t default_inputs; + uint32_t default_outputs; + uint32_t n_inputs; uint32_t n_outputs; uint32_t inputs_positionMAX_CHANNELS; @@ -257,16 +262,23 @@ impl->info.change_mask = impl->info_all; if (impl->info.change_mask || full) { char n_inputs64, n_outputs64, latency64; + char n_default_inputs64, n_default_outputs64; struct spa_dict_item items6; struct spa_dict dict = SPA_DICT(items, 0); char in_posMAX_CHANNELS * 8; char out_posMAX_CHANNELS * 8; + /* these are the current graph inputs/outputs */ snprintf(n_inputs, sizeof(n_inputs), "%d", impl->graph.n_inputs); snprintf(n_outputs, sizeof(n_outputs), "%d", impl->graph.n_outputs); + /* these are the default number of graph inputs/outputs */ + snprintf(n_default_inputs, sizeof(n_default_inputs), "%d", impl->graph.default_inputs); + snprintf(n_default_outputs, sizeof(n_default_outputs), "%d", impl->graph.default_outputs); itemsdict.n_items++ = SPA_DICT_ITEM("n_inputs", n_inputs); itemsdict.n_items++ = SPA_DICT_ITEM("n_outputs", n_outputs); + itemsdict.n_items++ = SPA_DICT_ITEM("n_default_inputs", n_default_inputs); + itemsdict.n_items++ = SPA_DICT_ITEM("n_default_outputs", n_default_outputs); if (graph->n_inputs_position) { print_channels(in_pos, sizeof(in_pos), graph->n_inputs_position, graph->inputs_position); @@ -339,12 +351,6 @@ return 0; } -static float get_default(struct impl *impl, struct descriptor *desc, uint32_t p) -{ - struct spa_fga_port *port = &desc->desc->portsp; - return port->def; -} - static struct node *find_node(struct graph *graph, const char *name) { struct node *node; @@ -433,6 +439,20 @@ return NULL; } +static void get_ranges(struct impl *impl, struct spa_fga_port *p, + float *def, float *min, float *max) +{ + uint32_t rate = impl->rate ? impl->rate : DEFAULT_RATE; + *def = p->def; + *min = p->min; + *max = p->max; + if (p->hint & SPA_FGA_HINT_SAMPLE_RATE) { + *def *= rate; + *min *= rate; + *max *= rate; + } +} + static int impl_enum_prop_info(void *object, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -447,7 +467,6 @@ struct spa_fga_port *p; float def, min, max; char name512; - uint32_t rate = impl->rate ? impl->rate : DEFAULT_RATE; if (idx >= graph->n_control) return 0; @@ -458,15 +477,7 @@ d = desc->desc; p = &d->portsport->p; - if (p->hint & SPA_FGA_HINT_SAMPLE_RATE) { - def = p->def * rate; - min = p->min * rate; - max = p->max * rate; - } else { - def = p->def; - min = p->min; - max = p->max; - } + get_ranges(impl, p, &def, &min, &max); if (node->name0 != '\0') snprintf(name, sizeof(name), "%s:%s", node->name, p->name); @@ -565,41 +576,58 @@ return 1; } -static int port_set_control_value(struct port *port, float *value, uint32_t id) +static int port_id_set_control_value(struct port *port, uint32_t id, float value) { struct node *node = port->node; struct impl *impl = node->graph->impl; - struct descriptor *desc = node->desc; + struct spa_fga_port *p = &desc->desc->portsport->p; float old; bool changed; old = port->control_dataid; - port->control_dataid = value ? *value : desc->default_controlport->idx; + port->control_dataid = value; + spa_log_info(impl->log, "control %d %d ('%s') from %f to %f", port->idx, id, - desc->desc->portsport->p.name, old, port->control_dataid); + p->name, old, value); + changed = old != port->control_dataid; node->control_changed |= changed; + return changed ? 1 : 0; } +static int port_set_control_value(struct port *port, float *value) +{ + struct node *node = port->node; + struct impl *impl = node->graph->impl; + struct spa_fga_port *p; + float v, def, min, max; + uint32_t i; + int count = 0; + + p = &node->desc->desc->portsport->p; + get_ranges(impl, p, &def, &min, &max); + v = SPA_CLAMP(value ? *value : def, min, max); + + port->control_current = v; + port->control_initialized = true; + + for (i = 0; i < node->n_hndl; i++) + count += port_id_set_control_value(port, i, v); + + return count; +} + static int set_control_value(struct node *node, const char *name, float *value) { struct port *port; - int count = 0; - uint32_t i, n_hndl; port = find_port(node, name, SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL); if (port == NULL) return -ENOENT; - /* if we don't have any instances yet, set the first control value, we will - * copy to other instances later */ - n_hndl = SPA_MAX(1u, port->node->n_hndl); - for (i = 0; i < n_hndl; i++) - count += port_set_control_value(port, value, i); - - return count; + return port_set_control_value(port, value); } static int parse_params(struct graph *graph, const struct spa_pod *pod) @@ -706,7 +734,7 @@ v = v * (vol->maxn_port - vol->minn_port) + vol->minn_port; n_hndl = SPA_MAX(1u, p->node->n_hndl); - res += port_set_control_value(p, &v, i % n_hndl); + res += port_id_set_control_value(p, i % n_hndl, v); } return res; } @@ -925,7 +953,6 @@ free(desc->input); free(desc->output); free(desc->control); - free(desc->default_control); free(desc->notify); free(desc); }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/filter-graph/meson.build -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/filter-graph/meson.build
Changed
@@ -18,16 +18,16 @@ simd_cargs += '-DHAVE_SSE' simd_dependencies += filter_graph_sse endif -if have_avx - filter_graph_avx = static_library('filter_graph_avx', - 'audio-dsp-avx.c' , +if have_avx2 + filter_graph_avx2 = static_library('filter_graph_avx2', + 'audio-dsp-avx2.c' , include_directories : configinc, - c_args : avx_args, fma_args,'-O3', '-DHAVE_AVX', + c_args : avx2_args, fma_args,'-O3', '-DHAVE_AVX2', dependencies : spa_dep , install : false ) - simd_cargs += '-DHAVE_AVX' - simd_dependencies += filter_graph_avx + simd_cargs += '-DHAVE_AVX2' + simd_dependencies += filter_graph_avx2 endif if have_neon filter_graph_neon = static_library('filter_graph_neon',
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/filter-graph/plugin_builtin.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/filter-graph/plugin_builtin.c
Changed
@@ -14,6 +14,7 @@ #include <sys/wait.h> #include <sys/socket.h> #include <fcntl.h> +#include <time.h> #include <spa/utils/json.h> #include <spa/utils/result.h> @@ -1228,7 +1229,7 @@ struct spa_log *log; unsigned long rate; - float *port4; + float *port6; float delay; uint32_t delay_samples; @@ -1334,7 +1335,8 @@ } if (in != NULL && out != NULL) { spa_fga_dsp_delay(impl->dsp, impl->buffer, &impl->ptr, impl->buffer_samples, - impl->delay_samples, out, in, SampleCount); + impl->delay_samples, out, in, SampleCount, + impl->port40, impl->port50); } if (impl->port3 != NULL) impl->port30 = impl->latency; @@ -1359,6 +1361,16 @@ .hint = SPA_FGA_HINT_LATENCY, .flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_CONTROL, }, + { .index = 4, + .name = "Feedback", + .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL, + .def = 0.0f, .min = -10.0f, .max = 10.0f + }, + { .index = 5, + .name = "Feedforward", + .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL, + .def = 0.0f, .min = -10.0f, .max = 10.0f + }, }; static const struct spa_fga_descriptor delay_desc = { @@ -3104,6 +3116,136 @@ .cleanup = builtin_cleanup, }; +/* busy */ +struct busy_impl { + struct plugin *plugin; + + struct spa_fga_dsp *dsp; + struct spa_log *log; + + unsigned long rate; + + float wait_scale; + float cpu_scale; +}; + +static void *busy_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor, + unsigned long SampleRate, int index, const char *config) +{ + struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin); + struct busy_impl *impl; + struct spa_json it1; + const char *val; + char key256; + float wait_percent = 0.0f, cpu_percent = 0.0f; + int len; + + if (config != NULL) { + if (spa_json_begin_object(&it0, config, strlen(config)) <= 0) { + spa_log_error(pl->log, "busy:config must be an object"); + return NULL; + } + + while ((len = spa_json_object_next(&it0, key, sizeof(key), &val)) > 0) { + if (spa_streq(key, "wait-percent")) { + if (spa_json_parse_float(val, len, &wait_percent) <= 0) { + spa_log_error(pl->log, "busy:wait-percent requires a number"); + return NULL; + } + } else if (spa_streq(key, "cpu-percent")) { + if (spa_json_parse_float(val, len, &cpu_percent) <= 0) { + spa_log_error(pl->log, "busy:cpu-percent requires a number"); + return NULL; + } + } else { + spa_log_warn(pl->log, "busy: ignoring config key: '%s'", key); + } + } + if (wait_percent <= 0.0f) + wait_percent = 0.0f; + if (cpu_percent <= 0.0f) + cpu_percent = 0.0f; + } + + impl = calloc(1, sizeof(*impl)); + if (impl == NULL) + return NULL; + + impl->plugin = pl; + impl->dsp = pl->dsp; + impl->log = pl->log; + impl->rate = SampleRate; + impl->wait_scale = wait_percent * SPA_NSEC_PER_SEC / (100.0f * SampleRate); + impl->cpu_scale = cpu_percent * SPA_NSEC_PER_SEC / (100.0f * SampleRate); + spa_log_info(impl->log, "wait-percent:%f cpu-percent:%f", wait_percent, cpu_percent); + + return impl; +} + +static void busy_run(void * Instance, unsigned long SampleCount) +{ + struct busy_impl *impl = Instance; + struct timespec ts; + uint64_t busy_nsec; + + if (impl->wait_scale > 0.0f) { + busy_nsec = (uint64_t)(impl->wait_scale * SampleCount); + ts.tv_sec = busy_nsec / SPA_NSEC_PER_SEC; + ts.tv_nsec = busy_nsec % SPA_NSEC_PER_SEC; + clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); + } + if (impl->cpu_scale > 0.0f) { + clock_gettime(CLOCK_MONOTONIC, &ts); + busy_nsec = SPA_TIMESPEC_TO_NSEC(&ts); + busy_nsec += (uint64_t)(impl->cpu_scale * SampleCount); + do { + clock_gettime(CLOCK_MONOTONIC, &ts); + } while ((uint64_t)SPA_TIMESPEC_TO_NSEC(&ts) < busy_nsec); + } +} + +static const struct spa_fga_descriptor busy_desc = { + .name = "busy", + .flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA, + + .n_ports = 0, + .ports = NULL, + + .instantiate = busy_instantiate, + .connect_port = builtin_connect_port, + .run = busy_run, + .cleanup = builtin_cleanup, +}; + +/* null */ +static void null_run(void * Instance, unsigned long SampleCount) +{ +} + +static struct spa_fga_port null_ports = { + { .index = 0, + .name = "In", + .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO, + }, + { .index = 1, + .name = "Control", + .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL, + }, +}; + +static const struct spa_fga_descriptor null_desc = { + .name = "null", + .flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA, + + .n_ports = SPA_N_ELEMENTS(null_ports), + .ports = null_ports, + + .instantiate = builtin_instantiate, + .connect_port = builtin_connect_port, + .run = null_run, + .cleanup = builtin_cleanup, +}; + static const struct spa_fga_descriptor * builtin_descriptor(unsigned long Index) { switch(Index) { @@ -3169,6 +3311,10 @@ return &zeroramp_desc; case 30: return &noisegate_desc; + case 31: + return &busy_desc; + case 32: + return &null_desc; } return NULL; }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/filter-graph/plugin_sofa.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/filter-graph/plugin_sofa.c
Changed
@@ -32,6 +32,7 @@ unsigned long rate; float *port7; int n_samples, blocksize, tailsize; + float gain; float *tmp2; struct MYSOFA_EASY *sofa; @@ -71,6 +72,7 @@ impl->plugin = pl; impl->dsp = pl->dsp; impl->log = pl->log; + impl->gain = 1.0f; while ((len = spa_json_object_next(&it0, key, sizeof(key), &val)) > 0) { if (spa_streq(key, "blocksize")) { @@ -94,6 +96,13 @@ goto error; } } + else if (spa_streq(key, "gain")) { + if (spa_json_parse_float(val, len, &impl->gain) <= 0) { + spa_log_error(impl->log, "spatializer:gain requires a number"); + errno = EINVAL; + goto error; + } + } } if (!filename0) { spa_log_error(impl->log, "spatializer:filename was not given"); @@ -168,11 +177,14 @@ reason = "Only sources with MC supported"; errno = ENOTSUP; break; - default: case MYSOFA_INTERNAL_ERROR: errno = EIO; reason = "Internal error"; break; + default: + errno = ret; + reason = strerror(errno); + break; } spa_log_error(impl->log, "Unable to load HRTF from %s: %s (%d)", filename, reason, ret); goto error; @@ -183,8 +195,8 @@ if (impl->tailsize <= 0) impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768); - spa_log_info(impl->log, "using n_samples:%u %d:%d blocksize sofa:%s", impl->n_samples, - impl->blocksize, impl->tailsize, filename); + spa_log_info(impl->log, "using n_samples:%u %d:%d blocksize gain:%f sofa:%s", impl->n_samples, + impl->blocksize, impl->tailsize, impl->gain, filename); impl->tmp0 = calloc(impl->plugin->quantum_limit, sizeof(float)); impl->tmp1 = calloc(impl->plugin->quantum_limit, sizeof(float)); @@ -250,6 +262,13 @@ if (impl->r_conv2) convolver_free(impl->r_conv2); + if (impl->gain != 1.0f) { + for (int i = 0; i < impl->n_samples; i++) { + left_iri *= impl->gain; + right_iri *= impl->gain; + } + } + impl->l_conv2 = convolver_new(impl->dsp, impl->blocksize, impl->tailsize, left_ir, impl->n_samples); impl->r_conv2 = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
View file
_service:download_files:pipewire-1.5.84.tar.bz2/spa/plugins/support/dbus.c -> _service:download_files:pipewire-1.6.0.tar.bz2/spa/plugins/support/dbus.c
Changed
@@ -141,20 +141,34 @@ struct connection *conn = userdata; struct impl *impl = conn->impl; struct source_data *data; + int dupfd; spa_log_debug(impl->log, "add watch %p %d", watch, dbus_watch_get_unix_fd(watch)); data = calloc(1, sizeof(struct source_data)); + if (data == NULL) + goto error; data->conn = conn; + dupfd = dup(dbus_watch_get_unix_fd(watch)); + if (dupfd == -1) + goto error_free; + /* we dup because dbus tends to add the same fd multiple times and our epoll * implementation does not like that */ data->source = spa_loop_utils_add_io(impl->utils, - dup(dbus_watch_get_unix_fd(watch)), - dbus_to_io(watch), true, handle_io_event, watch); + dupfd, dbus_to_io(watch), true, handle_io_event, watch); + if (data->source == NULL) + goto error_free; spa_list_append(&conn->source_list, &data->link); dbus_watch_set_data(watch, data, source_data_free); return TRUE; + +error_free: + free(data); +error: + spa_log_error(impl->log, "Failed to add watch: %m"); + return FALSE; } static void remove_watch(DBusWatch *watch, void *userdata) @@ -221,8 +235,12 @@ spa_log_debug(impl->log, "add timeout %p conn:%p impl:%p", timeout, conn, impl); data = calloc(1, sizeof(struct source_data)); + if (data == NULL) + goto error; data->conn = conn; data->source = spa_loop_utils_add_timer(impl->utils, handle_timer_event, timeout); + if (data->source == NULL) + goto error_free; spa_list_append(&conn->source_list, &data->link); dbus_timeout_set_data(timeout, data, source_data_free); @@ -233,6 +251,12 @@ spa_loop_utils_update_timer(impl->utils, data->source, &ts, NULL, false); return TRUE; + +error_free: + free(data); +error: + spa_log_error(impl->log, "Failed to add timeout: %m"); + return FALSE; } static void remove_timeout(DBusTimeout *timeout, void *userdata) @@ -420,6 +444,8 @@ int res; conn = calloc(1, sizeof(struct connection)); + if (conn == NULL) + return NULL; conn->this = impl_connection; conn->impl = impl; conn->type = type;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/daemon/filter-chain/spatializer-7.1.conf -> _service:download_files:pipewire-1.6.0.tar.bz2/src/daemon/filter-chain/spatializer-7.1.conf
Changed
@@ -19,6 +19,8 @@ name = spFL config = { filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa" + # The gain depends on the .sofa file in use + gain = 0.5 } control = { "Azimuth" = 30.0 @@ -32,6 +34,7 @@ name = spFR config = { filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa" + gain = 0.5 } control = { "Azimuth" = 330.0 @@ -45,6 +48,7 @@ name = spFC config = { filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa" + gain = 0.5 } control = { "Azimuth" = 0.0 @@ -58,6 +62,7 @@ name = spRL config = { filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa" + gain = 0.5 } control = { "Azimuth" = 150.0 @@ -71,6 +76,7 @@ name = spRR config = { filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa" + gain = 0.5 } control = { "Azimuth" = 210.0 @@ -84,6 +90,7 @@ name = spSL config = { filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa" + gain = 0.5 } control = { "Azimuth" = 90.0 @@ -97,6 +104,7 @@ name = spSR config = { filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa" + gain = 0.5 } control = { "Azimuth" = 270.0 @@ -110,6 +118,7 @@ name = spLFE config = { filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa" + gain = 0.5 } control = { "Azimuth" = 0.0 @@ -118,8 +127,32 @@ } } - { type = builtin label = mixer name = mixL } - { type = builtin label = mixer name = mixR } + { type = builtin label = mixer name = mixL + control = { + # Set individual left mixer gain if needed + #"Gain 1" = 1.0 + #"Gain 2" = 1.0 + #"Gain 3" = 1.0 + #"Gain 4" = 1.0 + #"Gain 5" = 1.0 + #"Gain 6" = 1.0 + #"Gain 7" = 1.0 + #"Gain 8" = 1.0 + } + } + { type = builtin label = mixer name = mixR + control = { + # Set individual right mixer gain if needed + #"Gain 1" = 1.0 + #"Gain 2" = 1.0 + #"Gain 3" = 1.0 + #"Gain 4" = 1.0 + #"Gain 5" = 1.0 + #"Gain 6" = 1.0 + #"Gain 7" = 1.0 + #"Gain 8" = 1.0 + } + } links = # output
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/daemon/pipewire-avb.conf.in -> _service:download_files:pipewire-1.6.0.tar.bz2/src/daemon/pipewire-avb.conf.in
Changed
@@ -67,6 +67,7 @@ # the addresses this server listens on #ifname = "eth0.2" ifname = "enp3s0" + milan = false } avb.properties.rules =
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/examples/utils.h
Added
@@ -0,0 +1,60 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2026 Red Hat */ +/* SPDX-License-Identifier: MIT */ + +static inline char * +encode_hex(const uint8_t *data, size_t size) +{ + FILE *ms; + char *encoded = NULL; + size_t encoded_size = 0; + size_t i; + + ms = open_memstream(&encoded, &encoded_size); + for (i = 0; i < size; i++) { + fprintf(ms, "%02x", datai); + } + fclose(ms); + + return encoded; +} + +static inline int8_t +ascii_hex_to_hex(uint8_t ascii_hex) +{ + if (ascii_hex >= '0' && ascii_hex <= '9') + return ascii_hex - '0'; + else if (ascii_hex >= 'a' && ascii_hex <= 'f') + return ascii_hex - 'a' + 10; + else if (ascii_hex >= 'A' && ascii_hex <= 'F') + return ascii_hex - 'A' + 10; + else + return -1; +} + +static inline int +decode_hex(const char *encoded, uint8_t *data, size_t size) +{ + size_t length; + size_t i; + + length = strlen(encoded); + + if (size < (length / 2) * sizeof(uint8_t)) + return -1; + + i = 0; + while (i < length) { + int8_t top = ascii_hex_to_hex(encodedi); + int8_t bottom = ascii_hex_to_hex(encodedi + 1); + + if (top == -1 || bottom == -1) + return -1; + + uint8_t el = top << 4 | bottom; + datai / 2 = el; + i += 2; + } + + return 1; +}
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/examples/video-play-fixate.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/examples/video-play-fixate.c
Changed
@@ -13,13 +13,28 @@ #include <unistd.h> #include <signal.h> #include <libdrm/drm_fourcc.h> +#include <sys/mman.h> +#include <sys/sysmacros.h> +#include <assert.h> +#include <spa/utils/json.h> #include <spa/utils/result.h> +#include <spa/param/dict-utils.h> +#include <spa/param/peer-utils.h> #include <spa/param/video/format-utils.h> #include <spa/param/props.h> #include <spa/debug/format.h> +#include <spa/debug/pod.h> #include <pipewire/pipewire.h> +#include <pipewire/capabilities.h> + +#include "utils.h" + +/* Comment out to test device ID negotation backward compatibility. */ +#define SUPPORT_DEVICE_ID_NEGOTIATION 1 +/* If defined, emulate failing to import DMA buffer. */ +#define EMULATE_DMA_BUF_IMPORT_FAIL 1 #define WIDTH 640 #define HEIGHT 480 @@ -27,6 +42,8 @@ #define MAX_BUFFERS 64 #define MAX_MOD 8 +#define MAX_PARAMS 16 +#define MAX_DEVICE_IDS 16 #include "sdl.h" @@ -46,6 +63,35 @@ uint64_t modifiersMAX_MOD; }; +struct { + unsigned int major; + unsigned int minor; + struct modifier_info mod_info; +} devices = { +#if 1 + { + .major = 100, + .minor = 100, + .mod_info = { + .spa_format = SPA_VIDEO_FORMAT_RGBA, + .modifiers = { DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID }, + .n_modifiers = 2, + }, + }, +#endif +#if 1 + { + .major = 200, + .minor = 200, + .mod_info = { + .spa_format = SPA_VIDEO_FORMAT_RGBA, + .modifiers = {DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_GENERIC_16_16_TILE}, + .n_modifiers = 2, + }, + }, +#endif +}; + struct data { const char *path; @@ -64,12 +110,20 @@ int32_t stride; struct spa_rectangle size; - uint32_t n_mod_info; - struct modifier_info mod_info2; - int counter; + + bool capabilities_known; + bool device_negotiation_supported; + + dev_t device_idsMAX_DEVICE_IDS; + size_t n_device_ids; + + int used_device_index; }; +static int build_formats(struct data *data, struct spa_pod_builder *b, const struct spa_pod **params); + +#ifdef EMULATE_DMA_BUF_IMPORT_FAIL static struct pw_version parse_pw_version(const char* version) { struct pw_version pw_version; sscanf(version, "%d.%d.%d", &pw_version.major, &pw_version.minor, @@ -84,41 +138,38 @@ return major <= pw_version.major && minor <= pw_version.minor && micro <= pw_version.micro; } -static void init_modifiers(struct data *data) +static void strip_modifier(struct data *data, uint32_t spa_format, uint64_t modifier) { - data->n_mod_info = 1; - data->mod_info0.spa_format = SPA_VIDEO_FORMAT_RGB; - data->mod_info0.n_modifiers = 2; - data->mod_info0.modifiers0 = DRM_FORMAT_MOD_LINEAR; - data->mod_info0.modifiers1 = DRM_FORMAT_MOD_INVALID; -} + struct modifier_info *mod_info; -static void destroy_modifiers(struct data *data) -{ - data->mod_info0.n_modifiers = 0; -} + assert(data->used_device_index >= 0); -static void strip_modifier(struct data *data, uint32_t spa_format, uint64_t modifier) -{ - if (data->mod_info0.spa_format != spa_format) + mod_info = &devicesdata->used_device_index.mod_info; + + if (mod_info->spa_format != spa_format) return; - struct modifier_info *mod_info = &data->mod_info0; + uint32_t counter = 0; // Dropping of single modifiers is only supported on PipeWire 0.3.40 and newer. // On older PipeWire just dropping all modifiers might work on Versions newer then 0.3.33/35 if (has_pw_version(0,3,40)) { - printf("Dropping a single modifier\n"); + printf("Dropping a single modifier from device %u:%u\n", + devicesdata->used_device_index.major, + devicesdata->used_device_index.minor); for (uint32_t i = 0; i < mod_info->n_modifiers; i++) { if (mod_info->modifiersi == modifier) continue; mod_info->modifierscounter++ = mod_info->modifiersi; } } else { - printf("Dropping all modifiers\n"); + printf("Dropping all modifiers from device %u:%u\n", + devicesdata->used_device_index.major, + devicesdata->used_device_index.minor); counter = 0; } mod_info->n_modifiers = counter; } +#endif /* EMULATE_DMA_BUF_IMPORT_FAIL */ static void handle_events(struct data *data) { @@ -132,15 +183,36 @@ } } -static struct spa_pod *build_format(struct spa_pod_builder *b, SDL_RendererInfo *info, enum spa_video_format format, - uint64_t *modifiers, int modifier_count) +static struct spa_pod *build_format(struct data *data, struct spa_pod_builder *b, + SDL_RendererInfo *info, enum spa_video_format format, int device_index) { struct spa_pod_frame f2; int i, c; + uint64_t *modifiers; + int modifier_count; + + if (device_index == -1) { + modifiers = NULL; + modifier_count = 0; + } else { + struct modifier_info *mod_info = &devicesdevice_index.mod_info; + + modifiers = mod_info->modifiers; + modifier_count = mod_info->n_modifiers; + } spa_pod_builder_push_object(b, &f0, 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); + /* device */ + if (data->device_negotiation_supported && device_index >= 0) { + dev_t device_id = makedev(devicesdevice_index.major, + devicesdevice_index.minor); + + spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_deviceId, + SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_bytes(b, &device_id, sizeof device_id); + } /* format */ spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); /* modifiers */ @@ -192,10 +264,12 @@ struct pw_stream *stream = data->stream; struct pw_buffer *b; struct spa_buffer *buf; + struct spa_data *d; void *sdata, *ddata; int sstride, dstride, ostride;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/examples/video-src-fixate.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/examples/video-src-fixate.c
Changed
@@ -18,11 +18,24 @@ #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> +#include <sys/sysmacros.h> +#include <assert.h> +#include <spa/param/dict-utils.h> +#include <spa/param/peer-utils.h> #include <spa/param/video/format-utils.h> #include <spa/debug/format.h> +#include <spa/debug/pod.h> #include <pipewire/pipewire.h> +#include <pipewire/capabilities.h> + +#include "utils.h" + +/* Comment out to test device ID negotation backward compatibility. */ +#define SUPPORT_DEVICE_ID_NEGOTIATION 1 +/* Comment out to disable device IDs listing */ +#define SUPPORT_DEVICE_IDS_LIST 1 #define BPP 4 #define CURSOR_WIDTH 64 @@ -30,10 +43,34 @@ #define CURSOR_BPP 4 #define MAX_BUFFERS 64 +#define MAX_PARAMS 16 +#define MAX_MOD 10 #define M_PI_M2 ( M_PI + M_PI ) -uint64_t supported_modifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR}; +struct { + unsigned int major; + unsigned int minor; + uint64_t supported_modifiersMAX_MOD; + size_t n_supported_modifiers; +} devices = { +#if 1 + { + .major = 100, + .minor = 100, + .supported_modifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR}, + .n_supported_modifiers = 2, + }, +#endif +#if 1 + { + .major = 200, + .minor = 200, + .supported_modifiers = {DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_GENERIC_16_16_TILE}, + .n_supported_modifiers = 2, + }, +#endif +}; struct data { struct pw_thread_loop *loop; @@ -50,6 +87,9 @@ double crop; double accumulator; + + bool capabilities_known; + bool device_negotiation_supported; }; static void draw_elipse(uint32_t *dst, int width, int height, uint32_t color) @@ -70,14 +110,24 @@ } } -static struct spa_pod *fixate_format(struct spa_pod_builder *b, enum spa_video_format format, - uint64_t *modifier) +static struct spa_pod *fixate_format(struct data *data, struct spa_pod_builder *b, + int device_index, enum spa_video_format format, uint64_t *modifier) { struct spa_pod_frame f1; spa_pod_builder_push_object(b, &f0, 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); + + /* device */ + if (data->device_negotiation_supported) { + dev_t device_id = makedev(devicesdevice_index.major, + devicesdevice_index.minor); + + spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_deviceId, + SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_bytes(b, &device_id, sizeof device_id); + } /* format */ spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); /* modifiers */ @@ -98,15 +148,36 @@ return spa_pod_builder_pop(b, &f0); } -static struct spa_pod *build_format(struct spa_pod_builder *b, enum spa_video_format format, - uint64_t *modifiers, int modifier_count) +static struct spa_pod *build_format(struct data *data, struct spa_pod_builder *b, + int device_index, enum spa_video_format format) { + uint64_t *modifiers; + int modifier_count; struct spa_pod_frame f2; int i, c; + if (device_index == -1) { + modifiers = NULL; + modifier_count = 0; + } else { + modifiers = devicesdevice_index.supported_modifiers; + modifier_count = devicesdevice_index.n_supported_modifiers; + } + spa_pod_builder_push_object(b, &f0, 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); + + /* device */ + if (data->device_negotiation_supported && device_index >= 0) { + dev_t device_id = makedev(devicesdevice_index.major, + devicesdevice_index.minor); + + spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_deviceId, + SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_bytes(b, &device_id, sizeof device_id); + } + /* format */ spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); /* modifiers */ @@ -295,27 +366,24 @@ if ((d0.type & (1<<SPA_DATA_DmaBuf)) > 0) { printf("pretend to support dmabufs while setting the fd to -1\n"); d0.type = SPA_DATA_DmaBuf; +#ifdef HAVE_MEMFD_CREATE + d0.fd = memfd_create("video-src-fixate-fake-dmabuf", MFD_CLOEXEC | MFD_ALLOW_SEALING); +#else d0.fd = -1; - d0.data = NULL; - return; - } - - if ((d0.type & (1<<SPA_DATA_MemFd)) == 0) { - pw_log_error("unsupported data type %08x", d0.type); - return; - } - - printf("use memfd\n"); - /* create the memfd on the buffer, set the type and flags */ - d0.type = SPA_DATA_MemFd; - d0.flags = SPA_DATA_FLAG_READWRITE | SPA_DATA_FLAG_MAPPABLE; +#endif + } else { + printf("use memfd\n"); + /* create the memfd on the buffer, set the type and flags */ + d0.type = SPA_DATA_MemFd; + d0.flags = SPA_DATA_FLAG_READWRITE | SPA_DATA_FLAG_MAPPABLE; #ifdef HAVE_MEMFD_CREATE - d0.fd = memfd_create("video-src-fixate-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING); + d0.fd = memfd_create("video-src-fixate-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING); #else - d0.fd = -1; + d0.fd = -1; #endif + } if (d0.fd == -1) { - pw_log_error("can't create memfd: %m"); + pw_log_error("can't open file descriptor: %m"); return; } d0.mapoffset = 0; @@ -359,8 +427,115 @@ close(d0.fd); } -/* Be notified when the stream param changes. We're only looking at the - * format param. +static void +discover_capabilities(struct data *data, const struct spa_pod *param) +{ +#ifdef SUPPORT_DEVICE_ID_NEGOTIATION + struct spa_peer_param_info info; + void *state = NULL; + + while (spa_peer_param_parse(param, &info, sizeof(info), &state) == 1) { + struct spa_param_dict_info di; + + if (spa_param_dict_parse(info.param, &di, sizeof(di)) > 0) { + struct spa_dict dict; + struct spa_dict_item *items; + const struct spa_dict_item *it; + + if (spa_param_dict_info_parse(&di, sizeof(di), &dict, NULL) < 0)
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/gst/gstpipewiredeviceprovider.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/gst/gstpipewiredeviceprovider.c
Changed
@@ -792,6 +792,7 @@ GST_DEBUG_OBJECT (self, "disconnect"); g_clear_pointer ((struct pw_proxy**)&self->registry, pw_proxy_destroy); + spa_hook_remove (&self->core_listener); pw_thread_loop_unlock (self->core->loop); g_clear_pointer (&self->core, gst_pipewire_core_release); @@ -866,6 +867,7 @@ g_clear_pointer ((struct pw_proxy**)&self->registry, pw_proxy_destroy); if (self->core != NULL) { + spa_hook_remove (&self->core_listener); pw_thread_loop_unlock (self->core->loop); } g_clear_pointer (&self->core, gst_pipewire_core_release);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/gst/gstpipewirepool.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/gst/gstpipewirepool.c
Changed
@@ -209,8 +209,6 @@ data->b = b; data->buf = buf; data->crop = spa_buffer_find_meta_data (b->buffer, SPA_META_VideoCrop, sizeof(*data->crop)); - if (data->crop) - gst_buffer_add_video_crop_meta(buf); data->videotransform = spa_buffer_find_meta_data (b->buffer, SPA_META_VideoTransform, sizeof(*data->videotransform)); data->cursor = spa_buffer_find_meta_data (b->buffer, SPA_META_Cursor, sizeof(*data->cursor)); @@ -434,26 +432,25 @@ GST_LOG_OBJECT (pool, "release buffer %p", buffer); GstPipeWirePoolData *data = gst_pipewire_pool_get_data(buffer); + GstPipeWirePool *p = GST_PIPEWIRE_POOL (pool); + g_autoptr (GstPipeWireStream) s = g_weak_ref_get (&p->stream); GST_OBJECT_LOCK (pool); + pw_thread_loop_lock (s->core->loop); if (!data->queued && data->b != NULL) { - GstPipeWirePool *p = GST_PIPEWIRE_POOL (pool); - g_autoptr (GstPipeWireStream) s = g_weak_ref_get (&p->stream); int res; - pw_thread_loop_lock (s->core->loop); - if ((res = pw_stream_return_buffer (s->pwstream, data->b)) < 0) { GST_ERROR_OBJECT (pool,"can't return buffer %p; gstbuffer : %p, %s",data->b, buffer, spa_strerror(res)); } else { data->queued = TRUE; GST_DEBUG_OBJECT (pool, "returned buffer %p; gstbuffer:%p", data->b, buffer); } - - pw_thread_loop_unlock (s->core->loop); } + + pw_thread_loop_unlock (s->core->loop); GST_OBJECT_UNLOCK (pool); }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/gst/gstpipewiresrc.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/gst/gstpipewiresrc.c
Changed
@@ -781,7 +781,7 @@ crop = data->crop; if (crop) { - GstVideoCropMeta *meta = gst_buffer_get_video_crop_meta(buf); + GstVideoCropMeta *meta = gst_buffer_add_video_crop_meta(buf); if (meta) { meta->x = crop->region.position.x; meta->y = crop->region.position.y; @@ -1056,6 +1056,44 @@ return state; } + +static enum pw_stream_state +wait_negotiated (GstPipeWireSrc *this) +{ + enum pw_stream_state state; + const char *error = NULL; + struct timespec abstime; + + pw_thread_loop_get_time (this->stream->core->loop, &abstime, + GST_PIPEWIRE_DEFAULT_TIMEOUT * SPA_NSEC_PER_SEC); + + while (TRUE) { + state = pw_stream_get_state (this->stream->pwstream, &error); + + GST_DEBUG_OBJECT (this, "waiting for NEGOTIATED, now %s", pw_stream_state_as_string (state)); + if (state == PW_STREAM_STATE_ERROR) + break; + if (this->flushing) { + state = PW_STREAM_STATE_ERROR; + break; + } + + if (this->negotiated) + break; + + if (this->autoconnect) { + if (pw_thread_loop_timed_wait_full (this->stream->core->loop, &abstime) < 0) { + state = PW_STREAM_STATE_ERROR; + break; + } + } else { + pw_thread_loop_wait (this->stream->core->loop); + } + } + GST_DEBUG_OBJECT (this, state != PW_STREAM_STATE_ERROR ? "got negotiated signal" : "error during negotiation"); + return state; +} + static gboolean gst_pipewire_src_negotiate (GstBaseSrc * basesrc) { @@ -1067,7 +1105,6 @@ g_autoptr (GPtrArray) possible = NULL; gboolean result = FALSE; const char *error = NULL; - struct timespec abstime; uint32_t target_id; /* first see what is possible on our source pad */ @@ -1177,26 +1214,8 @@ (const struct spa_pod **)possible->pdata, possible->len); - pw_thread_loop_get_time (pwsrc->stream->core->loop, &abstime, - GST_PIPEWIRE_DEFAULT_TIMEOUT * SPA_NSEC_PER_SEC); - - while (TRUE) { - enum pw_stream_state state = pw_stream_get_state (pwsrc->stream->pwstream, &error); - - GST_DEBUG_OBJECT (basesrc, "waiting for NEGOTIATED, now %s", pw_stream_state_as_string (state)); - if (state == PW_STREAM_STATE_ERROR || pwsrc->flushing) - goto connect_error; - - if (pwsrc->negotiated) - break; - - if (pwsrc->autoconnect) { - if (pw_thread_loop_timed_wait_full (pwsrc->stream->core->loop, &abstime) < 0) - goto connect_error; - } else { - pw_thread_loop_wait (pwsrc->stream->core->loop); - } - } + if (wait_negotiated(pwsrc) == PW_STREAM_STATE_ERROR) + goto connect_error; negotiated_caps = g_steal_pointer (&pwsrc->caps); pw_thread_loop_unlock (pwsrc->stream->core->loop); @@ -1724,12 +1743,21 @@ break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: /* uncork and start recording */ + GST_DEBUG_OBJECT (this, "activating stream"); + pw_thread_loop_lock (this->stream->core->loop); pw_stream_set_active (this->stream->pwstream, true); + /* if state have been paused for longer time, the underlying node might + * be moved from idle to suspended, which would mean format cleared via + * handle_format_change. Wait for new format to avoid basesrc calling + * create() and get not-negotiated error as response. */ + if (wait_negotiated(this) == PW_STREAM_STATE_ERROR) + goto open_failed; pw_thread_loop_unlock (this->stream->core->loop); break; case GST_STATE_CHANGE_PLAYING_TO_PAUSED: /* stop recording ASAP by corking */ + GST_DEBUG_OBJECT (this, "in-activating stream"); pw_thread_loop_lock (this->stream->core->loop); pw_stream_set_active (this->stream->pwstream, false); pw_thread_loop_unlock (this->stream->core->loop);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/meson.build -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/meson.build
Changed
@@ -276,10 +276,6 @@ pipewire_module_protocol_deps = mathlib, dl_lib, pipewire_dep -if systemd_dep.found() - pipewire_module_protocol_deps += systemd_dep -endif - if selinux_dep.found() pipewire_module_protocol_deps += selinux_dep endif @@ -573,6 +569,22 @@ endif summary({'zeroconf-discover': build_module_zeroconf_discover}, bool_yn: true, section: 'Optional Modules') +# Several modules (rtp-sink, rtp-source, raop-sink) use the same code +# for actual RTP transport. To not have to recompile the same code +# multiple times, and to make the build script a little more robust +# (by avoiding build script code duplication), create a static library +# that contains that common code. +pipewire_module_rtp_common_lib = static_library('pipewire-module-rtp-common-lib', + 'module-rtp/stream.c' , + include_directories : configinc, + install : false, + dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep, +) +pipewire_module_rtp_common_dep = declare_dependency( + link_with: pipewire_module_rtp_common_lib, + dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep, +) + build_module_raop_discover = avahi_dep.found() if build_module_raop_discover pipewire_module_raop_discover = shared_library('pipewire-module-raop-discover', @@ -605,13 +617,12 @@ if build_module_raop pipewire_module_raop_sink = shared_library('pipewire-module-raop-sink', 'module-raop-sink.c', - 'module-raop/rtsp-client.c', - 'module-rtp/stream.c' , + 'module-raop/rtsp-client.c' , include_directories : configinc, install : true, install_dir : modules_install_dir, install_rpath: modules_install_dir, - dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep, openssl_lib, + dependencies : pipewire_module_rtp_common_dep, openssl_lib, ) endif summary({'raop-sink (requires OpenSSL)': build_module_raop}, bool_yn: true, section: 'Optional Modules') @@ -620,36 +631,33 @@ summary({'ROC': roc_dep.found()}, bool_yn: true, section: 'Streaming between daemons') pipewire_module_rtp_source = shared_library('pipewire-module-rtp-source', - 'module-rtp-source.c', - 'module-rtp/stream.c' , + 'module-rtp-source.c' , include_directories : configinc, install : true, install_dir : modules_install_dir, install_rpath: modules_install_dir, - dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep, + dependencies : pipewire_module_rtp_common_dep, ) pipewire_module_rtp_sink = shared_library('pipewire-module-rtp-sink', - 'module-rtp-sink.c', - 'module-rtp/stream.c' , + 'module-rtp-sink.c' , include_directories : configinc, install : true, install_dir : modules_install_dir, install_rpath: modules_install_dir, - dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep, + dependencies : pipewire_module_rtp_common_dep, ) build_module_rtp_session = avahi_dep.found() if build_module_rtp_session pipewire_module_rtp_session = shared_library('pipewire-module-rtp-session', - 'module-rtp/stream.c', - 'module-zeroconf-discover/avahi-poll.c', - 'module-rtp-session.c' , + 'module-zeroconf-discover/avahi-poll.c', + 'module-rtp-session.c' , include_directories : configinc, install : true, install_dir : modules_install_dir, install_rpath: modules_install_dir, - dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep, opus_dep, + dependencies : pipewire_module_rtp_common_dep, avahi_dep, ) endif @@ -685,7 +693,7 @@ build_module_roc = roc_dep.found() if build_module_roc pipewire_module_roc_sink = shared_library('pipewire-module-roc-sink', - 'module-roc-sink.c' , + 'module-roc-sink.c', 'module-roc/common.c', include_directories : configinc, install : true, install_dir : modules_install_dir, @@ -694,7 +702,7 @@ ) pipewire_module_roc_source = shared_library('pipewire-module-roc-source', - 'module-roc-source.c' , + 'module-roc-source.c', 'module-roc/common.c' , include_directories : configinc, install : true, install_dir : modules_install_dir, @@ -736,8 +744,23 @@ 'module-avb/acmp.c', 'module-avb/aecp.c', 'module-avb/aecp-aem.c', + 'module-avb/aecp-aem-cmds-resps/cmd-available.c', + 'module-avb/aecp-aem-cmds-resps/cmd-get-set-control.c', + 'module-avb/aecp-aem-cmds-resps/cmd-get-set-name.c', + 'module-avb/aecp-aem-cmds-resps/cmd-get-set-clock-source.c', + 'module-avb/aecp-aem-cmds-resps/cmd-get-set-sampling-rate.c', + 'module-avb/aecp-aem-cmds-resps/cmd-register-unsolicited-notifications.c', + 'module-avb/aecp-aem-cmds-resps/cmd-deregister-unsolicited-notifications.c', + 'module-avb/aecp-aem-cmds-resps/cmd-register-unsolicited-notifications.c', + 'module-avb/aecp-aem-cmds-resps/cmd-get-set-stream-format.c', + 'module-avb/aecp-aem-cmds-resps/cmd-lock-entity.c', + 'module-avb/aecp-aem-cmds-resps/cmd-get-set-configuration.c', + 'module-avb/aecp-aem-cmds-resps/cmd-lock-entity.c', + 'module-avb/aecp-aem-cmds-resps/cmd-register-unsolicited-notifications.c', + 'module-avb/aecp-aem-cmds-resps/reply-unsol-helpers.c', 'module-avb/es-builder.c', 'module-avb/avdecc.c', + 'module-avb/descriptors.c', 'module-avb/maap.c', 'module-avb/mmrp.c', 'module-avb/mrp.c',
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/acmp.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/acmp.c
Changed
@@ -11,6 +11,8 @@ #include "msrp.h" #include "internal.h" #include "stream.h" +#include "aecp-aem-descriptors.h" +#include "aecp-aem-state.h" static const uint8_t mac6 = AVB_BROADCAST_MAC; @@ -77,12 +79,64 @@ free(p); } +static void pending_destroy(struct acmp *acmp) +{ + struct pending *p, *t; + for (uint32_t list_id = 0; list_id < PENDING_CONTROLLER; list_id++) { + spa_list_for_each_safe(p, t, &acmp->pendinglist_id, link) { + pending_free(acmp, p); + } + } +} + struct msg_info { uint16_t type; const char *name; int (*handle) (struct acmp *acmp, uint64_t now, const void *m, int len); }; +static struct stream *find_stream(struct server *server, enum spa_direction direction, + uint16_t index) +{ + uint16_t type; + struct descriptor *desc; + struct stream *stream; + + switch (direction) { + case SPA_DIRECTION_INPUT: + type = AVB_AEM_DESC_STREAM_INPUT; + break; + case SPA_DIRECTION_OUTPUT: + type = AVB_AEM_DESC_STREAM_OUTPUT; + break; + default: + pw_log_error("Unkown direction\n"); + return NULL; + } + + desc = server_find_descriptor(server, type, index); + if (!desc) { + pw_log_error("Could not find stream type %u index %u\n", + type, index); + return NULL; + } + + switch (direction) { + case SPA_DIRECTION_INPUT: + struct aecp_aem_stream_input_state *stream_in; + stream_in = desc->ptr; + stream = &stream_in->stream; + break; + case SPA_DIRECTION_OUTPUT: + struct aecp_aem_stream_output_state *stream_out; + stream_out = desc->ptr; + stream = &stream_out->stream; + break; + } + + return stream; +} + static int reply_not_supported(struct acmp *acmp, uint8_t type, const void *m, int len) { struct server *server = acmp->server; @@ -120,8 +174,7 @@ return 0; memcpy(buf, m, len); - stream = server_find_stream(server, SPA_DIRECTION_OUTPUT, - reply->talker_unique_id); + stream = find_stream(server, SPA_DIRECTION_OUTPUT, ntohs(reply->talker_unique_id)); if (stream == NULL) { status = AVB_ACMP_STATUS_TALKER_NO_STREAM_INDEX; goto done; @@ -130,7 +183,7 @@ AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE); reply->stream_id = htobe64(stream->id); - stream_activate(stream, now); + stream_activate(stream, ntohs(reply->talker_unique_id), now); memcpy(reply->stream_dest_mac, stream->addr, 6); reply->connection_count = htons(1); @@ -169,14 +222,13 @@ reply->sequence_id = htons(pending->old_sequence_id); AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE); - stream = server_find_stream(server, SPA_DIRECTION_INPUT, - ntohs(reply->listener_unique_id)); + stream = find_stream(server, SPA_DIRECTION_INPUT, ntohs(reply->listener_unique_id)); if (stream == NULL) return 0; stream->peer_id = be64toh(reply->stream_id); memcpy(stream->addr, reply->stream_dest_mac, 6); - stream_activate(stream, now); + stream_activate(stream, ntohs(reply->listener_unique_id), now); res = avb_server_send_packet(server, h->dest, AVB_TSN_ETH, h, pending->size); @@ -199,8 +251,7 @@ return 0; memcpy(buf, m, len); - stream = server_find_stream(server, SPA_DIRECTION_OUTPUT, - reply->talker_unique_id); + stream = find_stream(server, SPA_DIRECTION_OUTPUT, ntohs(reply->talker_unique_id)); if (stream == NULL) { status = AVB_ACMP_STATUS_TALKER_NO_STREAM_INDEX; goto done; @@ -243,8 +294,7 @@ reply->sequence_id = htons(pending->old_sequence_id); AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_RESPONSE); - stream = server_find_stream(server, SPA_DIRECTION_INPUT, - reply->listener_unique_id); + stream = find_stream(server, SPA_DIRECTION_INPUT, ntohs(reply->listener_unique_id)); if (stream == NULL) return 0; @@ -369,6 +419,7 @@ { struct acmp *acmp = data; spa_hook_remove(&acmp->server_listener); + pending_destroy(acmp); free(acmp); }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/adp.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/adp.c
Changed
@@ -156,7 +156,14 @@ static void adp_destroy(void *data) { struct adp *adp = data; + struct entity *e, *t; + spa_hook_remove(&adp->server_listener); + + spa_list_for_each_safe(e, t, &adp->entities, link) { + entity_free(e); + } + free(adp); }
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps
Added
+(directory)
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-available.c
Added
@@ -0,0 +1,68 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <stdbool.h> +#include <stdint.h> + +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../aecp-aem-descriptors.h" +#include "../aecp-aem-types.h" + +#include "cmd-resp-helpers.h" +#include "cmd-available.h" + +/* ENTITY AVAILABLE according to the locking state */ +#define AECP_AEM_AVAIL_ENTITY_ACQUIRED (1<<0) +#define AECP_AEM_AVAIL_ENTITY_LOCKED (1<<1) +#define AECP_AEM_AVAIL_SUBENTITY_ACQUIRED (1<<2) +#define AECP_AEM_AVAIL_SUBENTITY_LOCKED (1<<3) + +int handle_cmd_entity_available_milan_v12(struct aecp *aecp, int64_t now, const void *m, + int len) +{ + uint8_t buf512; + + /* Commnand received specific */ + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + struct avb_ethernet_header *h_reply; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + + /* Reply specific */ + struct avb_packet_aecp_aem *p_reply; + struct avb_packet_aecp_aem_available *avail_reply; + + /* Entity specific */ + struct descriptor *desc; + struct aecp_aem_entity_milan_state *entity_state; + struct aecp_aem_lock_state *lock; + + desc = server_find_descriptor(server, AVB_AEM_DESC_ENTITY, 0); + if (desc == NULL) { + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + } + + entity_state = desc->ptr; + lock = &entity_state->state.lock_state; + + /* Forge the response for the entity that is locking the device */ + memcpy(buf, m, len); + h_reply = (struct avb_ethernet_header *) buf; + p_reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + avail_reply = (struct avb_packet_aecp_aem_available*)p_reply->payload; + + avail_reply->acquired_controller_guid = 0; + + if ((lock->base_info.expire_timeout < now) || !lock->is_locked) { + avail_reply->flags = 0; + } else if (lock->is_locked) { + avail_reply->lock_controller_guid = htobe64(lock->locked_id); + avail_reply->flags = htonl(AECP_AEM_AVAIL_ENTITY_LOCKED); + } + + return reply_success(aecp, buf, len); +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-available.h
Added
@@ -0,0 +1,16 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ +#ifndef __AVB_AECP_AEM_AVAILABLE_H__ +#define __AVB_AECP_AEM_AVAILABLE_H__ + +#include <stdint.h> + +/** + * \brief Milan V1.2 implementation to handle available command. + */ +int handle_cmd_entity_available_milan_v12(struct aecp *aecp, int64_t now, const void *m, + int len); + +#endif //__AVB_AECP_AEM_AVAILABLE_H__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-deregister-unsolicited-notifications.c
Added
@@ -0,0 +1,68 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <pipewire/log.h> +#include <inttypes.h> + +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../aecp-aem-milan.h" + +#include "cmd-deregister-unsolicited-notifications.h" +#include "cmd-resp-helpers.h" + +int handle_cmd_deregister_unsol_notif_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct descriptor *desc; + struct aecp_aem_entity_milan_state *entity_state; + struct aecp_aem_unsol_notification_state *unsol; + uint64_t controller_id = htobe64(p->aecp.controller_guid); + const uint32_t ctrler_max = AECP_AEM_MILAN_MAX_CONTROLLER; + uint16_t index; + + desc = server_find_descriptor(aecp->server, AVB_AEM_DESC_ENTITY, 0); + if (desc == NULL) + return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + + entity_state = (struct aecp_aem_entity_milan_state *) desc->ptr; + unsol = entity_state->unsol_notif_state; + + /** First check if the controller was already registered */ + for (index = 0; index < ctrler_max; index++) { + uint64_t ctrl_eid = unsolindex.ctrler_entity_id; + bool ctrler_reged = unsolindex.is_registered; + + if ((ctrl_eid == controller_id) && ctrler_reged) { + pw_log_debug("controller 0x%"PRIx64", already registered", + controller_id); + return reply_success(aecp, m, len); + } + } + + /** When one slot is in the array is available use it */ + for (index = 0; index < ctrler_max; index++) { + if (!unsolindex.is_registered) { + break; + } + } + + /** Reach the maximum controller allocated */ + if (index == ctrler_max) { + return reply_no_resources(aecp, m, len); + } + + unsolindex.ctrler_entity_id = controller_id; + memcpy(unsolindex.ctrler_mac_addr, h->src, sizeof(h->src)); + unsolindex.is_registered = true; + unsolindex.port_id = 0; + unsolindex.next_seq_id = 0; + + pw_log_info("Unsol registration for 0x%"PRIx64, controller_id); + return reply_success(aecp, m, len); +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-deregister-unsolicited-notifications.h
Added
@@ -0,0 +1,23 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_DEREGISTER_UNSOLICITED_NOTIFICATIONS_H__ +#define __AVB_DEREGISTER_UNSOLICITED_NOTIFICATIONS_H__ + +#include <stdint.h> + +/** + * @brief Command handling will generate the response to the + * received command when deregistering the a controller from + * the list of subscribed controller entities. + * + * @see IEEE1722.1-2021 Section 7.4.38 . + * @see Milan V1.2 Section 5.4.2.22. + */ +int handle_cmd_deregister_unsol_notif_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len); + +#endif // __AVB_DEREGISTER_UNSOLICITED_NOTIFICATIONS_H__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-clock-source.c
Added
@@ -0,0 +1,142 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <stdint.h> +#include <stdbool.h> + +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../descriptors.h" +#include "../aecp-aem-types.h" + +#include "cmd-resp-helpers.h" +#include "reply-unsol-helpers.h" +#include "cmd-get-set-clock-source.h" + +static int reply_invalid_clock_source(struct aecp *aecp, + struct avb_aem_desc_clock_domain *desc, const void *m, int len) +{ + uint8_t buf128; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)buf; + struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem_setget_clock_source *sclk_source; + + memcpy(buf, m, len); + sclk_source = + (struct avb_packet_aecp_aem_setget_clock_source *) p->payload; + + /** The descriptor keep the network endianess */ + sclk_source->clock_source_index = desc->clock_source_index; + + // Reply success with the old value which is the current if it fails. + return reply_success(aecp, buf, len); +} + +static int handle_unsol_set_clock_source(struct aecp *aecp, struct descriptor *desc, + const void *m, int len, uint64_t ctrler_id) +{ + uint8_t buf128; + struct aecp_aem_base_info bi = { 0 }; + int rc; + + memcpy(buf, m, len); + bi.controller_entity_id = htobe64(ctrler_id); + bi.expire_timeout = INT64_MAX; + + rc = reply_unsolicited_notifications(aecp, &bi, buf, len, false); + + return rc; +} + +/** + * \see IEEE 1722.1-2021, 7.4.24. SET_CLOCK_SOURCE Command + * \see Milan V1.2 5.4.2.15 + * \todo verify if this is valid for AVB + */ +int handle_cmd_get_clock_source_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + uint8_t buf128; + struct avb_ethernet_header *h = (struct avb_ethernet_header *) buf; + struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem_setget_clock_source *sclk_source; + struct avb_aem_desc_clock_domain* dclk_domain; + struct descriptor *desc; + uint16_t desc_index; + uint16_t desc_type; + + memcpy(buf, m, len); + sclk_source = + (struct avb_packet_aecp_aem_setget_clock_source *) p->payload; + + desc_index = htons(sclk_source->descriptor_id); + desc_type = htons(sclk_source->descriptor_id); + + desc = server_find_descriptor(aecp->server, desc_type, desc_index); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len); + + dclk_domain = (struct avb_aem_desc_clock_domain*) desc->ptr; + + /** Descriptors always keep the network endianness */ + sclk_source->clock_source_index = dclk_domain->clock_source_index; + + len = sizeof(*p) + sizeof(*sclk_source) + sizeof(*h); + return reply_success(aecp, m, len); +} + +/** + * \see IEEE 1722.1-2021, 7.4.23. SET_CLOCK_SOURCE Command + * \see Milan V1.2 5.4.2.15 + * \todo verify if this is valid for AVB + */ +int handle_cmd_set_clock_source_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + int rc; + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem_setget_clock_source *sclk_source; + /** Information in the packet */ + uint16_t desc_type; + uint16_t desc_index; + uint16_t clock_src_index; + uint64_t ctrlr_id; + + /*Information about the system */ + struct descriptor *desc; + struct avb_aem_desc_clock_domain* dclk_domain; + + sclk_source = + (struct avb_packet_aecp_aem_setget_clock_source *) p->payload; + + desc_type = ntohs(sclk_source->descriptor_type); + desc_index = ntohs(sclk_source->descriptor_id); + clock_src_index = ntohs(sclk_source->clock_source_index); + ctrlr_id = htobe64(p->aecp.controller_guid); + + /** Retrieve the descriptor */ + desc = server_find_descriptor(server, desc_type, desc_index); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len); + + dclk_domain = (struct avb_aem_desc_clock_domain *) desc->ptr; + if (clock_src_index >= dclk_domain->clock_sources_count) { + return reply_invalid_clock_source(aecp, dclk_domain, m, len); + } + + /** Descriptor always keep the network endianness */ + dclk_domain->clock_source_index = htons(clock_src_index); + rc = reply_success(aecp, m, len); + if (rc) { + pw_log_error("Reply failed for set_clock_source\n"); + return -1; + } + + return handle_unsol_set_clock_source(aecp, desc, m, len, ctrlr_id); +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-clock-source.h
Added
@@ -0,0 +1,14 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_AECP_AEM_CMD_GET_SET_CLOCK_SOURCE_H__ +#define __AVB_AECP_AEM_CMD_GET_SET_CLOCK_SOURCE_H__ + +#include <stdint.h> + +int handle_cmd_set_clock_source_milan_v12(struct aecp *aecp, int64_t now, const void *m, int len); +int handle_cmd_get_clock_source_milan_v12(struct aecp *aecp, int64_t now, const void *m, int len); + +#endif /* __AVB_AECP_AEM_CMD_GET_SET_CLOCK_SOURCE_H__ */
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-configuration.c
Added
@@ -0,0 +1,194 @@ +#include <stdint.h> +#include <stdbool.h> + +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../aecp-aem-descriptors.h" +#include "../aecp-aem-types.h" + +#include "cmd-get-set-configuration.h" +#include "cmd-resp-helpers.h" + + +#if 0 +static int handle_unsol_set_configuration_milan_v12(struct aecp *aecp, struct descriptor *desc, + uint64_t ctrler_id) +{ + /* Reply */ + uint8_t buf512; + void *m = buf; + struct avb_aem_desc_entity *entity_desc; + struct avb_ethernet_header *h = m; + struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem_setget_configuration *cfg; + size_t len = sizeof (*h) + sizeof(*p) + sizeof(*cfg); + int rc; + + memset(buf, 0, sizeof(buf)); + entity_desc = (struct avb_aem_desc_entity*) desc->ptr; + cfg = (struct avb_packet_aecp_aem_setget_configuration *) p->payload; + cfg->configuration_index = htons(entity_desc->current_configuration); + p->aecp.target_guid = htobe64(aecp->server->entity_id); + + AVB_PACKET_AEM_SET_COMMAND_TYPE(p, AVB_AECP_AEM_CMD_SET_CONFIGURATION); + rc = reply_unsolicited_notifications_ctrler_id(aecp, ctrler_id, + buf, len, false); + + if (rc) { + pw_log_error("unsol notif failed"); + } + + return rc; +} +#endif + +/** + * Common handler for SET_CONFIGURATION command + * + * Milan v1.2, Sec. 5.4.2.5 + * IEEE 1722.1-2021, Sec. 7.4.7 + */ +int handle_cmd_set_configuration_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + uint8_t buf2048; + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + /* Reply */ + struct avb_ethernet_header *h_reply; + struct avb_packet_aecp_aem *p_reply; + struct avb_packet_aecp_aem_setget_configuration *cfg; + + /* Information about the current entity */ + struct avb_aem_desc_entity *entity_desc; + uint16_t req_cfg_id, cur_cfg_id, cfg_count; + struct descriptor *desc; + int rc; + bool has_failed; + + /* FIXME ACMP: IMPORTANT!!!! find the stream connection information + * whether they are running or not. */ + + /* Milan v1.2, Sec. 5.4.2.5 + * The PAAD-AE shall not accept a SET_CONFIGURATION command if one of + * the Stream Input is bound or one of the Stream Output is streaming. + * In this case, the STREAM_IS_RUNNING error code shall be returned. + * + * If the PAAD-AE is locked by a controller, it shall not accept a + * SET_CONFIGURATION command from a different controller, and it shall + * also not change its current configuration by non-ATDECC + * means (proprietary remote control software, front-panel, ...). + */ + + /** WARNING! Milan forces only one entity */ + desc = server_find_descriptor(server, AVB_AEM_DESC_ENTITY, 0); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + + // TODO maybe avoid copy here + memcpy(buf, m, len); + h_reply = (struct avb_ethernet_header *)buf; + p_reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + + cfg = (struct avb_packet_aecp_aem_setget_configuration*) p_reply->payload; + entity_desc = (struct avb_aem_desc_entity*) desc->ptr; + cur_cfg_id = ntohs(entity_desc->current_configuration); + req_cfg_id = ntohs(cfg->configuration_index); + cfg_count = ntohs(entity_desc->configurations_count); + + if (entity_desc->entity_id != p->aecp.target_guid) { + pw_log_error("Invalid entity id"); + has_failed = true; + /* TODO: req_cfg_id is zero based, cfg_count is not. + * Should be req_cfg_id >= cfg_count */ + } else if (req_cfg_id >= cfg_count) { + pw_log_error("Requested %u, but has max %u id", + req_cfg_id, cfg_count); + has_failed = true; + } else if (req_cfg_id == cur_cfg_id) { + pw_log_warn("requested %u and same current %u id", req_cfg_id, + cur_cfg_id); + has_failed = true; + } else { + entity_desc->current_configuration = cfg->configuration_index; + has_failed = false; + } + + /* + * Always contains the current value, + * that is itcontains the new value if the command succeeds or the old + * value if it fails. + */ + if (has_failed) { + cfg->configuration_index = entity_desc->current_configuration; + } + + rc = reply_success(aecp, buf, len); + if (rc) { + pw_log_error("Reply Failed"); + return rc; + } + +#if 0 + if(!has_failed) { + return handle_unsol_set_configuration_milan_v12(aecp, desc, + tobe64(p->aecp.controller_guid)); + } +#endif + + return 0; +} + + +/** + * Common handler for GET_CONFIGURATION command + * Milan v1.2, Sec. 5.4.2.6 + * IEEE 1722.1-2021, Sec. 7.4.8 + */ +int handle_cmd_get_configuration_common(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + uint8_t buf2048; + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + /* Reply */ + struct avb_ethernet_header *h_reply; + struct avb_packet_aecp_aem *p_reply; + struct avb_packet_aecp_aem_setget_configuration *cfg; + + /* Information about the current entity */ + struct avb_aem_desc_entity *entity_desc; + struct descriptor *desc; + int rc; + + desc = server_find_descriptor(server, AVB_AEM_DESC_ENTITY, 0); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + + memcpy(buf, m, len); + h_reply = (struct avb_ethernet_header *)buf; + p_reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + + cfg = (struct avb_packet_aecp_aem_setget_configuration*) p_reply->payload; + entity_desc = (struct avb_aem_desc_entity*) desc->ptr; + + if (entity_desc->entity_id != p->aecp.target_guid) { + pw_log_error("Invalid entity id"); + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + } + + cfg->configuration_index = entity_desc->current_configuration; + + rc = reply_success(aecp, buf, len); + if (rc) { + pw_log_error("Reply Failed"); + return rc; + } + + return 0; +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-configuration.h
Added
@@ -0,0 +1,10 @@ +#ifndef __AECP_AEM_CMD_GET_SET_CONFIGURATION_H__ +#define __AECP_AEM_CMD_GET_SET_CONFIGURATION_H__ + +int handle_cmd_set_configuration_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len); + +int handle_cmd_get_configuration_common(struct aecp *aecp, int64_t now, + const void *m, int len); + +#endif //__AECP_AEM_CMD_GET_SET_CONFIGURATION_H__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-control.c
Added
@@ -0,0 +1,299 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2026 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2026 Alexandre Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <limits.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> + +#include "spa/utils/defs.h" + +#include "../aecp.h" +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../aecp-aem-descriptors.h" +#include "../aecp-aem-control-value-units.h" + +#include "cmd-get-set-control.h" +#include "cmd-resp-helpers.h" +#include "reply-unsol-helpers.h" + +typedef int (*control_cb_t)(struct aecp *aecp, struct descriptor *desc, + int64_t now, const void *m, int len); + +/** + * \brief copies the aem controls's current value into the aem packet response + */ +static void control_copy_payload(const struct avb_aem_desc_value_format *format, + uint8_t *payload, uint32_t type_sz, size_t format_count) +{ + for (size_t index = 0; index < format_count; index++) { + memcpy(payload, &formatindex.current_value, type_sz); + payload += type_sz; + } +} +/** + * \brief handles unsolicited notification for the set-control + */ +static int send_unsol_control_milan_v12(struct aecp *aecp, + const uint8_t *m, size_t len, uint64_t ctrler_id) +{ + uint8_t unsol_buf512; + struct aecp_aem_base_info info = { 0 }; + int rc = 0; + + memcpy(unsol_buf, m, len); + /* Prepare a template packet */ + info.controller_entity_id = htobe64(ctrler_id); + info.expire_timeout = INT64_MAX; + + rc = reply_unsolicited_notifications(aecp, &info, unsol_buf, len, false); + + return rc; +} + +/** + * \brief answer BAD ARGUMENTS copying the current value of the descriptor + * into the payload + */ +static int reply_control_badargs(struct aecp *aecp, const void *m, int len, + uint32_t type_sz, const struct avb_aem_desc_value_format *format, + size_t count) +{ + // Milan allow bigger than 512 packets, and the response might be bigger + uint8_t buf2048; + struct avb_ethernet_header *h = (void *)buf; + struct avb_packet_aecp_header *reply = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem *p_reply = (void *)reply; + struct avb_packet_aecp_aem_setget_control *ae_reply; + + int pkt_size = sizeof(*h) + sizeof(*p_reply) + (type_sz * count); + + if (pkt_size > AVB_PACKET_MILAN_DEFAULT_MTU) { + pw_log_error("Packet size will be too big, returning only the" + "original one with error"); + return reply_status(aecp, AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, + m, len); + } + + memcpy(buf, m, len); + ae_reply = (struct avb_packet_aecp_aem_setget_control *)p_reply->payload; + + control_copy_payload(format, ae_reply->payload, type_sz, count); + + return reply_status(aecp, AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, + buf, pkt_size); +} + +static int handle_cmd_get_control_identify(struct aecp *aecp, struct descriptor *desc, + int64_t now, const void *m, int len) +{ + uint8_t buf512; + struct avb_ethernet_header *h = (struct avb_ethernet_header *) buf; + struct avb_aem_desc_control *ctrl_desc; + struct avb_aem_desc_value_format *desc_formats; + struct avb_packet_aecp_header *reply = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem *p_reply = (void *)reply; + struct avb_packet_aecp_aem_setget_control *ae_reply; + int pkt_size; + + ctrl_desc = desc->ptr; + desc_formats = ctrl_desc->value_format; + + memcpy(buf, m, len); + ae_reply = (struct avb_packet_aecp_aem_setget_control *)p_reply->payload; + + // Idenfity only has one value element + pkt_size = sizeof(*h) + sizeof(*p_reply)+ CONTROL_LINEAR_UINT8_SIZE; + + control_copy_payload(desc_formats, ae_reply->payload, + CONTROL_LINEAR_UINT8_SIZE, 1); + + return reply_success(aecp, buf, pkt_size); +} + +static int handle_cmd_set_control_identify(struct aecp *aecp, struct descriptor *desc, + int64_t now, const void *m, int len) +{ + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + + struct avb_aem_desc_control *ctrl_desc; + struct avb_aem_desc_value_format *desc_formats; + struct avb_aem_desc_value_format *old_value_format; + struct avb_packet_aecp_aem_setget_control *control; + uint8_t *value_req; + int rc; + + control = (struct avb_packet_aecp_aem_setget_control*)p->payload; + ctrl_desc = desc->ptr; + desc_formats = ctrl_desc->value_format; + old_value_format = desc_formats; + value_req = (uint8_t *)control->payload; + + if (*value_req == desc_formats->current_value) { + return reply_success(aecp, m, len); + } + + if ((*value_req % desc_formats->step)) { + pw_log_error("invalid step increment value\n"); + goto value_error; + } + + if ((*value_req > desc_formats->maximum)) { + pw_log_error("invalid format value above maximum\n"); + goto value_error; + } + + if ((*value_req < desc_formats->minimum)) { + pw_log_error("invalid format value below minimum\n"); + goto value_error; + } + + desc_formats->current_value = *value_req; + rc = reply_success(aecp, m, len); + if (rc) { + pw_log_error("Could not send the set-control response\n"); + return -1; + } + + return send_unsol_control_milan_v12(aecp, m, len, + p->aecp.controller_guid); +value_error: + return reply_control_badargs(aecp, m, len, + CONTROL_LINEAR_UINT8_SIZE, old_value_format, 1); +} + +struct control_get_set_st { + /** The ID correspond to ids in 7.3.5. Control Types Table 7-98 Control Types*/ + uint64_t ctrl_type; + control_cb_t ctrl_setter; + control_cb_t ctrl_getter; +}; + +#define DECL_CTRL_CBS(type_id, setter, getter) \ + { .ctrl_type = type_id, .ctrl_setter = setter, .ctrl_getter = getter } + +static const struct control_get_set_st controls_handlers = +{ + DECL_CTRL_CBS(0x90e0f00000000001, handle_cmd_set_control_identify, + handle_cmd_get_control_identify) +}; + +/** + * \brief Common function to retrieve the setter allowing to the legacy and + * milan avb to be supported. + */ +static control_cb_t get_ctrl_setter_common(const struct control_get_set_st *cbs, + size_t elemnts_cnt, uint64_t ctrl_req_type) +{ + for (size_t pos = 0; pos < elemnts_cnt; pos++) { + if (cbspos.ctrl_type == ctrl_req_type) { + return cbspos.ctrl_setter; + } + } + + return NULL; +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-control.h
Added
@@ -0,0 +1,21 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2026 Alexandre Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2026 Kebag-Logic */ +/* SPDX-License-Identifier: MIT */ +#ifndef __AVB_AECP_AEM_CMD_GET_SET_CONTROL_H__ +#define __AVB_AECP_AEM_CMD_GET_SET_CONTROL_H__ + +#include <stdint.h> + +/** + * \brief set the control according to milan. It involves for now + * only the use of the identify function + */ +int handle_cmd_set_control_milan_v12(struct aecp *aecp, int64_t now, const void *m, int len); + +/** + * \brief retrieve the value the information about the control + */ +int handle_cmd_get_control_milan_v12(struct aecp *aecp, int64_t now, const void *m, int len); + +#endif //__AVB_AECP_AEM_CMD_GET_SET_CONTROL_H__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-name.c
Added
@@ -0,0 +1,182 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <limits.h> +#include <stdbool.h> +#include <inttypes.h> +#include <stdio.h> + +#include "../aecp.h" +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../aecp-aem-descriptors.h" + + +#include "cmd-get-set-name.h" +#include "cmd-resp-helpers.h" +#include "reply-unsol-helpers.h" + +/** + * \brief Different descriptor hold different position for a name + * Therefore different descriptors should be handled diffierently. + */ +static char *get_name_ptr(uint16_t desc_type, void *ptr, uint16_t name_index) +{ + /* Handle Entity specifically due to multiple name fields */ + if (desc_type == AVB_AEM_DESC_ENTITY) { + struct avb_aem_desc_entity *d = ptr; + /* + * IEEE 1722.1-2021 Table 7-38: + * 0: entity_name + * 1: group_name + * 2: serial_number + */ + if (name_index == 0) return d->entity_name; + if (name_index == 1) return d->group_name; + if (name_index == 2) return d->serial_number; + return NULL; + } + + /* Handle Strings descriptor */ + if (desc_type == AVB_AEM_DESC_STRINGS) { + if (name_index > 6) + return NULL; + return (char *)ptr + (name_index * 64); + } + + /* Case when the name index should be forcibly 0 */ + if (name_index != 0) + return NULL; + + /* Exclude descriptors that do not start with object_name */ + switch (desc_type) { + case AVB_AEM_DESC_STREAM_PORT_INPUT: + case AVB_AEM_DESC_STREAM_PORT_OUTPUT: + case AVB_AEM_DESC_EXTERNAL_PORT_INPUT: + case AVB_AEM_DESC_EXTERNAL_PORT_OUTPUT: + case AVB_AEM_DESC_INTERNAL_PORT_INPUT: + case AVB_AEM_DESC_INTERNAL_PORT_OUTPUT: + return NULL; + } + + /* + * Most others (Configuration, Audio Unit, Stream Input/Output, AVB Interface, + * Clock Source, etc.) start with object_name64 at offset 0. + */ + return ptr; +} + +static int send_unsol_name(struct aecp *aecp, + const struct avb_packet_aecp_aem *p, const void *msg, int len) +{ + uint8_t unsol_buf512; + struct aecp_aem_base_info info = { 0 }; + + memcpy(unsol_buf, msg, len); + info.controller_entity_id = htobe64(p->aecp.controller_guid); + info.expire_timeout = INT64_MAX; + + return reply_unsolicited_notifications(aecp, &info, unsol_buf, len, false); +} + +/** + * IEEE 1722.1-2021 7.4.18 GET_NAME + * For now this is not handling UTF characters, only ASCII + */ +int handle_cmd_get_name_common(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + uint8_t buf512; + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + const struct avb_packet_aecp_aem_setget_name *cmd; + struct avb_ethernet_header *h_reply; + struct avb_packet_aecp_aem *p_reply; + struct avb_packet_aecp_aem_setget_name *reply; + struct descriptor *desc; + uint16_t desc_type, desc_id, name_index; + char *name_ptr; + + cmd = (const struct avb_packet_aecp_aem_setget_name *)p->payload; + desc_type = ntohs(cmd->descriptor_type); + desc_id = ntohs(cmd->descriptor_index); + name_index = ntohs(cmd->name_index); + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len); + + name_ptr = get_name_ptr(desc_type, desc->ptr, name_index); + if (name_ptr == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, m, len); + + memcpy(buf, m, len); + h_reply = (struct avb_ethernet_header *)buf; + p_reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + reply = (struct avb_packet_aecp_aem_setget_name *)p_reply->payload; + + /** + * IEEE 1722.1-2021: 7.4.17.1: The name does not contain a trailing NULL + * but if the name is less than 64 bytes in length then it is zero + * padded + */ + memcpy(reply->name, name_ptr, 64); + + return reply_success(aecp, buf, len); +} + + + +/** + * IEEE 1722.1-2021 7.4.17 SET_NAME + * For now this is not handling UTF characters, only ASCII + */ +int handle_cmd_set_name_common(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + const struct avb_packet_aecp_aem_setget_name *cmd; + struct descriptor *desc; + uint16_t desc_type, desc_id, name_index; + char *name_ptr; + int rc; + + cmd = (const struct avb_packet_aecp_aem_setget_name *)p->payload; + desc_type = ntohs(cmd->descriptor_type); + desc_id = ntohs(cmd->descriptor_index); + name_index = ntohs(cmd->name_index); + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len); + + name_ptr = get_name_ptr(desc_type, desc->ptr, name_index); + if (name_ptr == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, m, len); + + /** + * IEEE 1722.1-2021: 7.4.17.1: The name does not contain a trailing NULL + * but if the name is less than 64 bytes in length then it is zero + * padded + */ + memcpy(name_ptr, cmd->name, 64); + + /** TODO: According to the specification, the string should alwasy be 0 + * terminated, the goal would be to check whether a string is UTF-8 and + * that it is correctly zero terminitaed if less than 64 char, if not + * then a simple memcpy is enough */ + + rc = reply_success(aecp, m, len); + if (rc < 0) + return rc; + + return send_unsol_name(aecp, p, m, len); +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-name.h
Added
@@ -0,0 +1,13 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_AECP_AEM_CMD_GET_SET_NAME_H__ +#define __AVB_AECP_AEM_CMD_GET_SET_NAME_H__ + +#include "../aecp.h" + +int handle_cmd_set_name_common(struct aecp *aecp, int64_t now, const void *m, int len); +int handle_cmd_get_name_common(struct aecp *aecp, int64_t now, const void *m, int len); + +#endif /* __AVB_AECP_AEM_CMD_GET_SET_NAME_H__ */
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-sampling-rate.c
Added
@@ -0,0 +1,231 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <limits.h> +#include <stdbool.h> +#include <inttypes.h> + +#include "../aecp.h" +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../aecp-aem-descriptors.h" +#include "../aecp-aem-types.h" + +#include "cmd-resp-helpers.h" +#include "cmd-get-set-sampling-rate.h" + +#include "reply-unsol-helpers.h" + +/** + * \brief Verify if the audio unit supports the sample in the list it has + * \param const uint32_t samplerate: The sample rate command received + * \return true if the samplerate provide is supported by the audio unit + * false otherwise. + */ +static bool valid_sample_rate_audio_unit_compat(const struct avb_aem_desc_audio_unit *au, + const union avb_packet_aecp_aem_pull_frequency *pullfreq) +{ + uint32_t au_pull_freq; + // Descriptors are always are using the network endianees. + uint16_t au_sr_counts = ntohs(au->sampling_rates_count); + const union avb_aem_desc_sampling_rate *sr; + + /* TODO: it's necessary to handle the pull ?!!!*/ + for (uint32_t pos = 0; pos < au_sr_counts; pos++) { + sr = &au->sampling_ratespos; + au_pull_freq = ntohl(sr->pull_frequency); + + if (au_pull_freq == pullfreq->pull_frequency) { + return true; + } + } + + pw_log_error("Unsupported Audio Unit sample rate %d\n", + pullfreq->frequency); + + return false; +} + +/** + * \brief Verify if the samplerate provide is supported by Milan V1.2 + * \param const union pullfreq: union holding all the necessary information. + * the function expects the Host endianess. + * \return true if the samplerate provide is supported by Milan V1.2 + * false otherwise. + */ +static bool valid_sample_rate_milan_v12(const union avb_packet_aecp_aem_pull_frequency *pullfreq) +{ + static const uint32_t list_valid_samplerates = { + 192000, 96000, 48000 + }; + + static const size_t elmt = SPA_N_ELEMENTS(list_valid_samplerates); + + /* TODO: it's necessary to handle the pull ?!!!*/ + for (uint32_t pos = 0; pos < elmt; pos++) { + if (list_valid_sampleratespos == pullfreq->frequency) { + return true; + } + } + + pw_log_error("Unsupported sample rate for Milan V1.2 %d", + pullfreq->frequency); + + return false; +} + +static int send_unsol_get_sampling_rate_milan_v12(struct aecp *aecp, + const uint8_t *m, size_t len, uint64_t ctrler_id) +{ + uint8_t unsol_buf512; + struct aecp_aem_base_info info = { 0 }; + int rc = 0; + + memcpy(unsol_buf, m, len); + /* Prepare a template packet */ + info.controller_entity_id = htobe64(ctrler_id); + info.expire_timeout = INT64_MAX; + + rc = reply_unsolicited_notifications(aecp, &info, unsol_buf, len, false); + return rc; +} + +/** + * \brief Handling errors. The standard forces us to return the current value + * if the entity does not support the requested sampling rate for the audio + * unit. + */ +static int sample_rate_invalid_response(struct aecp *aecp, + const struct avb_aem_desc_audio_unit *unit, const uint8_t *m, size_t len) +{ + uint8_t buf512; + struct avb_ethernet_header *h = (struct avb_ethernet_header *) buf; + struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem_setget_sampling_rate *cmd; + + memcpy(buf, m, len); + cmd = (struct avb_packet_aecp_aem_setget_sampling_rate *)p->payload; + + memcpy(&cmd->sampling_rate, &unit->current_sampling_rate, + sizeof(unit->current_sampling_rate)); + + return reply_status(aecp, AVB_AECP_AEM_STATUS_NOT_SUPPORTED, p, len); +} + +/** + * \brief Milan sampling rate handles. it sets an internal sampling rate to + * the audio unit and not the sampling rate received from the network. + * \sa Milan V1.2 5.4.2.13 SET_SAMPLING_RATE + * \sa IEEE 1722.1-2021 7.4.21 SET_SAMPLING_RATE + */ +int handle_cmd_set_sampling_rate_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + const struct avb_packet_aecp_aem_setget_sampling_rate *cmd; + union avb_packet_aecp_aem_pull_frequency pullfreq; + struct descriptor *desc; + uint16_t desc_type, desc_id; + int rc; + + /* TODO: take care of the multiplier in the sampling rate. + * This function does not take care call the PULL + */ + cmd = (const struct avb_packet_aecp_aem_setget_sampling_rate *)p->payload; + desc_type = ntohs(cmd->descriptor_type); + desc_id = ntohs(cmd->descriptor_id); + + memcpy(&pullfreq, &cmd->sampling_rate, sizeof(pullfreq)); + pullfreq.pull_frequency = ntohl(pullfreq.pull_frequency); + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + + if (desc_type == AVB_AEM_DESC_AUDIO_UNIT) { + struct avb_aem_desc_audio_unit *unit = desc->ptr; + /* TODO check if the STREAM_PORT associated with it supportes + * the SSRC/ASRC bit. + */ + + if (!valid_sample_rate_audio_unit_compat(unit, &pullfreq)) { + return sample_rate_invalid_response(aecp, unit, m, len); + } + + if (!valid_sample_rate_milan_v12(&pullfreq)) { + return sample_rate_invalid_response(aecp, unit, m, len); + } + + memcpy(&unit->current_sampling_rate, &cmd->sampling_rate, + sizeof(unit->current_sampling_rate)); + + } else { + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NOT_IMPLEMENTED, p, len); + } + + rc = reply_success(aecp, m, len); + if (rc) { + return rc; + } + + return send_unsol_get_sampling_rate_milan_v12(aecp, m, len, + p->aecp.controller_guid); +} + + /** + * \brief Setting the sample rate for the common AVB + * \sa IEEE 1722.1-2021 7.4.21 SET_SAMPLING_RATE + */ +int handle_cmd_get_sampling_rate_common(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + /** Choose to have ahte maximum power of 2 size that a buffer can hold */ + /** TODO: In the future this could be optimized to know if the payload + * response fits into the buffer */ + uint8_t buf2048; + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem *reply_p; + struct avb_ethernet_header *h_reply; + struct avb_packet_aecp_aem_setget_sampling_rate *reply_payload; + uint16_t desc_type, desc_id;
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-sampling-rate.h
Added
@@ -0,0 +1,18 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_AECP_AEM_CMD_GET_SET_SAMPLING_RATE_H__ +#define __AVB_AECP_AEM_CMD_GET_SET_SAMPLING_RATE_H__ + + +#include <stdint.h> + +int handle_cmd_set_sampling_rate_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len); + +int handle_cmd_get_sampling_rate_common(struct aecp *aecp, int64_t now, + const void *m, int len); + +#endif /* __AVB_AECP_AEM_CMD_GET_SET_SAMPLING_RATE_H__ */
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-stream-format.c
Added
@@ -0,0 +1,154 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <limits.h> +#include <stdbool.h> +#include <inttypes.h> + +#include "../aecp.h" +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../aecp-aem-descriptors.h" +#include "../aecp-aem-types.h" + +#include "cmd-get-set-stream-format.h" +#include "cmd-resp-helpers.h" +#include "reply-unsol-helpers.h" + +static int send_unsol_stream_format(struct aecp *aecp, void *msg, int len) +{ + struct avb_ethernet_header *h_unsol = (void*)msg; + struct avb_packet_aecp_aem *p_unsol = SPA_PTROFF(h_unsol, sizeof(*h_unsol), void); + struct aecp_aem_base_info info = { 0 }; + + /* Set the originator controller ID to avoid echo */ + info.controller_entity_id = htobe64(p_unsol->aecp.controller_guid); + info.expire_timeout = INT64_MAX; + + return reply_unsolicited_notifications(aecp, &info, msg, len, false); +} + +/** + * \see IEEE 1722.1-2021 7.4.10 + * \see Milan V1.2 5.4.2.8 GET_STREAM_FORMAT + */ +int handle_cmd_get_stream_format_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + uint8_t buf2048; + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + const struct avb_packet_aecp_aem_setget_stream_format *get_cmd; + struct avb_ethernet_header *h_reply; + struct avb_packet_aecp_aem *p_reply; + struct avb_packet_aecp_aem_setget_stream_format *get_reply; + struct descriptor *desc; + struct avb_aem_desc_stream *stream_desc; + uint16_t desc_type, desc_id; + + get_cmd = (const struct avb_packet_aecp_aem_setget_stream_format *)p->payload; + desc_type = ntohs(get_cmd->descriptor_type); + desc_id = ntohs(get_cmd->descriptor_id); + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len); + + if (desc_type != AVB_AEM_DESC_STREAM_INPUT && + desc_type != AVB_AEM_DESC_STREAM_OUTPUT) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, m, len); + + stream_desc = (struct avb_aem_desc_stream *)desc->ptr; + + memcpy(buf, m, len); + h_reply = (struct avb_ethernet_header *)buf; + p_reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + get_reply = (struct avb_packet_aecp_aem_setget_stream_format *)p_reply->payload; + + get_reply->stream_format = stream_desc->current_format; + + return reply_success(aecp, buf, len); +} + +/** + * \see IEEE 1722.1-2021 7.4.9 + * \see Milan V1.2 5.4.2.7 SET_STREAM_FORMAT + */ +int handle_cmd_set_stream_format_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + uint8_t buf2048; + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + const struct avb_packet_aecp_aem_setget_stream_format *set_cmd; + struct avb_ethernet_header *h_reply; + struct avb_packet_aecp_aem *p_reply; + struct avb_packet_aecp_aem_setget_stream_format *set_reply; + struct descriptor *desc; + struct avb_aem_desc_stream *stream_desc; + uint16_t desc_type, desc_id; + uint64_t new_format; + int i; + int rc; + bool found = false; + void *stream; + + set_cmd = (const struct avb_packet_aecp_aem_setget_stream_format *)p->payload; + desc_type = ntohs(set_cmd->descriptor_type); + desc_id = ntohs(set_cmd->descriptor_id); + new_format = set_cmd->stream_format; + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len); + + if (desc_type == AVB_AEM_DESC_STREAM_INPUT) { + struct aecp_aem_stream_input_state *state = + (struct aecp_aem_stream_input_state *)desc->ptr; + stream = &state->stream; + // TODO check if the stream is bound + } else if (desc_type == AVB_AEM_DESC_STREAM_OUTPUT) { + struct aecp_aem_stream_output_state *state = + (struct aecp_aem_stream_output_state *)desc->ptr; + stream = &state->stream; + // TODO check if the stream is STREAM_RUNNING + } else { + return reply_status(aecp, + AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, m, len); + } + + (void)stream; + stream_desc = (struct avb_aem_desc_stream *)desc->ptr; + for (i = 0; i < ntohs(stream_desc->number_of_formats); i++) { + if (stream_desc->stream_formatsi == new_format) { + found = true; + break; + } + } + + + memcpy(buf, m, len); + h_reply = (struct avb_ethernet_header *)buf; + p_reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + set_reply = (struct avb_packet_aecp_aem_setget_stream_format*)p_reply->payload; + + /** If this not found, return the current format */ + if (!found) { + set_reply->stream_format = stream_desc->current_format; + return reply_status(aecp, + AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, m, len); + } + + stream_desc->current_format = new_format; + rc = reply_success(aecp, buf, len); + if (rc < 0) + return rc; + + return send_unsol_stream_format(aecp, buf, len); +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-stream-format.h
Added
@@ -0,0 +1,16 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_AECP_AEM_CMD_GET_SET_STREAM_FORMAT_H__ +#define __AVB_AECP_AEM_CMD_GET_SET_STREAM_FORMAT_H__ + +#include "../aecp-aem.h" + +int handle_cmd_set_stream_format_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len); + +int handle_cmd_get_stream_format_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len); + +#endif //__AVB_AECP_AEM_CMD_GET_SET_STREAM_FORMAT_H__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-lock-entity.c
Added
@@ -0,0 +1,180 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <limits.h> +#include <stdbool.h> +#include <inttypes.h> + +#include "../aecp.h" +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../aecp-aem-descriptors.h" +#include "../aecp-aem-types.h" + +#include "cmd-lock-entity.h" +#include "cmd-resp-helpers.h" + +#include "reply-unsol-helpers.h" + +static int handle_unsol_lock_common(struct aecp *aecp, + struct aecp_aem_lock_state *lock, bool internal) +{ + uint8_t buf512; + void *m = buf; + // struct aecp_aem_regis_unsols + struct avb_ethernet_header *h = m; + struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem_lock *ae; + size_t len = sizeof(*h) + sizeof(*p) + sizeof(*ae); + int rc; + + memset(buf, 0, sizeof(buf)); + ae = (struct avb_packet_aecp_aem_lock*)p->payload; + + if (!lock->is_locked) { + ae->locked_guid = 0; + ae->flags = htonl(AECP_AEM_LOCK_ENTITY_FLAG_UNLOCK); + lock->is_locked = false; + lock->base_info.expire_timeout = LONG_MAX; + } else { + ae->locked_guid = htobe64(lock->locked_id); + ae->flags = 0; + } + + AVB_PACKET_AEM_SET_COMMAND_TYPE(p, AVB_AECP_AEM_CMD_LOCK_ENTITY); + + /** Setup the packet for the unsolicited notification*/ + rc = reply_unsolicited_notifications(aecp, &lock->base_info, buf, len, internal); + if (rc) { + pw_log_error("Unsolicited notification failed \n"); + } + + return rc; +} + +static int handle_unsol_lock_entity_milanv12(struct aecp *aecp, struct descriptor *desc, + uint64_t ctrler_id) +{ + int rc = -1; + struct aecp_aem_entity_milan_state *entity_state; + struct aecp_aem_lock_state *lock; + + entity_state = desc->ptr; + lock = &entity_state->state.lock_state; + lock->base_info.controller_entity_id = ctrler_id; + rc = handle_unsol_lock_common(aecp, lock, false); + + return rc; +} + + +/* LOCK_ENTITY */ +/* Milan v1.2, Sec. 5.4.2.2; IEEE 1722.1-2021, Sec. 7.4.2*/ +int handle_cmd_lock_entity_milan_v12(struct aecp *aecp, int64_t now, const void *m, int len) +{ + uint8_t buf512; + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + const struct avb_packet_aecp_aem_lock *ae; + + struct avb_ethernet_header *h_reply; + struct avb_packet_aecp_aem *p_reply; + struct avb_packet_aecp_aem_lock *ae_reply; + + struct descriptor *desc; + struct aecp_aem_entity_milan_state *entity_state; + struct aecp_aem_lock_state *lock; + uint16_t desc_type, desc_id; + bool reply_locked = false; + uint64_t ctrler_id; + + ae = (const struct avb_packet_aecp_aem_lock*)p->payload; + desc_type = ntohs(ae->descriptor_type); + desc_id = ntohs(ae->descriptor_id); + ctrler_id = htobe64(p->aecp.controller_guid) ; + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + + entity_state = desc->ptr; + lock = &entity_state->state.lock_state; + + if (desc_type != AVB_AEM_DESC_ENTITY || desc_id != 0) { + /* + * Milan v1.2: The PAAD-AE shall not allow locking another descriptor + * than the ENTITY descriptor (NOT_SUPPORTED shall be returned in + * this case). + */ + return reply_not_supported(aecp, m, len); + } + + if (ae->flags & htonl(AECP_AEM_LOCK_ENTITY_FLAG_UNLOCK)) { + /* Entity is not locked */ + if (!lock->is_locked) { + return reply_success(aecp, m, len); + } + + /* Unlocking by the controller which locked */ + if (ctrler_id == lock->locked_id) { + pw_log_debug("Unlocking\n"); + lock->is_locked = false; + lock->locked_id = 0; + } else { + /* Unlocking by a controller that did not lock?*/ + if (ctrler_id != lock->locked_id) { + pw_log_debug("Already unlocked by %" PRIx64, lock->locked_id); + reply_locked = true; + } else { + // TODO: Can this statement be reached? + pw_log_error("Invalid state\n"); + spa_assert(0); + } + } + } else { + // Is it really locked? + if (!lock->is_locked || + lock->base_info.expire_timeout < now) { + + lock->base_info.expire_timeout = now + + AECP_AEM_LOCK_ENTITY_EXPIRE_TIMEOUT_SECOND * SPA_NSEC_PER_SEC; + lock->is_locked = true; + lock->locked_id = ctrler_id; + } else { + // If the lock is taken again by device + if (ctrler_id == lock->locked_id) { + lock->base_info.expire_timeout += + AECP_AEM_LOCK_ENTITY_EXPIRE_TIMEOUT_SECOND; + + lock->is_locked = true; + } else { + // Cannot lock because already locked + pw_log_debug("but the device is locked by %" PRIx64, lock->locked_id); + reply_locked = true; + } + } + } + + /* Forge the response for the entity that is locking the device */ + memcpy(buf, m, len); + h_reply = (struct avb_ethernet_header *) buf; + p_reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + ae_reply = (struct avb_packet_aecp_aem_lock*)p_reply->payload; + ae_reply->locked_guid = htobe64(lock->locked_id); + + if (reply_locked) { + return reply_entity_locked(aecp, buf, len); + } + + if (reply_success(aecp, buf, len)) { + pw_log_debug("Failed sending success reply\n"); + } + + /* Then update the state using the system */ + return handle_unsol_lock_entity_milanv12(aecp, desc, ctrler_id); + +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-lock-entity.h
Added
@@ -0,0 +1,21 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_AECP_AEM_LOCK_H__ +#define __AVB_AECP_AEM_LOCK_H__ + +#define AECP_AEM_LOCK_ENTITY_EXPIRE_TIMEOUT_SECOND (60UL) +#define AECP_AEM_LOCK_ENTITY_FLAG_UNLOCK (1) + +#include <stdint.h> + +/** + * @brief Command handling will generate the response for the lock command + */ +int handle_cmd_lock_entity_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len); + +#endif //__AVB_AECP_AEM_LOCK_H__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-register-unsolicited-notifications.c
Added
@@ -0,0 +1,68 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <pipewire/log.h> +#include <inttypes.h> + +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../aecp-aem-milan.h" + +#include "cmd-register-unsolicited-notifications.h" +#include "cmd-resp-helpers.h" + +int handle_cmd_register_unsol_notif_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct descriptor *desc; + struct aecp_aem_entity_milan_state *entity_state; + struct aecp_aem_unsol_notification_state *unsol; + uint64_t controller_id = htobe64(p->aecp.controller_guid); + const uint32_t ctrler_max = AECP_AEM_MILAN_MAX_CONTROLLER; + uint16_t index; + + desc = server_find_descriptor(aecp->server, AVB_AEM_DESC_ENTITY, 0); + if (desc == NULL) + return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + + entity_state = (struct aecp_aem_entity_milan_state *) desc->ptr; + unsol = entity_state->unsol_notif_state; + + /** First check if the controller was already registered */ + for (index = 0; index < ctrler_max; index++) { + uint64_t ctrl_eid = unsolindex.ctrler_entity_id; + bool ctrler_reged = unsolindex.is_registered; + + if ((ctrl_eid == controller_id) && ctrler_reged) { + pw_log_debug("controller 0x%"PRIx64", already registered", + controller_id); + return reply_success(aecp, m, len); + } + } + + /** When one slot is in the array is available use it */ + for (index = 0; index < ctrler_max; index++) { + if (!unsolindex.is_registered) { + break; + } + } + + /** Reach the maximum controller allocated */ + if (index == ctrler_max) { + return reply_no_resources(aecp, m, len); + } + + unsolindex.ctrler_entity_id = controller_id; + memcpy(unsolindex.ctrler_mac_addr, h->src, sizeof(h->src)); + unsolindex.is_registered = true; + unsolindex.port_id = 0; + unsolindex.next_seq_id = 0; + + pw_log_info("Unsol registration for 0x%"PRIx64, controller_id); + return reply_success(aecp, m, len); +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-register-unsolicited-notifications.h
Added
@@ -0,0 +1,23 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_REGISTER_UNSOLICITED_NOTIFICATIONS_H__ +#define __AVB_REGISTER_UNSOLICITED_NOTIFICATIONS_H__ + +#include <stdint.h> + +/** + * @brief Command handling will generate the response to the + * received command when registering the a controller from + * the list of subscribed entities. + * + * @see IEEE1722.1-2021 Section 7.4.37 . + * @see Milan V1.2 Section 5.4.2.21 . + */ +int handle_cmd_register_unsol_notif_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len); + +#endif // __AVB_REGISTER_UNSOLICITED_NOTIFICATIONS_H__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/cmd-resp-helpers.h
Added
@@ -0,0 +1,111 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_AECP_AEM_HELPERS_H__ +#define __AVB_AECP_AEM_HELPERS_H__ + +#include <stdint.h> +#include <string.h> + +#include <spa/utils/defs.h> +#include <pipewire/log.h> + +#include "../aecp.h" + +static inline int reply_status(struct aecp *aecp, int status, const void *m, int len) +{ + uint8_t buf2048; + struct server *server = aecp->server; + struct avb_ethernet_header *h = (void*)buf; + struct avb_packet_aecp_header *reply = SPA_PTROFF(h, sizeof(*h), void); + + memcpy(buf, m, len); + + pw_log_debug("status 0x%x\n", status); + + AVB_PACKET_AECP_SET_MESSAGE_TYPE(reply, AVB_AECP_MESSAGE_TYPE_AEM_RESPONSE); + AVB_PACKET_AECP_SET_STATUS(reply, status); + + return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, len); +} + +static inline int reply_entity_locked(struct aecp *aecp, const void *m, int len) +{ + pw_log_warn("reply entity locked"); + return reply_status(aecp, AVB_AECP_AEM_STATUS_ENTITY_LOCKED, m, len); +} + +/** \brief The function is be directly hooked with the cmd_info structure */ +static inline int direct_reply_entiy_locked(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + return reply_entity_locked(aecp, m, len); +} + +static inline int reply_not_implemented(struct aecp *aecp, const void *m, int len) +{ + pw_log_warn("reply not implementing"); + return reply_status(aecp, AVB_AECP_AEM_STATUS_NOT_IMPLEMENTED, m, len); +} + +/** \brief The function is be directly hooked with the cmd_info structure */ +static inline int direct_reply_not_implemented(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + return reply_not_implemented(aecp, m, len); +} + +static inline int reply_not_supported(struct aecp *aecp, const void *m, int len) +{ + pw_log_warn("reply not supported"); + return reply_status(aecp, AVB_AECP_AEM_STATUS_NOT_SUPPORTED, m, len); +} + +/** \brief The function is be directly hooked with the cmd_info structure */ +static inline int direct_reply_not_supported(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + return reply_not_supported(aecp, m, len); +} + +static inline int reply_no_resources(struct aecp *aecp, const void *m, int len) +{ + pw_log_warn("reply no resources"); + return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_RESOURCES, m, len); +} + +/** \brief The function is be directly hooked with the cmd_info structure */ +static inline int direct_reply_no_resources(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + return reply_no_resources(aecp, m, len); +} + +static inline int reply_bad_arguments(struct aecp *aecp, const void *m, int len) +{ + pw_log_warn("reply bad arguments"); + return reply_status(aecp, AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, m, len); +} + +/** \brief The function is be directly hooked with the cmd_info structure */ +static inline int direct_reply_bad_arguments(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + return reply_bad_arguments(aecp, m, len); +} + +static inline int reply_success(struct aecp *aecp, const void *m, int len) +{ + return reply_status(aecp, AVB_AECP_AEM_STATUS_SUCCESS, m, len); +} + +/** \brief The function is be directly hooked with the cmd_info structure */ +static inline int direct_reply_success(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + return reply_success(aecp, m, len); +} + +#endif //__AVB_AECP_AEM_HELPERS_H__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/reply-unsol-helpers.c
Added
@@ -0,0 +1,157 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../internal.h" + +#include "reply-unsol-helpers.h" + +#include <pipewire/log.h> + +#define AECP_UNSOL_BUFFER_SIZE (128U) +#define AECP_AEM_MIN_PACKET_LENGTH (60U) + +static int reply_unsol_get_specific_info(struct aecp *aecp, struct descriptor *desc, + struct aecp_aem_unsol_notification_state **unsol_state, size_t *count) +{ + enum avb_mode mode = aecp->server->avb_mode; + + switch (mode) { + case AVB_MODE_LEGACY: + pw_log_error("Not implemented\n"); + break; + case AVB_MODE_MILAN_V12: + struct aecp_aem_entity_milan_state *entity_state; + + entity_state = desc->ptr; + *unsol_state = entity_state->unsol_notif_state; + *count = AECP_AEM_MILAN_MAX_CONTROLLER; + + return 0; + default: + pw_log_error("Invalid avb_mode %d\n", mode); + break; + } + + return -1; +} + +static int reply_unsol_send(struct aecp *aecp, uint64_t controller_id, + void *packet, size_t len, bool internal) +{ + size_t ctrler_index; + int rc = 0; + struct avb_ethernet_header *h; + struct avb_packet_aecp_aem *p; + struct aecp_aem_unsol_notification_state *unsol_state; + struct descriptor *desc; + size_t max_ctrler; + + desc = server_find_descriptor(aecp->server, AVB_AEM_DESC_ENTITY, 0); + if (desc == NULL) { + pw_log_error("Could not find the ENTITY descriptor 0"); + return -EINVAL; + } + + rc = reply_unsol_get_specific_info(aecp, desc, &unsol_state, &max_ctrler); + if (rc) { + return -EINVAL; + } + + h = (struct avb_ethernet_header*) packet; + p = SPA_PTROFF(h, sizeof(*h), void); + + /* Loop through all the unsol entities. */ + for (ctrler_index = 0; ctrler_index < max_ctrler; ctrler_index++) + { + if (!unsol_statectrler_index.is_registered) { + pw_log_debug("Not registered %zu", ctrler_index); + continue; + } + + if ((controller_id == unsol_statectrler_index.ctrler_entity_id) + && !internal) { + /* Do not send unsolicited if that the one triggering + changes this is not a timeout. */ + pw_log_debug("Do not send twice of %"PRIx64" %"PRIx64, + controller_id, + unsol_statectrler_index.ctrler_entity_id); + continue; + } + + p->aecp.controller_guid = + htobe64(unsol_statectrler_index.ctrler_entity_id); + + p->aecp.sequence_id = htons(unsol_statectrler_index.next_seq_id); + + unsol_statectrler_index.next_seq_id++; + rc = avb_server_send_packet(aecp->server, + unsol_statectrler_index.ctrler_mac_addr, + AVB_TSN_ETH, packet, len); + + if (rc) { + pw_log_error("while sending packet to %"PRIx64, + unsol_statectrler_index.ctrler_entity_id); + return rc; + } + } + + return rc; +} + +static void reply_unsol_notifications_prepare(struct aecp *aecp, + uint8_t *buf, void *packet, size_t len) +{ + struct avb_ethernet_header *h; + struct avb_packet_aecp_aem *p; + size_t ctrl_data_length; + + /* Here the value of 12 is the delta between the target_entity_id and + start of the AECP message specific data. */ + ctrl_data_length = len - (sizeof(*h) + sizeof(*p)) + + AVB_PACKET_CONTROL_DATA_OFFSET; + + h = (struct avb_ethernet_header*) packet; + p = SPA_PTROFF(h, sizeof(*h), void); + + p->aecp.hdr.subtype = AVB_SUBTYPE_AECP; + AVB_PACKET_AECP_SET_MESSAGE_TYPE(&p->aecp, AVB_AECP_MESSAGE_TYPE_AEM_RESPONSE); + AVB_PACKET_SET_VERSION(&p->aecp.hdr, 0); + AVB_PACKET_AECP_SET_STATUS(&p->aecp, AVB_AECP_AEM_STATUS_SUCCESS); + AVB_PACKET_SET_LENGTH(&p->aecp.hdr, ctrl_data_length); + p->u = 1; + p->aecp.target_guid = htobe64(aecp->server->entity_id); +} + +/** + * @brief Sends unsolicited notifications. Does not sends information unless to + * the controller id unless an internal change has happenned (timeout, action + * etc) + * + */ +int reply_unsolicited_notifications(struct aecp *aecp, + struct aecp_aem_base_info *b_state, void *packet, size_t len, + bool internal) +{ + uint8_t bufAECP_UNSOL_BUFFER_SIZE; + /* Make sure to get the actual original len if the packet + * re-adjusted to comply with the 60 bytes min packet size. + */ + size_t original_len = len; + + if (len < AECP_AEM_MIN_PACKET_LENGTH) { + memset(buf, 0, AECP_AEM_MIN_PACKET_LENGTH); + memcpy(buf, packet, len); + len = AECP_AEM_MIN_PACKET_LENGTH; + packet = buf; + } + + /** Retrieve the entity descriptor */ + reply_unsol_notifications_prepare(aecp, buf, packet, original_len); + + return reply_unsol_send(aecp, b_state->controller_entity_id, packet, len, + internal); +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-cmds-resps/reply-unsol-helpers.h
Added
@@ -0,0 +1,23 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_REPLY_UNSOL_HELPER_H__ +#define __AVB_REPLY_UNSOL_HELPER_H__ + +#include <stdint.h> +#include <pipewire/log.h> + +/** + * @brief Sends unsolicited notifications. Does not sends information unless to + * the controller id unless an internal change has happenned (timeout, action + * etc) + * @see Milan V1.2 Section 5.4.2.21 + * @see IEEE 1722.1-2021 7.5.2 ( Unsolicited Notifications ) + */ +int reply_unsolicited_notifications(struct aecp *aecp, + struct aecp_aem_base_info *b_state, void *packet, size_t len, + bool internal); + +#endif // __AVB_REPLY_UNSOL_HELPER_H__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-control-value-units.h
Added
@@ -0,0 +1,8 @@ +#ifndef __AVB_AECP_AEM_CONTROL_VALUE_UNITS__ +#define __AVB_AECP_AEM_CONTROL_VALUE_UNITS__ + +#define CONTROL_LINEAR_UINT8_SIZE 1 +#define CONTROL_LINEAR_UINT8_TYPE (uint8_t) +#define CONTROL_LINEAR_UINT_MAX_COUNT (44U) + +#endif //__AVB_AECP_AEM_CONTROL_VALUE_UNITS__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-controls.h
Added
@@ -0,0 +1,171 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AECP_AEM_CONTROLS_H__ +#define __AECP_AEM_CONTROLS_H__ + +// TODO, When all the AVB needs to be supported then addition needs to be don here +/* IEEE 1722.1-2021, Table 7-121 - Value Types*/ +#define AECP_AEM_CTRL_LINEAR_INT8 0x0000 +#define AECP_AEM_CTRL_LINEAR_UINT8 0x0001 +#define AECP_AEM_CTRL_LINEAR_INT16 0x0002 +#define AECP_AEM_CTRL_LINEAR_UINT16 0x0003 +#define AECP_AEM_CTRL_LINEAR_INT32 0x0004 +#define AECP_AEM_CTRL_LINEAR_UINT32 0x0005 +#define AECP_AEM_CTRL_LINEAR_INT64 0x0006 +#define AECP_AEM_CTRL_LINEAR_UINT64 0x0007 +#define AECP_AEM_CTRL_LINEAR_FLOAT 0x0008 +#define AECP_AEM_CTRL_LINEAR_DOUBLE 0x0009 + +#define AECP_AEM_CTRL_SELECTOR_INT8 0x000a +#define AECP_AEM_CTRL_SELECTOR_UINT8 0x000b +#define AECP_AEM_CTRL_SELECTOR_INT16 0x000c +#define AECP_AEM_CTRL_SELECTOR_UINT16 0x000d +#define AECP_AEM_CTRL_SELECTOR_INT32 0x000e +#define AECP_AEM_CTRL_SELECTOR_UINT32 0x000f +#define AECP_AEM_CTRL_SELECTOR_INT64 0x0010 +#define AECP_AEM_CTRL_SELECTOR_UINT64 0x0011 +#define AECP_AEM_CTRL_SELECTOR_FLOAT 0x0012 +#define AECP_AEM_CTRL_SELECTOR_DOUBLE 0x0013 +#define AECP_AEM_CTRL_SELECTOR_STRING 0x0014 + +#define AEPC_AEM_CTRL_ARRAY_INT8 0x0015 +#define AEPC_AEM_CTRL_ARRAY_UINT8 0x0016 +#define AEPC_AEM_CTRL_ARRAY_INT16 0x0017 +#define AEPC_AEM_CTRL_ARRAY_UINT16 0x0018 +#define AEPC_AEM_CTRL_ARRAY_INT32 0x0019 +#define AEPC_AEM_CTRL_ARRAY_UINT32 0x001a +#define AEPC_AEM_CTRL_ARRAY_INT64 0x001b +#define AEPC_AEM_CTRL_ARRAY_UINT64 0x001c +#define AEPC_AEM_CTRL_ARRAY_FLOAT 0x001d +#define AEPC_AEM_CTRL_ARRAY_DOUBLE 0x001e + +#define AECP_AEM_CTRL_UTF8 0x001f +#define AECP_AEM_CTRL_BODE_PLOT 0x0020 +#define AECP_AEM_CTRL_SMPTE_TIME 0x0021 +#define AECP_AEM_CTRL_SAMPLE_RATE 0x0022 +#define AECP_AEM_CTRL_GPTP_TIME 0x0023 + +#define AECP_AEM_CTRL_CTRL_VENDOR 0x3ffe + +/* Definition of the UNIT codes */ +/* IEEE 1722.1-2021, Table 7-75 - Codes for Unitless quantities*/ +#define AECP_AEM_CTRL_UNIT_CODE_UNITLESS (0) +#define AECP_AEM_CTRL_UNIT_CODE_COUNT (1) +#define AECP_AEM_CTRL_UNIT_CODE_PERCENT (2) +#define AECP_AEM_CTRL_UNIT_CODE_FSTOP (3) + + + +#define AECP_AEM_CTRL_FORMAT_VENDOR (0) +#define AECP_AEM_CTRL_FORMAT_AVDECC (1) + +/* IEEE 1722.1-2021, Sec. 7.3.5 Control Types */ +#define AEM_CTRL_TYPE_ENABLE 0x90E0F00000000000ULL +#define AEM_CTRL_TYPE_IDENTIFY 0x90E0F00000000001ULL +#define AEM_CTRL_TYPE_MUTE 0x90E0F00000000002ULL +#define AEM_CTRL_TYPE_INVERT 0x90E0F00000000003ULL +#define AEM_CTRL_TYPE_GAIN 0x90E0F00000000004ULL +#define AEM_CTRL_TYPE_ATTENUATE 0x90E0F00000000005ULL +#define AEM_CTRL_TYPE_DELAY 0x90E0F00000000006ULL +#define AEM_CTRL_TYPE_SRC_MODE 0x90E0F00000000007ULL +#define AEM_CTRL_TYPE_SNAPSHOT 0x90E0F00000000008ULL +#define AEM_CTRL_TYPE_POW_LINE_FREQ 0x90E0F00000000009ULL +#define AEM_CTRL_TYPE_POWER_STATUS 0x90E0F0000000000AULL +#define AEM_CTRL_TYPE_FAN_STATUS 0x90E0F0000000000BULL +#define AEM_CTRL_TYPE_TEMPERATURE 0x90E0F0000000000CULL +#define AEM_CTRL_TYPE_ALTITUDE 0x90E0F0000000000DULL +#define AEM_CTRL_TYPE_ABSOLUTE_HUMIDITY 0x90E0F0000000000EULL +#define AEM_CTRL_TYPE_RELATIVE_HUMIDITY 0x90E0F0000000000FULL +#define AEM_CTRL_TYPE_ORIENTATION 0x90E0F00000000010ULL +#define AEM_CTRL_TYPE_VELOCITY 0x90E0F00000000011ULL +#define AEM_CTRL_TYPE_ACCELERATION 0x90E0F00000000012ULL +#define AEM_CTRL_TYPE_FILTER_RESPONSE 0x90E0F00000000013ULL +#define AEM_CTRL_TYPE_BAROMETRIC_PRESSURE 0x90E0F00000000014ULL +#define AEM_CTRL_TYPE_MANUFACTURER_URL 0x90E0F00000000015ULL +#define AEM_CTRL_TYPE_ENTITY_URL 0x90E0F00000000016ULL +#define AEM_CTRL_TYPE_CONFIGURATION_URL 0x90E0F00000000017ULL +#define AEM_CTRL_TYPE_GENERIC_URL 0x90E0F00000000018ULL +#define AEM_CTRL_TYPE_FAULT 0x90E0F00000000019ULL +#define AEM_CTRL_TYPE_CONTROLLER_TARGET_ENTITY 0x90E0F0000000001AULL +#define AEM_CTRL_TYPE_CONTROLLER_TARGET_OBJECT 0x90E0F0000000001BULL +#define AEM_CTRL_TYPE_LATENCY_COMPENSATION 0x90E0F0000000001CULL + +#define AEM_CTRL_TYPE_PANPOT 0x90E0F00000010000ULL +#define AEM_CTRL_TYPE_PHANTOM 0x90E0F00000010001ULL +#define AEM_CTRL_TYPE_AUDIO_SCALE 0x90E0F00000010002ULL +#define AEM_CTRL_TYPE_AUDIO_METERS 0x90E0F00000010003ULL +#define AEM_CTRL_TYPE_AUDIO_SPECTRUM 0x90E0F00000010004ULL + +#define AEM_CTRL_TYPE_SCANNING_MODE 0x90E0F00000020000ULL +#define AEM_CTRL_TYPE_AUTO_EXP_MODE 0x90E0F00000020001ULL +#define AEM_CTRL_TYPE_AUTO_EXP_PRIO 0x90E0F00000020002ULL +#define AEM_CTRL_TYPE_EXP_TIME 0x90E0F00000020003ULL +#define AEM_CTRL_TYPE_FOCUS 0x90E0F00000020004ULL +#define AEM_CTRL_TYPE_FOCUS_AUTO 0x90E0F00000020005ULL +#define AEM_CTRL_TYPE_IRIS 0x90E0F00000020006ULL +#define AEM_CTRL_TYPE_ZOOM 0x90E0F00000020007ULL +#define AEM_CTRL_TYPE_PRIVACY 0x90E0F00000020008ULL +#define AEM_CTRL_TYPE_BACKLIGHT 0x90E0F00000020009ULL +#define AEM_CTRL_TYPE_BRIGHTNESS 0x90E0F0000002000AULL +#define AEM_CTRL_TYPE_CONTRAST 0x90E0F0000002000BULL +#define AEM_CTRL_TYPE_HUE 0x90E0F0000002000CULL +#define AEM_CTRL_TYPE_SATURATION 0x90E0F0000002000DULL +#define AEM_CTRL_TYPE_SHARPNESS 0x90E0F0000002000EULL +#define AEM_CTRL_TYPE_GAMMA 0x90E0F0000002000FULL +#define AEM_CTRL_TYPE_WHITE_BAL_TEMP 0x90E0F00000020010ULL +#define AEM_CTRL_TYPE_WHITE_BAL_TEMP_AUTO 0x90E0F00000020011ULL +#define AEM_CTRL_TYPE_WHITE_BAL_COMP 0x90E0F00000020012ULL +#define AEM_CTRL_TYPE_WHITE_BAL_COMP_AUTO 0x90E0F00000020013ULL +#define AEM_CTRL_TYPE_DIGITAL_ZOOM 0x90E0F00000020014ULL + +#define AEM_CTRL_TYPE_MEDIA_PLAYLIST 0x90E0F00000030000ULL +#define AEM_CTRL_TYPE_MEDIA_PLAYLIST_NAME 0x90E0F00000030001ULL +#define AEM_CTRL_TYPE_MEDIA_DISK 0x90E0F00000030002ULL +#define AEM_CTRL_TYPE_MEIDA_DISK_NAME 0x90E0F00000030003ULL +#define AEM_CTRL_TYPE_TRACK 0x90E0F00000030004ULL +#define AEM_CTRL_TYPE_TRACK_NAME 0x90E0F00000030005ULL +#define AEM_CTRL_TYPE_SPEED 0x90E0F00000030006ULL +#define AEM_CTRL_TYPE_MEDIA_SAMPLE_POSITION 0x90E0F00000030007ULL +#define AEM_CTRL_TYPE_MEDIA_PLAYBACK_TRANSPORT 0x90E0F00000030008ULL +#define AEM_CTRL_TYPE_MEDIA_RECORD_TRANSPORT 0x90E0F00000030009ULL + +#define AEM_CTRL_TYPE_FREQUENCY 0x90E0F00000040000ULL +#define AEM_CTRL_TYPE_MODULATION 0x90E0F00000040001ULL +#define AEM_CTRL_TYPE_POLARIZATION 0x90E0F00000040002ULL + +#define AEM_CTRL_TYPE_BAUD_RATE 0x90E0F00000050000ULL +#define AEM_CTRL_TYPE_BIT_WIDTH 0x90E0F00000050001ULL +#define AEM_CTRL_TYPE_PARITY 0x90E0F00000050002ULL +#define AEM_CTRL_TYPE_STOP_BITS 0x90E0F00000050003ULL + +#define AEM_CTRL_TYPE_INTERFACE_OPERATIONAL 0x90E0F00000060000ULL +#define AEM_CTRL_TYPE_INTERFACE_MEDIA_OPTIONS 0x90E0F00000060001ULL +#define AEM_CTRL_TYPE_INTERFACE_MEDIA_STATUS 0x90E0F00000060002ULL +#define AEM_CTRL_TYPE_INTERFACE_NETWORK_NAME 0x90E0F00000060003ULL +#define AEM_CTRL_TYPE_FQTSS_DELTA_BANDWIDTH 0x90E0F00000060004ULL +#define AEM_CTRL_TYPE_FQTSS_ADMIN_IDLE_SLOPE 0x90E0F00000060005ULL +#define AEM_CTRL_TYPE_FQTSS_OPER_IDLE_SLOPE 0x90E0F00000060006ULL +#define AEM_CTRL_TYPE_FQTSS_PORT_TRANSMIT_RATE 0x90E0F00000060007ULL +#define AEM_CTRL_TYPE_FQTSS_CLASS_MEASUREMENT_INTERVAL 0x90E0F00000060008ULL +#define AEM_CTRL_TYPE_FQTSS_LOCK_CLASS_BANDWIDTH 0x90E0F00000060009ULL + +/* Identify +* IEEE 1722.1, Sec. 7.3.5.2 - Identify Control (IDENTIFY) +* Milan v1.2, Sec. 5.4.5.4 - Identification notification +*/ +#define AECP_AEM_CTRL_IDENTIFY_UNIT_MULTIPLY 0 +#define AECP_AEM_CTRL_IDENTIFY_UNIT_CODE AECP_AEM_CTRL_UNIT_CODE_UNITLESS +#define AECP_AEM_CTRL_IDENTIFY_STEP (255) +#define AECP_AEM_CTRL_IDENTIFY_MINIMUM (0) +#define AECP_AEM_CTRL_IDENTIFY_MAXIMUM (255) + + +#define BASE_CTRL_TYPE_MAC { 0x90, 0xe0, 0xf0, 0x01, 0x00, 0x00 }; +// 1722.1-2021, Annex B Table B.1 +#define BASE_CTRL_IDENTIFY_MAC { 0x90, 0xe0, 0xf0, 0x00, 0x00, 0x01 }; + +#endif //__AECP_AEM_CONTROLS_H__
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/aecp-aem-descriptors.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-descriptors.h
Changed
@@ -6,8 +6,11 @@ #ifndef AVB_AECP_AEM_DESCRIPTORS_H #define AVB_AECP_AEM_DESCRIPTORS_H -#include "internal.h" +#include <stdint.h> +/* +* IEEE 1722.1-2021, Table 7-1 - Descriptor Types +*/ #define AVB_AEM_DESC_ENTITY 0x0000 #define AVB_AEM_DESC_CONFIGURATION 0x0001 #define AVB_AEM_DESC_AUDIO_UNIT 0x0002 @@ -50,6 +53,13 @@ #define AVB_AEM_DESC_LAST_RESERVED_17221 0x0029 #define AVB_AEM_DESC_INVALID 0xffff +/* IEEE 1722.1-2021, Table 7-24 - Port Flags */ +// No flag is not defined in table +#define AVB_AEM_PORT_FLAG_NO_FLAG 0x0000 +#define AVB_AEM_PORT_FLAG_CLOCK_SYNC_SOURCE 0x0001 +#define AVB_AEM_PORT_FLAG_ASYNC_SAMPLE_RATE_CONV 0x0002 +#define AVB_AEM_PORT_FLAG_SYNC_SAMPLE_RATE_CONV 0x0004 + struct avb_aem_desc_entity { uint64_t entity_id; uint64_t entity_model_id; @@ -84,7 +94,12 @@ struct avb_aem_desc_descriptor_count descriptor_counts0; } __attribute__ ((__packed__)); -struct avb_aem_desc_sampling_rate { + +union avb_aem_desc_sampling_rate { + struct { + uint32_t frequency:29; + uint32_t pull:3; + }; uint32_t pull_frequency; } __attribute__ ((__packed__)); @@ -124,10 +139,46 @@ uint16_t base_transcoder; uint16_t number_of_control_blocks; uint16_t base_control_block; - uint32_t current_sampling_rate; + union avb_aem_desc_sampling_rate current_sampling_rate; uint16_t sampling_rates_offset; uint16_t sampling_rates_count; - struct avb_aem_desc_sampling_rate sampling_rates0; + union avb_aem_desc_sampling_rate sampling_rates0; +} __attribute__ ((__packed__)); + +/* IEEE 1722.1-2021, Table 7-28 - AUDIO_CLUSTER format values */ +#define AVB_AEM_AUDIO_CLUSTER_TYPE_IEC60958 0x00 +#define AVB_AEM_AUDIO_CLUSTER_TYPE_MBLA 0x40 +#define AVB_AEM_AUDIO_CLUSTER_TYPE_MIDI 0x80 +#define AVB_AEM_AUDIO_CLUSTER_TYPE_SMPTE 0x88 + +struct avb_aem_desc_audio_cluster { + char object_name64; + uint16_t localized_description; + + uint16_t signal_type; + uint16_t signal_index; + uint16_t signal_output; + uint32_t path_latency; + uint32_t block_latency; + uint16_t channel_count; + uint8_t format; + uint8_t aes3_data_type_ref; + uint16_t aes3_data_type; +} __attribute__ ((__packed__)); + +#define AVB_AEM_AUDIO_MAPPING_FORMAT_OFFSET (8) + +struct avb_aem_audio_mapping_format { + uint16_t mapping_stream_index; + uint16_t mapping_stream_channel; + uint16_t mapping_cluster_offset; + uint16_t mapping_cluster_channel; +} __attribute__ ((__packed__)); + +struct avb_aem_desc_audio_map { + uint16_t mapping_offset; + uint16_t number_of_mappings; + struct avb_aem_audio_mapping_format mappings0; } __attribute__ ((__packed__)); #define AVB_AEM_DESC_STREAM_FLAG_SYNC_SOURCE (1u<<0) @@ -200,6 +251,15 @@ uint16_t clock_source_location_index; } __attribute__ ((__packed__)); +struct avb_aem_desc_clock_domain { + char object_name64; + uint16_t localized_description; + uint16_t clock_source_index; + uint16_t descriptor_counts_offset; + uint16_t clock_sources_count; + uint16_t clock_sources0; +} __attribute__ ((__packed__)); + struct avb_aem_desc_locale { char locale_identifier64; uint16_t number_of_strings; @@ -227,4 +287,34 @@ uint16_t base_map; } __attribute__ ((__packed__)); +struct avb_aem_desc_value_format { + uint8_t minimum; + uint8_t maximum; + uint8_t step; + uint8_t default_value; + uint8_t current_value; + uint16_t unit; + uint16_t localized_description; +} __attribute__ ((__packed__)); + +struct avb_aem_desc_control { + char object_name64; + uint16_t localized_description; + + uint32_t block_latency; + uint32_t control_latency; + uint16_t control_domain; + uint16_t control_value_type; + uint64_t control_type; + uint32_t reset_time; + + uint16_t descriptor_counts_offset; + + uint16_t number_of_values; + uint16_t signal_type; + uint16_t signal_index; + uint16_t signal_output; + struct avb_aem_desc_value_format value_format0; +} __attribute__ ((__packed__)); + #endif /* AVB_AECP_AEM_DESCRIPTORS_H */
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-milan.h
Added
@@ -0,0 +1,6 @@ +#ifndef __AVB_AECP_AEM_MILAN_H__ +#define __AVB_AECP_AEM_MILAN_H__ + +#define AECP_AEM_MILAN_MAX_CONTROLLER 16 + +#endif //__AVB_AECP_AEM_MILAN_H__
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-state.h
Added
@@ -0,0 +1,180 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef AVB_AECP_AEM_STATE_H +#define AVB_AECP_AEM_STATE_H + +#include <stdint.h> +#include <stdbool.h> + +#include "aecp-aem-descriptors.h" +#include "aecp-aem-milan.h" +#include "stream.h" + +/** + * The way structure are organised in a "derived" manner. + * Each of the state structure must directly "castable" into the descriptor + * that the state variable relies upon. + * + * For instance, if a stream_input descriptor is created, the state structure + * stream_input_state needs to be created as follow: + * + * struct stream_input_state { + * struct stream_input_desc ... + * ... + * This way it's possible to get directly from the AEM command the descriptor + * and the state without having to create a mechanism for this. + */ + +/** + * \brief The base information would be required + * for descriptor that needs to udpate for unsollictied + * notificaction. + */ +struct aecp_aem_state_base { + /** + * Originator of the control + * This is needed so the unsoolictied notification does not send back SUCCESS + * to the originator of of the unsolicited notification + */ + uint64_t controller_entity_id; + + /** + * To avoid sending on every change for unsol notifications, only once a + * second + */ + int64_t last_update; + + /** timeout absolute time*/ + int64_t expire_timeout; +}; + +/** + * \brief the structure keeps track of the registered controller entities + */ +struct aecp_aem_unsol_notification_state { + /** + * The controller is that is locking this system + */ + uint64_t ctrler_entity_id; + + /** + * mac Address of the controller + */ + uint8_t ctrler_mac_addr6; + + /** + * Port where the registeration originated from + */ + uint8_t port_id; + + /*** + * The sequence ID of the next unsolicited notification + */ + + uint16_t next_seq_id; + /** + * Actual value of the lock, get removed when unregistere or expired. + */ + bool is_registered; + +}; + +struct aecp_aem_base_info { + /** Originator of the control + * This is needed so the unsoolictied notification does not send back SUCCESS + * to the originator of of the unsolicited notification */ + uint64_t controller_entity_id; + + /** + * To avoid sending on every change for unsol notifications, only once a + * a second + * */ + int64_t last_update; + + /** timeout absolute time*/ + int64_t expire_timeout; +}; + +struct aecp_aem_lock_state { + struct aecp_aem_base_info base_info; + /** + * the entity id that is locking this system + */ + uint64_t locked_id; + + /** + * actual value of the lock + */ + bool is_locked; +}; + +/** + * \brief the generic entity state common for all flavor of AVB + */ +struct aecp_aem_entity_state { + struct avb_aem_desc_entity desc; + struct aecp_aem_lock_state lock_state; +}; + +/** + * \brief Milan implementation of the entity + */ +struct aecp_aem_entity_milan_state { + struct aecp_aem_entity_state state; + struct aecp_aem_unsol_notification_state unsol_notif_stateAECP_AEM_MILAN_MAX_CONTROLLER; +}; + +/** + * \brief Legacy AVB implementation of the entity + */ +struct aecp_aem_entity_legacy_avb_state { + struct aecp_aem_entity_state state; +}; + +/** + * \brief The stream inputs are specified as in the IEEE-1722.1-2021 + * Table 7-156 + */ +struct aecp_aem_stream_input_counters { + struct aecp_aem_state_base base_state; + + uint32_t media_locked; + uint32_t media_unlocked; + uint32_t stream_interrupted; + uint32_t seq_mistmatch; + uint32_t media_reset; + /** Timestamp Uncertain */ + uint32_t tu; + uint32_t unsupported_format; + uint32_t late_timestamp; + uint32_t early_timestamp; + uint32_t frame_rx; +}; + +struct aecp_aem_stream_input_state { + struct avb_aem_desc_stream desc; + + struct aecp_aem_stream_input_counters counters; + struct stream stream; +}; + +struct aecp_aem_stream_output_counters { + struct aecp_aem_state_base base_state; + + uint32_t stream_start; + uint32_t stream_stop; + uint32_t media_reset; + uint32_t tu; + uint32_t frame_tx; +}; + +struct aecp_aem_stream_output_state { + struct avb_aem_desc_stream desc; + + struct aecp_aem_stream_output_counters counters; + struct stream stream; +}; + +#endif // AVB_AECP_AEM_STATE_H
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem-types.h
Added
@@ -0,0 +1,112 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_AECP_AEM_TYPES_H__ +#define __AVB_AECP_AEM_TYPES_H__ + +/* +* IEEE 1722.1-2021, Table 7-141 - status field +*/ +#define AVB_AECP_AEM_STATUS_SUCCESS 0 +#define AVB_AECP_AEM_STATUS_NOT_IMPLEMENTED 1 +#define AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR 2 +#define AVB_AECP_AEM_STATUS_ENTITY_LOCKED 3 +#define AVB_AECP_AEM_STATUS_ENTITY_ACQUIRED 4 +#define AVB_AECP_AEM_STATUS_NOT_AUTHENTICATED 5 +#define AVB_AECP_AEM_STATUS_AUTHENTICATION_DISABLED 6 +#define AVB_AECP_AEM_STATUS_BAD_ARGUMENTS 7 +#define AVB_AECP_AEM_STATUS_NO_RESOURCES 8 +#define AVB_AECP_AEM_STATUS_IN_PROGRESS 9 +#define AVB_AECP_AEM_STATUS_ENTITY_MISBEHAVING 10 +#define AVB_AECP_AEM_STATUS_NOT_SUPPORTED 11 +#define AVB_AECP_AEM_STATUS_STREAM_IS_RUNNING 12 + +#define AECP_AEM_STRLEN_MAX (64) + +/** IEEE 1722.1-2021, Table 7-140 - Command Codes */ +// TODO: Update me +#define AVB_AECP_AEM_CMD_ACQUIRE_ENTITY 0x0000 +#define AVB_AECP_AEM_CMD_LOCK_ENTITY 0x0001 +#define AVB_AECP_AEM_CMD_ENTITY_AVAILABLE 0x0002 +#define AVB_AECP_AEM_CMD_CONTROLLER_AVAILABLE 0x0003 +#define AVB_AECP_AEM_CMD_READ_DESCRIPTOR 0x0004 +#define AVB_AECP_AEM_CMD_WRITE_DESCRIPTOR 0x0005 +#define AVB_AECP_AEM_CMD_SET_CONFIGURATION 0x0006 +#define AVB_AECP_AEM_CMD_GET_CONFIGURATION 0x0007 +#define AVB_AECP_AEM_CMD_SET_STREAM_FORMAT 0x0008 +#define AVB_AECP_AEM_CMD_GET_STREAM_FORMAT 0x0009 +#define AVB_AECP_AEM_CMD_SET_VIDEO_FORMAT 0x000a +#define AVB_AECP_AEM_CMD_GET_VIDEO_FORMAT 0x000b +#define AVB_AECP_AEM_CMD_SET_SENSOR_FORMAT 0x000c +#define AVB_AECP_AEM_CMD_GET_SENSOR_FORMAT 0x000d +#define AVB_AECP_AEM_CMD_SET_STREAM_INFO 0x000e +#define AVB_AECP_AEM_CMD_GET_STREAM_INFO 0x000f +#define AVB_AECP_AEM_CMD_SET_NAME 0x0010 +#define AVB_AECP_AEM_CMD_GET_NAME 0x0011 +#define AVB_AECP_AEM_CMD_SET_ASSOCIATION_ID 0x0012 +#define AVB_AECP_AEM_CMD_GET_ASSOCIATION_ID 0x0013 +#define AVB_AECP_AEM_CMD_SET_SAMPLING_RATE 0x0014 +#define AVB_AECP_AEM_CMD_GET_SAMPLING_RATE 0x0015 +#define AVB_AECP_AEM_CMD_SET_CLOCK_SOURCE 0x0016 +#define AVB_AECP_AEM_CMD_GET_CLOCK_SOURCE 0x0017 +#define AVB_AECP_AEM_CMD_SET_CONTROL 0x0018 +#define AVB_AECP_AEM_CMD_GET_CONTROL 0x0019 +#define AVB_AECP_AEM_CMD_INCREMENT_CONTROL 0x001a +#define AVB_AECP_AEM_CMD_DECREMENT_CONTROL 0x001b +#define AVB_AECP_AEM_CMD_SET_SIGNAL_SELECTOR 0x001c +#define AVB_AECP_AEM_CMD_GET_SIGNAL_SELECTOR 0x001d +#define AVB_AECP_AEM_CMD_SET_MIXER 0x001e +#define AVB_AECP_AEM_CMD_GET_MIXER 0x001f +#define AVB_AECP_AEM_CMD_SET_MATRIX 0x0020 +#define AVB_AECP_AEM_CMD_GET_MATRIX 0x0021 +#define AVB_AECP_AEM_CMD_START_STREAMING 0x0022 +#define AVB_AECP_AEM_CMD_STOP_STREAMING 0x0023 +#define AVB_AECP_AEM_CMD_REGISTER_UNSOLICITED_NOTIFICATION 0x0024 +#define AVB_AECP_AEM_CMD_DEREGISTER_UNSOLICITED_NOTIFICATION 0x0025 +#define AVB_AECP_AEM_CMD_IDENTIFY_NOTIFICATION 0x0026 +#define AVB_AECP_AEM_CMD_GET_AVB_INFO 0x0027 +#define AVB_AECP_AEM_CMD_GET_AS_PATH 0x0028 +#define AVB_AECP_AEM_CMD_GET_COUNTERS 0x0029 +#define AVB_AECP_AEM_CMD_REBOOT 0x002a +#define AVB_AECP_AEM_CMD_GET_AUDIO_MAP 0x002b +#define AVB_AECP_AEM_CMD_ADD_AUDIO_MAPPINGS 0x002c +#define AVB_AECP_AEM_CMD_REMOVE_AUDIO_MAPPINGS 0x002d +#define AVB_AECP_AEM_CMD_GET_VIDEO_MAP 0x002e +#define AVB_AECP_AEM_CMD_ADD_VIDEO_MAPPINGS 0x002f +#define AVB_AECP_AEM_CMD_REMOVE_VIDEO_MAPPINGS 0x0030 +#define AVB_AECP_AEM_CMD_GET_SENSOR_MAP 0x0031 +#define AVB_AECP_AEM_CMD_ADD_SENSOR_MAPPINGS 0x0032 +#define AVB_AECP_AEM_CMD_REMOVE_SENSOR_MAPPINGS 0x0033 +#define AVB_AECP_AEM_CMD_START_OPERATION 0x0034 +#define AVB_AECP_AEM_CMD_ABORT_OPERATION 0x0035 +#define AVB_AECP_AEM_CMD_OPERATION_STATUS 0x0036 +#define AVB_AECP_AEM_CMD_AUTH_ADD_KEY 0x0037 +#define AVB_AECP_AEM_CMD_AUTH_DELETE_KEY 0x0038 +#define AVB_AECP_AEM_CMD_AUTH_GET_KEY_LIST 0x0039 +#define AVB_AECP_AEM_CMD_AUTH_GET_KEY 0x003a +#define AVB_AECP_AEM_CMD_AUTH_ADD_KEY_TO_CHAIN 0x003b +#define AVB_AECP_AEM_CMD_AUTH_DELETE_KEY_FROM_CHAIN 0x003c +#define AVB_AECP_AEM_CMD_AUTH_GET_KEYCHAIN_LIST 0x003d +#define AVB_AECP_AEM_CMD_AUTH_GET_IDENTITY 0x003e +#define AVB_AECP_AEM_CMD_AUTH_ADD_TOKEN 0x003f +#define AVB_AECP_AEM_CMD_AUTH_DELETE_TOKEN 0x0040 +#define AVB_AECP_AEM_CMD_AUTHENTICATE 0x0041 +#define AVB_AECP_AEM_CMD_DEAUTHENTICATE 0x0042 +#define AVB_AECP_AEM_CMD_ENABLE_TRANSPORT_SECURITY 0x0043 +#define AVB_AECP_AEM_CMD_DISABLE_TRANSPORT_SECURITY 0x0044 +#define AVB_AECP_AEM_CMD_ENABLE_STREAM_ENCRYPTION 0x0045 +#define AVB_AECP_AEM_CMD_DISABLE_STREAM_ENCRYPTION 0x0046 +#define AVB_AECP_AEM_CMD_SET_MEMORY_OBJECT_LENGTH 0x0047 +#define AVB_AECP_AEM_CMD_GET_MEMORY_OBJECT_LENGTH 0x0048 +#define AVB_AECP_AEM_CMD_SET_STREAM_BACKUP 0x0049 +#define AVB_AECP_AEM_CMD_GET_STREAM_BACKUP 0x004a +#define AVB_AECP_AEM_CMD_GET_DYNAMIC_INFO 0x004b +#define AVB_AECP_AEM_CMD_EXPANSION 0x7fff + +#define AVB_AEM_ACQUIRE_ENTITY_PERSISTENT_FLAG (1<<0) + +#endif //__AVB_AECP_AEM_TYPES_H__ \ No newline at end of file
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/aecp-aem.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem.c
Changed
@@ -1,36 +1,30 @@ /* AVB support */ /* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki */ /* SPDX-License-Identifier: MIT */ #include "aecp-aem.h" #include "aecp-aem-descriptors.h" +#include "aecp-aem-cmds-resps/cmd-resp-helpers.h" +#include "utils.h" + +/* The headers including the command and response of the system */ +#include "aecp-aem-cmds-resps/cmd-available.h" +#include "aecp-aem-cmds-resps/cmd-get-set-configuration.h" +#include "aecp-aem-cmds-resps/cmd-get-set-sampling-rate.h" +#include "aecp-aem-cmds-resps/cmd-get-set-control.h" +#include "aecp-aem-cmds-resps/cmd-lock-entity.h" +#include "aecp-aem-cmds-resps/cmd-register-unsolicited-notifications.h" +#include "aecp-aem-cmds-resps/cmd-deregister-unsolicited-notifications.h" +#include "aecp-aem-cmds-resps/cmd-get-set-name.h" +#include "aecp-aem-cmds-resps/cmd-get-set-stream-format.h" +#include "aecp-aem-cmds-resps/cmd-get-set-clock-source.h" +#include "aecp-aem-cmds-resps/cmd-lock-entity.h" -static int reply_status(struct aecp *aecp, int status, const void *m, int len) -{ - struct server *server = aecp->server; - uint8_t buflen; - struct avb_ethernet_header *h = (void*)buf; - struct avb_packet_aecp_header *reply = SPA_PTROFF(h, sizeof(*h), void); - - memcpy(buf, m, len); - AVB_PACKET_AECP_SET_MESSAGE_TYPE(reply, AVB_AECP_MESSAGE_TYPE_AEM_RESPONSE); - AVB_PACKET_AECP_SET_STATUS(reply, status); - - return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, len); -} - -static int reply_not_implemented(struct aecp *aecp, const void *m, int len) -{ - return reply_status(aecp, AVB_AECP_AEM_STATUS_NOT_IMPLEMENTED, m, len); -} - -static int reply_success(struct aecp *aecp, const void *m, int len) -{ - return reply_status(aecp, AVB_AECP_AEM_STATUS_SUCCESS, m, len); -} /* ACQUIRE_ENTITY */ -static int handle_acquire_entity(struct aecp *aecp, const void *m, int len) +static int handle_acquire_entity_avb_legacy(struct aecp *aecp, int64_t now, + const void *m, int len) { struct server *server = aecp->server; const struct avb_packet_aecp_aem *p = m; @@ -45,7 +39,8 @@ desc = server_find_descriptor(server, desc_type, desc_id); if (desc == NULL) - return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); if (desc_type != AVB_AEM_DESC_ENTITY || desc_id != 0) return reply_not_implemented(aecp, m, len); @@ -54,7 +49,8 @@ } /* LOCK_ENTITY */ -static int handle_lock_entity(struct aecp *aecp, const void *m, int len) +static int handle_lock_entity_avb_legacy(struct aecp *aecp, int64_t now, + const void *m, int len) { struct server *server = aecp->server; const struct avb_packet_aecp_aem *p = m; @@ -78,7 +74,7 @@ } /* READ_DESCRIPTOR */ -static int handle_read_descriptor(struct aecp *aecp, const void *m, int len) +static int handle_read_descriptor_common(struct aecp *aecp, int64_t now, const void *m, int len) { struct server *server = aecp->server; const struct avb_ethernet_header *h = m; @@ -120,7 +116,8 @@ } /* GET_AVB_INFO */ -static int handle_get_avb_info(struct aecp *aecp, const void *m, int len) +static int handle_get_avb_info_common(struct aecp *aecp, int64_t now, + const void *m, int len) { struct server *server = aecp->server; const struct avb_ethernet_header *h = m; @@ -169,94 +166,241 @@ } /* AEM_COMMAND */ -struct cmd_info { - uint16_t type; - const char *name; - int (*handle) (struct aecp *aecp, const void *p, int len); +/* TODO in the case the AVB mode allows you to modifiy a Milan readonly +descriptor, then create a array of is_readonly depending on the mode used */ +static const char * const cmd_names = { + AVB_AECP_AEM_CMD_ACQUIRE_ENTITY = "acquire-entity", + AVB_AECP_AEM_CMD_LOCK_ENTITY = "lock-entity", + AVB_AECP_AEM_CMD_ENTITY_AVAILABLE = "entity-available", + AVB_AECP_AEM_CMD_CONTROLLER_AVAILABLE = "controller-available", + AVB_AECP_AEM_CMD_READ_DESCRIPTOR = "read-descriptor", + AVB_AECP_AEM_CMD_WRITE_DESCRIPTOR = "write-descriptor", + AVB_AECP_AEM_CMD_SET_CONFIGURATION = "set-configuration", + AVB_AECP_AEM_CMD_GET_CONFIGURATION = "get-configuration", + AVB_AECP_AEM_CMD_SET_STREAM_FORMAT = "set-stream-format", + AVB_AECP_AEM_CMD_GET_STREAM_FORMAT = "get-stream-format", + AVB_AECP_AEM_CMD_SET_VIDEO_FORMAT = "set-video-format", + AVB_AECP_AEM_CMD_GET_VIDEO_FORMAT = "get-video-format", + AVB_AECP_AEM_CMD_SET_SENSOR_FORMAT = "set-sensor-format", + AVB_AECP_AEM_CMD_GET_SENSOR_FORMAT = "get-sensor-format", + AVB_AECP_AEM_CMD_SET_STREAM_INFO = "set-stream-info", + AVB_AECP_AEM_CMD_GET_STREAM_INFO = "get-stream-info", + AVB_AECP_AEM_CMD_SET_NAME = "set-name", + AVB_AECP_AEM_CMD_GET_NAME = "get-name", + AVB_AECP_AEM_CMD_SET_ASSOCIATION_ID = "set-association-id", + AVB_AECP_AEM_CMD_GET_ASSOCIATION_ID = "get-association-id", + AVB_AECP_AEM_CMD_SET_SAMPLING_RATE = "set-sampling-rate", + AVB_AECP_AEM_CMD_GET_SAMPLING_RATE = "get-sampling-rate", + AVB_AECP_AEM_CMD_SET_CLOCK_SOURCE = "set-clock-source", + AVB_AECP_AEM_CMD_GET_CLOCK_SOURCE = "get-clock-source", + AVB_AECP_AEM_CMD_SET_CONTROL = "set-control", + AVB_AECP_AEM_CMD_GET_CONTROL = "get-control", + AVB_AECP_AEM_CMD_INCREMENT_CONTROL = "increment-control", + AVB_AECP_AEM_CMD_DECREMENT_CONTROL = "decrement-control", + AVB_AECP_AEM_CMD_SET_SIGNAL_SELECTOR = "set-signal-selector", + AVB_AECP_AEM_CMD_GET_SIGNAL_SELECTOR = "get-signal-selector", + AVB_AECP_AEM_CMD_SET_MIXER = "set-mixer", + AVB_AECP_AEM_CMD_GET_MIXER = "get-mixer", + AVB_AECP_AEM_CMD_SET_MATRIX = "set-matrix", + AVB_AECP_AEM_CMD_GET_MATRIX = "get-matrix", + AVB_AECP_AEM_CMD_START_STREAMING = "start-streaming", + AVB_AECP_AEM_CMD_STOP_STREAMING = "stop-streaming", + AVB_AECP_AEM_CMD_REGISTER_UNSOLICITED_NOTIFICATION = "register-unsolicited-notification", + AVB_AECP_AEM_CMD_DEREGISTER_UNSOLICITED_NOTIFICATION = "deregister-unsolicited-notification", + AVB_AECP_AEM_CMD_IDENTIFY_NOTIFICATION = "identify-notification", + AVB_AECP_AEM_CMD_GET_AVB_INFO = "get-avb-info", + AVB_AECP_AEM_CMD_GET_AS_PATH = "get-as-path", + AVB_AECP_AEM_CMD_GET_COUNTERS = "get-counters", + AVB_AECP_AEM_CMD_REBOOT = "reboot", + AVB_AECP_AEM_CMD_GET_AUDIO_MAP = "get-audio-map", + AVB_AECP_AEM_CMD_ADD_AUDIO_MAPPINGS = "add-audio-mappings", + AVB_AECP_AEM_CMD_REMOVE_AUDIO_MAPPINGS = "remove-audio-mappings", + AVB_AECP_AEM_CMD_GET_VIDEO_MAP = "get-video-map", + AVB_AECP_AEM_CMD_ADD_VIDEO_MAPPINGS = "add-video-mappings", + AVB_AECP_AEM_CMD_REMOVE_VIDEO_MAPPINGS = "remove-video-mappings", + AVB_AECP_AEM_CMD_GET_SENSOR_MAP = "get-sensor-map" }; -static const struct cmd_info cmd_info = { - { AVB_AECP_AEM_CMD_ACQUIRE_ENTITY, "acquire-entity", handle_acquire_entity, }, - { AVB_AECP_AEM_CMD_LOCK_ENTITY, "lock-entity", handle_lock_entity, }, - { AVB_AECP_AEM_CMD_ENTITY_AVAILABLE, "entity-available", NULL, }, - { AVB_AECP_AEM_CMD_CONTROLLER_AVAILABLE, "controller-available", NULL, }, - { AVB_AECP_AEM_CMD_READ_DESCRIPTOR, "read-descriptor", handle_read_descriptor, }, - { AVB_AECP_AEM_CMD_WRITE_DESCRIPTOR, "write-descriptor", NULL, }, - { AVB_AECP_AEM_CMD_SET_CONFIGURATION, "set-configuration", NULL, }, - { AVB_AECP_AEM_CMD_GET_CONFIGURATION, "get-configuration", NULL, }, - { AVB_AECP_AEM_CMD_SET_STREAM_FORMAT, "set-stream-format", NULL, }, - { AVB_AECP_AEM_CMD_GET_STREAM_FORMAT, "get-stream-format", NULL, }, - { AVB_AECP_AEM_CMD_SET_VIDEO_FORMAT, "set-video-format", NULL, }, - { AVB_AECP_AEM_CMD_GET_VIDEO_FORMAT, "get-video-format", NULL, }, - { AVB_AECP_AEM_CMD_SET_SENSOR_FORMAT, "set-sensor-format", NULL, }, - { AVB_AECP_AEM_CMD_GET_SENSOR_FORMAT, "get-sensor-format", NULL, }, - { AVB_AECP_AEM_CMD_SET_STREAM_INFO, "set-stream-info", NULL, }, - { AVB_AECP_AEM_CMD_GET_STREAM_INFO, "get-stream-info", NULL, }, - { AVB_AECP_AEM_CMD_SET_NAME, "set-name", NULL, }, - { AVB_AECP_AEM_CMD_GET_NAME, "get-name", NULL, }, - { AVB_AECP_AEM_CMD_SET_ASSOCIATION_ID, "set-association-id", NULL, }, - { AVB_AECP_AEM_CMD_GET_ASSOCIATION_ID, "get-association-id", NULL, }, - { AVB_AECP_AEM_CMD_SET_SAMPLING_RATE, "set-sampling-rate", NULL, }, - { AVB_AECP_AEM_CMD_GET_SAMPLING_RATE, "get-sampling-rate", NULL, }, - { AVB_AECP_AEM_CMD_SET_CLOCK_SOURCE, "set-clock-source", NULL, }, - { AVB_AECP_AEM_CMD_GET_CLOCK_SOURCE, "get-clock-source", NULL, }, - { AVB_AECP_AEM_CMD_SET_CONTROL, "set-control", NULL, }, - { AVB_AECP_AEM_CMD_GET_CONTROL, "get-control", NULL, }, - { AVB_AECP_AEM_CMD_INCREMENT_CONTROL, "increment-control", NULL, }, - { AVB_AECP_AEM_CMD_DECREMENT_CONTROL, "decrement-control", NULL, }, - { AVB_AECP_AEM_CMD_SET_SIGNAL_SELECTOR, "set-signal-selector", NULL, }, - { AVB_AECP_AEM_CMD_GET_SIGNAL_SELECTOR, "get-signal-selector", NULL, }, - { AVB_AECP_AEM_CMD_SET_MIXER, "set-mixer", NULL, }, - { AVB_AECP_AEM_CMD_GET_MIXER, "get-mixer", NULL, }, - { AVB_AECP_AEM_CMD_SET_MATRIX, "set-matrix", NULL, }, - { AVB_AECP_AEM_CMD_GET_MATRIX, "get-matrix", NULL, }, - { AVB_AECP_AEM_CMD_START_STREAMING, "start-streaming", NULL, }, - { AVB_AECP_AEM_CMD_STOP_STREAMING, "stop-streaming", NULL, }, - { AVB_AECP_AEM_CMD_REGISTER_UNSOLICITED_NOTIFICATION, "register-unsolicited-notification", NULL, }, - { AVB_AECP_AEM_CMD_DEREGISTER_UNSOLICITED_NOTIFICATION, "deregister-unsolicited-notification", NULL, }, - { AVB_AECP_AEM_CMD_IDENTIFY_NOTIFICATION, "identify-notification", NULL, }, - { AVB_AECP_AEM_CMD_GET_AVB_INFO, "get-avb-info", handle_get_avb_info, }, - { AVB_AECP_AEM_CMD_GET_AS_PATH, "get-as-path", NULL, }, - { AVB_AECP_AEM_CMD_GET_COUNTERS, "get-counters", NULL, },
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/aecp-aem.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/aecp-aem.h
Changed
@@ -1,104 +1,13 @@ /* AVB support */ /* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */ +/* SPDX-FileCopyrightText: Copyright © 2027 Alexandre Malki <alexandre.malki@kebag-logic.com> */ /* SPDX-License-Identifier: MIT */ #ifndef AVB_AEM_H #define AVB_AEM_H #include "aecp.h" - -#define AVB_AECP_AEM_STATUS_SUCCESS 0 -#define AVB_AECP_AEM_STATUS_NOT_IMPLEMENTED 1 -#define AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR 2 -#define AVB_AECP_AEM_STATUS_ENTITY_LOCKED 3 -#define AVB_AECP_AEM_STATUS_ENTITY_ACQUIRED 4 -#define AVB_AECP_AEM_STATUS_NOT_AUTHENTICATED 5 -#define AVB_AECP_AEM_STATUS_AUTHENTICATION_DISABLED 6 -#define AVB_AECP_AEM_STATUS_BAD_ARGUMENTS 7 -#define AVB_AECP_AEM_STATUS_NO_RESOURCES 8 -#define AVB_AECP_AEM_STATUS_IN_PROGRESS 9 -#define AVB_AECP_AEM_STATUS_ENTITY_MISBEHAVING 10 -#define AVB_AECP_AEM_STATUS_NOT_SUPPORTED 11 -#define AVB_AECP_AEM_STATUS_STREAM_IS_RUNNING 12 - -#define AVB_AECP_AEM_CMD_ACQUIRE_ENTITY 0x0000 -#define AVB_AECP_AEM_CMD_LOCK_ENTITY 0x0001 -#define AVB_AECP_AEM_CMD_ENTITY_AVAILABLE 0x0002 -#define AVB_AECP_AEM_CMD_CONTROLLER_AVAILABLE 0x0003 -#define AVB_AECP_AEM_CMD_READ_DESCRIPTOR 0x0004 -#define AVB_AECP_AEM_CMD_WRITE_DESCRIPTOR 0x0005 -#define AVB_AECP_AEM_CMD_SET_CONFIGURATION 0x0006 -#define AVB_AECP_AEM_CMD_GET_CONFIGURATION 0x0007 -#define AVB_AECP_AEM_CMD_SET_STREAM_FORMAT 0x0008 -#define AVB_AECP_AEM_CMD_GET_STREAM_FORMAT 0x0009 -#define AVB_AECP_AEM_CMD_SET_VIDEO_FORMAT 0x000a -#define AVB_AECP_AEM_CMD_GET_VIDEO_FORMAT 0x000b -#define AVB_AECP_AEM_CMD_SET_SENSOR_FORMAT 0x000c -#define AVB_AECP_AEM_CMD_GET_SENSOR_FORMAT 0x000d -#define AVB_AECP_AEM_CMD_SET_STREAM_INFO 0x000e -#define AVB_AECP_AEM_CMD_GET_STREAM_INFO 0x000f -#define AVB_AECP_AEM_CMD_SET_NAME 0x0010 -#define AVB_AECP_AEM_CMD_GET_NAME 0x0011 -#define AVB_AECP_AEM_CMD_SET_ASSOCIATION_ID 0x0012 -#define AVB_AECP_AEM_CMD_GET_ASSOCIATION_ID 0x0013 -#define AVB_AECP_AEM_CMD_SET_SAMPLING_RATE 0x0014 -#define AVB_AECP_AEM_CMD_GET_SAMPLING_RATE 0x0015 -#define AVB_AECP_AEM_CMD_SET_CLOCK_SOURCE 0x0016 -#define AVB_AECP_AEM_CMD_GET_CLOCK_SOURCE 0x0017 -#define AVB_AECP_AEM_CMD_SET_CONTROL 0x0018 -#define AVB_AECP_AEM_CMD_GET_CONTROL 0x0019 -#define AVB_AECP_AEM_CMD_INCREMENT_CONTROL 0x001a -#define AVB_AECP_AEM_CMD_DECREMENT_CONTROL 0x001b -#define AVB_AECP_AEM_CMD_SET_SIGNAL_SELECTOR 0x001c -#define AVB_AECP_AEM_CMD_GET_SIGNAL_SELECTOR 0x001d -#define AVB_AECP_AEM_CMD_SET_MIXER 0x001e -#define AVB_AECP_AEM_CMD_GET_MIXER 0x001f -#define AVB_AECP_AEM_CMD_SET_MATRIX 0x0020 -#define AVB_AECP_AEM_CMD_GET_MATRIX 0x0021 -#define AVB_AECP_AEM_CMD_START_STREAMING 0x0022 -#define AVB_AECP_AEM_CMD_STOP_STREAMING 0x0023 -#define AVB_AECP_AEM_CMD_REGISTER_UNSOLICITED_NOTIFICATION 0x0024 -#define AVB_AECP_AEM_CMD_DEREGISTER_UNSOLICITED_NOTIFICATION 0x0025 -#define AVB_AECP_AEM_CMD_IDENTIFY_NOTIFICATION 0x0026 -#define AVB_AECP_AEM_CMD_GET_AVB_INFO 0x0027 -#define AVB_AECP_AEM_CMD_GET_AS_PATH 0x0028 -#define AVB_AECP_AEM_CMD_GET_COUNTERS 0x0029 -#define AVB_AECP_AEM_CMD_REBOOT 0x002a -#define AVB_AECP_AEM_CMD_GET_AUDIO_MAP 0x002b -#define AVB_AECP_AEM_CMD_ADD_AUDIO_MAPPINGS 0x002c -#define AVB_AECP_AEM_CMD_REMOVE_AUDIO_MAPPINGS 0x002d -#define AVB_AECP_AEM_CMD_GET_VIDEO_MAP 0x002e -#define AVB_AECP_AEM_CMD_ADD_VIDEO_MAPPINGS 0x002f -#define AVB_AECP_AEM_CMD_REMOVE_VIDEO_MAPPINGS 0x0030 -#define AVB_AECP_AEM_CMD_GET_SENSOR_MAP 0x0031 -#define AVB_AECP_AEM_CMD_ADD_SENSOR_MAPPINGS 0x0032 -#define AVB_AECP_AEM_CMD_REMOVE_SENSOR_MAPPINGS 0x0033 -#define AVB_AECP_AEM_CMD_START_OPERATION 0x0034 -#define AVB_AECP_AEM_CMD_ABORT_OPERATION 0x0035 -#define AVB_AECP_AEM_CMD_OPERATION_STATUS 0x0036 -#define AVB_AECP_AEM_CMD_AUTH_ADD_KEY 0x0037 -#define AVB_AECP_AEM_CMD_AUTH_DELETE_KEY 0x0038 -#define AVB_AECP_AEM_CMD_AUTH_GET_KEY_LIST 0x0039 -#define AVB_AECP_AEM_CMD_AUTH_GET_KEY 0x003a -#define AVB_AECP_AEM_CMD_AUTH_ADD_KEY_TO_CHAIN 0x003b -#define AVB_AECP_AEM_CMD_AUTH_DELETE_KEY_FROM_CHAIN 0x003c -#define AVB_AECP_AEM_CMD_AUTH_GET_KEYCHAIN_LIST 0x003d -#define AVB_AECP_AEM_CMD_AUTH_GET_IDENTITY 0x003e -#define AVB_AECP_AEM_CMD_AUTH_ADD_TOKEN 0x003f -#define AVB_AECP_AEM_CMD_AUTH_DELETE_TOKEN 0x0040 -#define AVB_AECP_AEM_CMD_AUTHENTICATE 0x0041 -#define AVB_AECP_AEM_CMD_DEAUTHENTICATE 0x0042 -#define AVB_AECP_AEM_CMD_ENABLE_TRANSPORT_SECURITY 0x0043 -#define AVB_AECP_AEM_CMD_DISABLE_TRANSPORT_SECURITY 0x0044 -#define AVB_AECP_AEM_CMD_ENABLE_STREAM_ENCRYPTION 0x0045 -#define AVB_AECP_AEM_CMD_DISABLE_STREAM_ENCRYPTION 0x0046 -#define AVB_AECP_AEM_CMD_SET_MEMORY_OBJECT_LENGTH 0x0047 -#define AVB_AECP_AEM_CMD_GET_MEMORY_OBJECT_LENGTH 0x0048 -#define AVB_AECP_AEM_CMD_SET_STREAM_BACKUP 0x0049 -#define AVB_AECP_AEM_CMD_GET_STREAM_BACKUP 0x004a -#define AVB_AECP_AEM_CMD_EXPANSION 0x7fff - -#define AVB_AEM_ACQUIRE_ENTITY_PERSISTENT_FLAG (1<<0) +#include "aecp-aem-types.h" struct avb_packet_aecp_aem_acquire { uint32_t flags; @@ -114,6 +23,12 @@ uint16_t descriptor_id; } __attribute__ ((__packed__)); +struct avb_packet_aecp_aem_available { + uint32_t flags; + uint64_t acquired_controller_guid; + uint64_t lock_controller_guid; +} __attribute__ ((__packed__)); + struct avb_packet_aecp_aem_read_descriptor { uint16_t configuration; uint8_t reserved2; @@ -190,10 +105,18 @@ uint64_t association_id; } __attribute__ ((__packed__)); +union avb_packet_aecp_aem_pull_frequency { + struct { + uint32_t frequency:29; + uint32_t pull:3; + }; + uint32_t pull_frequency; +}__attribute__ ((__packed__)); + struct avb_packet_aecp_aem_setget_sampling_rate { uint16_t descriptor_type; uint16_t descriptor_id; - uint32_t sampling_rate; + union avb_packet_aecp_aem_pull_frequency sampling_rate; } __attribute__ ((__packed__)); struct avb_packet_aecp_aem_setget_clock_source { @@ -206,6 +129,7 @@ struct avb_packet_aecp_aem_setget_control { uint16_t descriptor_type; uint16_t descriptor_id; + uint8_t payload0; } __attribute__ ((__packed__)); struct avb_packet_aecp_aem_incdec_control { @@ -315,9 +239,13 @@ uint8_t payload0; } __attribute__ ((__packed__)); -#define AVB_PACKET_AEM_SET_COMMAND_TYPE(p,v) ((p)->cmd1 = ((v) >> 8),(p)->cmd2 = (v)) +#define AVB_PACKET_MILAN_DEFAULT_MTU (1500) + +#define AVB_PACKET_CONTROL_DATA_OFFSET (12U) + +#define AVB_PACKET_AEM_SET_COMMAND_TYPE(p,v) ((p)->cmd1 = ((v) >> 8),(p)->cmd2 = (v)) -#define AVB_PACKET_AEM_GET_COMMAND_TYPE(p) ((p)->cmd1 << 8 | (p)->cmd2) +#define AVB_PACKET_AEM_GET_COMMAND_TYPE(p) ((p)->cmd1 << 8 | (p)->cmd2) int avb_aecp_aem_handle_command(struct aecp *aecp, const void *m, int len); int avb_aecp_aem_handle_response(struct aecp *aecp, const void *m, int len);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/avdecc.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/avdecc.c
Changed
@@ -40,6 +40,12 @@ #define server_emit_periodic(s,n) server_emit(s, periodic, 0, n) #define server_emit_command(s,n,c,a,f) server_emit(s, command, 0, n, c, a, f) + +static const char *avb_mode_str = { + AVB_MODE_LEGACY = "AVB Legacy", + AVB_MODE_MILAN_V12 = "Milan V1.2", +}; + static void on_timer_event(void *data) { struct server *server = data; @@ -252,16 +258,20 @@ spa_list_append(&impl->servers, &server->link); str = spa_dict_lookup(props, "ifname"); server->ifname = str ? strdup(str) : NULL; + + if ((str = spa_dict_lookup(props, "milan")) != NULL && spa_atob(str)) + server->avb_mode = AVB_MODE_MILAN_V12; + else + server->avb_mode = AVB_MODE_LEGACY; + spa_hook_list_init(&server->listener_list); spa_list_init(&server->descriptors); - spa_list_init(&server->streams); server->debug_messages = false; if ((res = setup_socket(server)) < 0) goto error_free; - init_descriptors(server); server->mrp = avb_mrp_new(server); if (server->mrp == NULL) @@ -284,11 +294,10 @@ avb_mrp_attribute_begin(server->domain_attr->mrp, 0); avb_mrp_attribute_join(server->domain_attr->mrp, 0, true); - server_create_stream(server, SPA_DIRECTION_INPUT, 0); - server_create_stream(server, SPA_DIRECTION_OUTPUT, 0); - avb_maap_reserve(server->maap, 1); + init_descriptors(server); + return server; error_free: @@ -308,10 +317,17 @@ { struct impl *impl = server->impl; + server_destroy_descriptors(server); spa_list_remove(&server->link); if (server->source) pw_loop_destroy_source(impl->loop, server->source); pw_timer_queue_cancel(&server->timer); spa_hook_list_clean(&server->listener_list); + free(server->ifname); free(server); } + +const char *get_avb_mode_str(enum avb_mode mode) +{ + return avb_mode_strmode; +}
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/descriptors.c
Added
@@ -0,0 +1,824 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki (alexandre.malki@kebag-logic.com) */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#include "adp.h" +#include "aecp-aem.h" +#include "aecp-aem-types.h" +#include "aecp-aem-descriptors.h" +#include "aecp-aem-controls.h" +#include "es-builder.h" + +#include "entity-model-milan-v12.h" + +static void init_descriptor_legacy_avb(struct server *server) +{ + es_builder_add_descriptor(server, AVB_AEM_DESC_STRINGS, 0, + sizeof(struct avb_aem_desc_strings), + &(struct avb_aem_desc_strings) + { + .string_0 = "PipeWire", + .string_1 = "Configuration 1", + .string_2 = "Wim Taymans", + }); + + es_builder_add_descriptor(server, AVB_AEM_DESC_LOCALE, 0, + sizeof(struct avb_aem_desc_locale), + &(struct avb_aem_desc_locale) + { + .locale_identifier = "en-EN", + .number_of_strings = htons(1), + .base_strings = htons(0) + }); + + es_builder_add_descriptor(server, AVB_AEM_DESC_ENTITY, 0, + sizeof(struct avb_aem_desc_entity), + &(struct avb_aem_desc_entity) + { + .entity_id = htobe64(server->entity_id), + .entity_model_id = htobe64(0), + .entity_capabilities = htonl( + AVB_ADP_ENTITY_CAPABILITY_AEM_SUPPORTED | + AVB_ADP_ENTITY_CAPABILITY_CLASS_A_SUPPORTED | + AVB_ADP_ENTITY_CAPABILITY_GPTP_SUPPORTED | + AVB_ADP_ENTITY_CAPABILITY_AEM_IDENTIFY_CONTROL_INDEX_VALID | + AVB_ADP_ENTITY_CAPABILITY_AEM_INTERFACE_INDEX_VALID), + + .talker_stream_sources = htons(8), + .talker_capabilities = htons( + AVB_ADP_TALKER_CAPABILITY_IMPLEMENTED | + AVB_ADP_TALKER_CAPABILITY_AUDIO_SOURCE), + .listener_stream_sinks = htons(8), + .listener_capabilities = htons( + AVB_ADP_LISTENER_CAPABILITY_IMPLEMENTED | + AVB_ADP_LISTENER_CAPABILITY_AUDIO_SINK), + .controller_capabilities = htons(0), + .available_index = htonl(0), + .association_id = htobe64(0), + .entity_name = "PipeWire", + .vendor_name_string = htons(2), + .model_name_string = htons(0), + .firmware_version = "0.3.48", + .group_name = "", + .serial_number = "", + .configurations_count = htons(1), + .current_configuration = htons(0) + }); + struct { + struct avb_aem_desc_configuration desc; + struct avb_aem_desc_descriptor_count descriptor_counts8; + } __attribute__ ((__packed__)) config = + { + { + .object_name = "Configuration 1", + .localized_description = htons(1), + .descriptor_counts_count = htons(8), + .descriptor_counts_offset = htons( + 4 + sizeof(struct avb_aem_desc_configuration)), + }, + .descriptor_counts = { + { htons(AVB_AEM_DESC_AUDIO_UNIT), htons(1) }, + { htons(AVB_AEM_DESC_STREAM_INPUT), htons(1) }, + { htons(AVB_AEM_DESC_STREAM_OUTPUT), htons(1) }, + { htons(AVB_AEM_DESC_AVB_INTERFACE), htons(1) }, + { htons(AVB_AEM_DESC_CLOCK_SOURCE), htons(1) }, + { htons(AVB_AEM_DESC_CONTROL), htons(2) }, + { htons(AVB_AEM_DESC_LOCALE), htons(1) }, + { htons(AVB_AEM_DESC_CLOCK_DOMAIN), htons(1) } + } + }; + es_builder_add_descriptor(server, AVB_AEM_DESC_CONFIGURATION, 0, + sizeof(config), &config); + + struct { + struct avb_aem_desc_audio_unit desc; + union avb_aem_desc_sampling_rate sampling_rates6; + } __attribute__ ((__packed__)) audio_unit = + { + { + .object_name = "PipeWire", + .localized_description = htons(0), + .clock_domain_index = htons(0), + .number_of_stream_input_ports = htons(1), + .base_stream_input_port = htons(0), + .number_of_stream_output_ports = htons(1), + .base_stream_output_port = htons(0), + .number_of_external_input_ports = htons(8), + .base_external_input_port = htons(0), + .number_of_external_output_ports = htons(8), + .base_external_output_port = htons(0), + .number_of_internal_input_ports = htons(0), + .base_internal_input_port = htons(0), + .number_of_internal_output_ports = htons(0), + .base_internal_output_port = htons(0), + .number_of_controls = htons(0), + .base_control = htons(0), + .number_of_signal_selectors = htons(0), + .base_signal_selector = htons(0), + .number_of_mixers = htons(0), + .base_mixer = htons(0), + .number_of_matrices = htons(0), + .base_matrix = htons(0), + .number_of_splitters = htons(0), + .base_splitter = htons(0), + .number_of_combiners = htons(0), + .base_combiner = htons(0), + .number_of_demultiplexers = htons(0), + .base_demultiplexer = htons(0), + .number_of_multiplexers = htons(0), + .base_multiplexer = htons(0), + .number_of_transcoders = htons(0), + .base_transcoder = htons(0), + .number_of_control_blocks = htons(0), + .base_control_block = htons(0), + .current_sampling_rate.pull_frequency = htonl(48000), + .sampling_rates_offset = htons( + 4 + sizeof(struct avb_aem_desc_audio_unit)), + .sampling_rates_count = htons(6), + }, + .sampling_rates = { + { .pull_frequency = htonl(44100) }, + { .pull_frequency = htonl(48000) }, + { .pull_frequency = htonl(88200) }, + { .pull_frequency = htonl(96000) }, + { .pull_frequency = htonl(176400) }, + { .pull_frequency = htonl(192000) }, + } + }; + es_builder_add_descriptor(server, AVB_AEM_DESC_AUDIO_UNIT, 0, + sizeof(audio_unit), &audio_unit); + + struct { + struct avb_aem_desc_stream desc; + uint64_t stream_formats6; + } __attribute__ ((__packed__)) stream_input_0 = + { + { + .object_name = "Stream Input 1", + .localized_description = htons(0xffff), + .clock_domain_index = htons(0), + .stream_flags = htons( + AVB_AEM_DESC_STREAM_FLAG_SYNC_SOURCE | + AVB_AEM_DESC_STREAM_FLAG_CLASS_A), + .current_format = htobe64(0x00a0020840000800ULL), + .formats_offset = htons( + 4 + sizeof(struct avb_aem_desc_stream)), + .number_of_formats = htons(6), + .backup_talker_entity_id_0 = htobe64(0), + .backup_talker_unique_id_0 = htons(0), + .backup_talker_entity_id_1 = htobe64(0), + .backup_talker_unique_id_1 = htons(0), + .backup_talker_entity_id_2 = htobe64(0), + .backup_talker_unique_id_2 = htons(0), + .backedup_talker_entity_id = htobe64(0), + .backedup_talker_unique = htons(0), + .avb_interface_index = htons(0), + .buffer_length = htons(8) + }, + .stream_formats = { + htobe64(0x00a0010860000800ULL), + htobe64(0x00a0020860000800ULL), + htobe64(0x00a0030860000800ULL), + htobe64(0x00a0040860000800ULL), + htobe64(0x00a0050860000800ULL), + htobe64(0x00a0060860000800ULL), + }, + }; + es_builder_add_descriptor(server, AVB_AEM_DESC_STREAM_INPUT, 0, + sizeof(stream_input_0), &stream_input_0); + + struct { + struct avb_aem_desc_stream desc; + uint64_t stream_formats6; + } __attribute__ ((__packed__)) stream_output_0 = + { + { + .object_name = "Stream Output 1", + .localized_description = htons(0xffff),
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/descriptors.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/descriptors.h
Changed
@@ -3,12 +3,6 @@ /* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki (alexandre.malki@kebag-logic.com) */ /* SPDX-License-Identifier: MIT */ -#include "adp.h" -#include "aecp-aem.h" -#include "aecp-aem-descriptors.h" -#include "es-builder.h" -#include "internal.h" - /** * \todo This whole code needs to be re-factore, * configuring the entity using such a "HARDCODED" @@ -25,249 +19,4 @@ * Having the YANG would allow directly to know the * capabilites/limits of the protocol */ -static inline void init_descriptors(struct server *server) -{ - es_builder_add_descriptor(server, AVB_AEM_DESC_STRINGS, 0, - sizeof(struct avb_aem_desc_strings), - &(struct avb_aem_desc_strings) - { - .string_0 = "PipeWire", - .string_1 = "Configuration 1", - .string_2 = "Wim Taymans", - }); - es_builder_add_descriptor(server, AVB_AEM_DESC_LOCALE, 0, - sizeof(struct avb_aem_desc_locale), - &(struct avb_aem_desc_locale) - { - .locale_identifier = "en-EN", - .number_of_strings = htons(1), - .base_strings = htons(0) - }); - es_builder_add_descriptor(server, AVB_AEM_DESC_ENTITY, 0, - sizeof(struct avb_aem_desc_entity), - &(struct avb_aem_desc_entity) - { - .entity_id = htobe64(server->entity_id), - .entity_model_id = htobe64(0), - .entity_capabilities = htonl( - AVB_ADP_ENTITY_CAPABILITY_AEM_SUPPORTED | - AVB_ADP_ENTITY_CAPABILITY_CLASS_A_SUPPORTED | - AVB_ADP_ENTITY_CAPABILITY_GPTP_SUPPORTED | - AVB_ADP_ENTITY_CAPABILITY_AEM_IDENTIFY_CONTROL_INDEX_VALID | - AVB_ADP_ENTITY_CAPABILITY_AEM_INTERFACE_INDEX_VALID), - - .talker_stream_sources = htons(8), - .talker_capabilities = htons( - AVB_ADP_TALKER_CAPABILITY_IMPLEMENTED | - AVB_ADP_TALKER_CAPABILITY_AUDIO_SOURCE), - .listener_stream_sinks = htons(8), - .listener_capabilities = htons( - AVB_ADP_LISTENER_CAPABILITY_IMPLEMENTED | - AVB_ADP_LISTENER_CAPABILITY_AUDIO_SINK), - .controller_capabilities = htons(0), - .available_index = htonl(0), - .association_id = htobe64(0), - .entity_name = "PipeWire", - .vendor_name_string = htons(2), - .model_name_string = htons(0), - .firmware_version = "0.3.48", - .group_name = "", - .serial_number = "", - .configurations_count = htons(1), - .current_configuration = htons(0) - }); - struct { - struct avb_aem_desc_configuration desc; - struct avb_aem_desc_descriptor_count descriptor_counts8; - } __attribute__ ((__packed__)) config = - { - { - .object_name = "Configuration 1", - .localized_description = htons(1), - .descriptor_counts_count = htons(8), - .descriptor_counts_offset = htons( - 4 + sizeof(struct avb_aem_desc_configuration)), - }, - .descriptor_counts = { - { htons(AVB_AEM_DESC_AUDIO_UNIT), htons(1) }, - { htons(AVB_AEM_DESC_STREAM_INPUT), htons(1) }, - { htons(AVB_AEM_DESC_STREAM_OUTPUT), htons(1) }, - { htons(AVB_AEM_DESC_AVB_INTERFACE), htons(1) }, - { htons(AVB_AEM_DESC_CLOCK_SOURCE), htons(1) }, - { htons(AVB_AEM_DESC_CONTROL), htons(2) }, - { htons(AVB_AEM_DESC_LOCALE), htons(1) }, - { htons(AVB_AEM_DESC_CLOCK_DOMAIN), htons(1) } - } - }; - es_builder_add_descriptor(server, AVB_AEM_DESC_CONFIGURATION, 0, - sizeof(config), &config); - - struct { - struct avb_aem_desc_audio_unit desc; - struct avb_aem_desc_sampling_rate sampling_rates6; - } __attribute__ ((__packed__)) audio_unit = - { - { - .object_name = "PipeWire", - .localized_description = htons(0), - .clock_domain_index = htons(0), - .number_of_stream_input_ports = htons(1), - .base_stream_input_port = htons(0), - .number_of_stream_output_ports = htons(1), - .base_stream_output_port = htons(0), - .number_of_external_input_ports = htons(8), - .base_external_input_port = htons(0), - .number_of_external_output_ports = htons(8), - .base_external_output_port = htons(0), - .number_of_internal_input_ports = htons(0), - .base_internal_input_port = htons(0), - .number_of_internal_output_ports = htons(0), - .base_internal_output_port = htons(0), - .number_of_controls = htons(0), - .base_control = htons(0), - .number_of_signal_selectors = htons(0), - .base_signal_selector = htons(0), - .number_of_mixers = htons(0), - .base_mixer = htons(0), - .number_of_matrices = htons(0), - .base_matrix = htons(0), - .number_of_splitters = htons(0), - .base_splitter = htons(0), - .number_of_combiners = htons(0), - .base_combiner = htons(0), - .number_of_demultiplexers = htons(0), - .base_demultiplexer = htons(0), - .number_of_multiplexers = htons(0), - .base_multiplexer = htons(0), - .number_of_transcoders = htons(0), - .base_transcoder = htons(0), - .number_of_control_blocks = htons(0), - .base_control_block = htons(0), - .current_sampling_rate = htonl(48000), - .sampling_rates_offset = htons( - 4 + sizeof(struct avb_aem_desc_audio_unit)), - .sampling_rates_count = htons(6), - }, - .sampling_rates = { - { .pull_frequency = htonl(44100) }, - { .pull_frequency = htonl(48000) }, - { .pull_frequency = htonl(88200) }, - { .pull_frequency = htonl(96000) }, - { .pull_frequency = htonl(176400) }, - { .pull_frequency = htonl(192000) }, - } - }; - es_builder_add_descriptor(server, AVB_AEM_DESC_AUDIO_UNIT, 0, - sizeof(audio_unit), &audio_unit); - - struct { - struct avb_aem_desc_stream desc; - uint64_t stream_formats6; - } __attribute__ ((__packed__)) stream_input_0 = - { - { - .object_name = "Stream Input 1", - .localized_description = htons(0xffff), - .clock_domain_index = htons(0), - .stream_flags = htons( - AVB_AEM_DESC_STREAM_FLAG_SYNC_SOURCE | - AVB_AEM_DESC_STREAM_FLAG_CLASS_A), - .current_format = htobe64(0x00a0020840000800ULL), - .formats_offset = htons( - 4 + sizeof(struct avb_aem_desc_stream)), - .number_of_formats = htons(6), - .backup_talker_entity_id_0 = htobe64(0), - .backup_talker_unique_id_0 = htons(0), - .backup_talker_entity_id_1 = htobe64(0), - .backup_talker_unique_id_1 = htons(0), - .backup_talker_entity_id_2 = htobe64(0), - .backup_talker_unique_id_2 = htons(0), - .backedup_talker_entity_id = htobe64(0), - .backedup_talker_unique = htons(0), - .avb_interface_index = htons(0), - .buffer_length = htons(8) - }, - .stream_formats = { - htobe64(0x00a0010860000800ULL), - htobe64(0x00a0020860000800ULL), - htobe64(0x00a0030860000800ULL), - htobe64(0x00a0040860000800ULL), - htobe64(0x00a0050860000800ULL), - htobe64(0x00a0060860000800ULL), - }, - }; - es_builder_add_descriptor(server, AVB_AEM_DESC_STREAM_INPUT, 0, - sizeof(stream_input_0), &stream_input_0); - - struct { - struct avb_aem_desc_stream desc; - uint64_t stream_formats6; - } __attribute__ ((__packed__)) stream_output_0 = - { - { - .object_name = "Stream Output 1", - .localized_description = htons(0xffff), - .clock_domain_index = htons(0),
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/entity-model-milan-v12.h
Added
@@ -0,0 +1,477 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki <alexandre.malki@kebag-logic.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __DESCRIPTOR_ENTITY_MODEL_MILAN_H__ +#define __DESCRIPTOR_ENTITY_MODEL_MILAN_H__ + +#define TALKER_ENABLE 1 + +// TODO: Make defines as long as specified length +/**************************************************************************************/ +/* IEEE 1722.1-2021, Sec. 7.2.12 - STRINGS Descriptor +* Up to 7 localized strings +*/ +#define DSC_STRINGS_0_DEVICE_NAME "PipeWire" +#define DSC_STRINGS_1_CONFIGURATION_NAME "NON - redundant - 48kHz" +#define DSC_STRINGS_2_MANUFACTURER_NAME "Kebag Logic" +#define DSC_STRINGS_3_GROUP_NAME "Kebag Logic" +#define DSC_STRINGS_4_MAINTAINER_0 "Alexandre Malki" +#define DSC_STRINGS_4_MAINTAINER_1 "Simon Gapp" + +/**************************************************************************************/ +/* IEEE 1722.1-2021, Sec. 7.2.11 - LOCALE Descriptor */ +#define DSC_LOCALE_LANGUAGE_CODE "en-EN" +#define DSC_LOCALE_NO_OF_STRINGS 1 +#define DSC_LOCALE_BASE_STRINGS 0 + +/**************************************************************************************/ +/* IEEE 1722.1-2021, Sec. 7.2.1 - ENTITY Descriptor */ +/* Milan v1.2, Sec. 5.3.3.1 */ + +#define DSC_ENTITY_MODEL_ENTITY_ID 0xDEAD00BEEF00FEED +#define DSC_ENTITY_MODEL_ID 0 +#define DSC_ENTITY_MODEL_ENTITY_CAPABILITIES (AVB_ADP_ENTITY_CAPABILITY_AEM_SUPPORTED | \ + AVB_ADP_ENTITY_CAPABILITY_CLASS_A_SUPPORTED | \ + AVB_ADP_ENTITY_CAPABILITY_GPTP_SUPPORTED | \ + AVB_ADP_ENTITY_CAPABILITY_AEM_IDENTIFY_CONTROL_INDEX_VALID | \ + AVB_ADP_ENTITY_CAPABILITY_AEM_INTERFACE_INDEX_VALID) +/* IEEE 1722.1-2021, Table 7-2 - ENTITY Descriptor +* This is the maximum number of STREAM_OUTPUT +* descriptors the ATDECC Entity has for +* Output Streams in any of its Configurations */ + +#if TALKER_ENABLE + #define DSC_ENTITY_MODEL_TALKER_STREAM_SOURCES 8 + #define DSC_ENTITY_MODEL_TALKER_CAPABILITIES (AVB_ADP_TALKER_CAPABILITY_IMPLEMENTED | \ + AVB_ADP_TALKER_CAPABILITY_AUDIO_SOURCE) +#else + #define DSC_ENTITY_MODEL_TALKER_STREAM_SOURCES 0 + #define DSC_ENTITY_MODEL_TALKER_CAPABILITIES 0 +#endif + + +#define DSC_ENTITY_MODEL_LISTENER_STREAM_SINKS 8 +#define DSC_ENTITY_MODEL_LISTENER_CAPABILITIES (AVB_ADP_LISTENER_CAPABILITY_IMPLEMENTED | \ + AVB_ADP_LISTENER_CAPABILITY_AUDIO_SINK) +#define DSC_ENTITY_MODEL_CONTROLLER_CAPABILITIES 0 +/* IEEE 1722.1-2021, Table 7-2 ENTITY Descriptor +* The available index of the ATDECC Entity. +* This is the same as the available_index field +* in ATDECC Discovery Protocol.*/ +#define DSC_ENTITY_MODEL_AVAILABLE_INDEX 0 +/* IEEE 1722.1-2021, Table 7-2 ENTITY Descriptor + * The association ID for the ATDECC Entity. + * This is the same as association_id field + * in ATDECC Discovery Protocol*/ +#define DSC_ENTITY_MODEL_ASSOCIATION_ID 0 +#define DSC_ENTITY_MODEL_ENTITY_NAME DSC_STRINGS_0_DEVICE_NAME +/* IEEE 1722.1-2021, Table 7-2 - ENTITY Descriptor + * The localized string reference pointing to the + * localized vendor name. See 7.3.7. */ +#define DSC_ENTITY_MODEL_VENDOR_NAME_STRING 2 +/* IEEE 1722.1-2021, Table 7-2 - ENTITY Descriptor + * The localized string reference pointing to the + * localized model name. See 7.3.7. */ +#define DSC_ENTITY_MODEL_MODEL_NAME_STRING 0 +#define DSC_ENTITY_MODEL_FIRMWARE_VERSION "0.3.48" +#define DSC_ENTITY_MODEL_GROUP_NAME DSC_STRINGS_3_GROUP_NAME +#define DSC_ENTITY_MODEL_SERIAL_NUMBER "0xBEBEDEAD" +#define DSC_ENTITY_MODEL_CONFIGURATIONS_COUNT 2 +#define DSC_ENTITY_MODEL_CURRENT_CONFIGURATION 0 + +/**************************************************************************************/ +/* IEEE 1722.1-2021, Sec. 7.2.2 - CONFIGURATION Descriptor*/ +/* Milan v1.2, Sec. 5.3.3.2 */ +#define DSC_CONFIGURATION_DESCRIPTOR_COUNTS_COUNT 8 +#define DSC_CONFIGURATION_OBJECT_NAME DSC_STRINGS_1_CONFIGURATION_NAME +/* IEEE 1722.1-2021, Table 7-3 CONFIGURATION Descriptor + * The localized string reference pointing to the + * localized Configuration name. */ +#define DSC_CONFIGURATION_LOCALIZED_DESCRIPTION 1 +/* IEEE 1722.1-2021, Table 7-3 CONFIGURATION Descriptor + * The offset to the descriptor_counts field from the + * start of the descriptor. This field is set to 74 for + * this version of AEM. */ +#define DSC_CONFIGURATION_DESCRIPTOR_COUNTS_OFFSET 74 + +#define DSC_CONFIGURATION_NO_OF_AUDIO_UNITS 1 +#define DSC_CONFIGURATION_NO_OF_STREAM_INPUTS 2 + +#define DSC_CONFIGURATION_NO_OF_STREAM_OUTPUTS 1 + +#define DSC_CONFIGURATION_NO_OF_AVB_INTERFACES 1 +#define DSC_CONFIGURATION_NO_OF_CLOCK_DOMAINS 1 +#define DSC_CONFIGURATION_NO_OF_CLOCK_SOURCES 3 +#define DSC_CONFIGURATION_NO_OF_CONTROLS 1 +#define DSC_CONFIGURATION_NO_OF_LOCALES 1 + +/**************************************************************************************/ +/* IEEE 1722.1-2021, Sec. 7.2.22 CONTROL Descriptor*/ +/* Milan v1.2, Sec. 5.3.3.10 */ + +#define DSC_CONTROL_OBJECT_NAME "Identify" +#define DSC_CONTROL_LOCALIZED_DESCRIPTION AVB_AEM_DESC_INVALID +#define DSC_CONTROL_BLOCK_LATENCY 500 +#define DSC_CONTROL_CONTROL_LATENCY 500 +#define DSC_CONTROL_CONTROL_DOMAIN 0 +#define DSC_CONTROL_CONTROL_VALUE_TYPE AECP_AEM_CTRL_LINEAR_UINT8 +#define DSC_CONTROL_CONTROL_TYPE AEM_CTRL_TYPE_IDENTIFY +/* IEEE 1722.1-2021, Table 7-39 - CONTROL Descriptor + * The time period in microseconds from when a control + * is set with the SET_CONTROL command till it automatically + * resets to its default values. + * When this is set to zero (0) automatic resets do not happen. */ +// TODO: Milan v1.2: The PAAD remains in identification mode until the value of the “IDENTIFY” CONTROL descriptor is set back to 0. +#define DSC_CONTROL_RESET_TIME 3 +#define DSC_CONTROL_NUMBER_OF_VALUES 1 +#define DSC_CONTROL_SIGNAL_TYPE AVB_AEM_DESC_INVALID +#define DSC_CONTROL_SIGNAL_INDEX 0 +#define DSC_CONTROL_SIGNAL_OUTPUT 0 + +#define DSC_CONTROL_IDENTIFY_MIN 0 +#define DSC_CONTROL_IDENTIFY_MAX 255 +#define DSC_CONTROL_IDENTIFY_STEP 255 +#define DSC_CONTROL_IDENTIFY_DEFAULT_VALUE 0 +#define DSC_CONTROL_IDENTIFY_CURRENT_VALUE 0 +#define DSC_CONTROL_LOCALIZED_DESCRIPTION AVB_AEM_DESC_INVALID + +/**************************************************************************************/ +/* IEEE 1722.1-2021, Sec. 7.2.19 AUDIO_MAP Descriptor */ +/* Milan v1.2, Sec. 5.3.3.9 */ + +// TODO: Prepared for for loop over total number of audio maps +#define DSC_AUDIO_MAPS_TOTAL_NO_OF_MAPS 2 + +#define DSC_AUDIO_MAPS_NO_OF_MAPPINGS 8 +#define DSC_AUDIO_MAPS_MAPPING_STREAM_INDEX 0 +#define DSC_AUDIO_MAPS_MAPPING_CLUSTER_CHANNEL 0 + +/**************************************************************************************/ +/* IEEE 1722.1-2021, Sec. 7.2.16 AUDIO_CLUSTER Descriptor */ +/* Milan v1.2, Sec. 5.3.3.8 */ +#define DSC_AUDIO_CLUSTER_NO_OF_CLUSTERS 16 +#define DSC_AUDIO_CLUSTER_OBJECT_NAME_LEN_IN_OCTET 64 +#define DSC_AUDIO_CLUSTER_OBJECT_NAME_INPUT "Input" +#define DSC_AUDIO_CLUSTER_OBJECT_NAME_OUTPUT "Output" + +#define DSC_AUDIO_CLUSTER_LOCALIZED_DESCRIPTION AVB_AEM_DESC_INVALID + +/* The signal_type and signal_index fields indicate the + * object providing the signal destined for the channels + * of the streams mapped to the port. For a signal which + * is sourced internally from the Unit, the signal_type + * is set to AUDIO_UNIT and signal_index is set to the + * index of the Unit. For a Cluster attached to a + * STREAM_PORT_INPUT the signal_type and signal_index + * fields is set to INVALID and zero (0) respectively. */ +#define DSC_AUDIO_CLUSTER_SIGNAL_TYPE 0 +#define DSC_AUDIO_CLUSTER_SIGNAL_INDEX 0 +/* The index of the output of the signal source of the + * cluster. For a signal_type of SIGNAL_SPLITTER or + * SIGNAL_DEMULTIPLEXER this is which output of the + * object it is being sourced from, for a signal_type + * of MATRIX this is the column the signal is from + * and for any other signal_type this is zero (0). */ +#define DSC_AUDIO_CLUSTER_SIGNAL_OUTPUT 0 +#define DSC_AUDIO_CLUSTER_PATH_LATENCY_IN_NS 500 +#define DSC_AUDIO_CLUSTER_BLOCK_LATENCY_IN_NS 500 +#define DSC_AUDIO_CLUSTER_CHANNEL_COUNT 1 +#define DSC_AUDIO_CLUSTER_FORMAT AVB_AEM_AUDIO_CLUSTER_TYPE_MBLA +#define DSC_AUDIO_CLUSTER_AES3_DATA_TYPE_REF 0 +#define DSC_AUDIO_CLUSTER_AES3_DATA_TYPE 0 + +/**************************************************************************************/ +/* IEEE 1722.1-2021, Sec. 7.2.13 STREAM_PORT_INPUT Descriptor */ +/* Milan v1.2, Sec. 5.3.3.7 */ + +#define DSC_STREAM_PORT_INPUT_CLOCK_DOMAIN_INDEX 0x0000 +#define DSC_STREAM_PORT_INPUT_PORT_FLAGS AVB_AEM_PORT_FLAG_CLOCK_SYNC_SOURCE +/* The number of clusters within the Port. This corresponds to the number of + * AUDIO_CLUSTER, VIDEO_CLUSTER or SENSOR_CLUSTER descriptors which represent + * these clusters. */ +// TODO: Validate value +#define DSC_STREAM_PORT_INPUT_NUMBER_OF_CONTROLS 0 +#define DSC_STREAM_PORT_INPUT_BASE_CONTROL 0 +// TODO: Validate value +#define DSC_STREAM_PORT_INPUT_NUMBER_OF_CLUSTERS 8
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/es-builder.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/es-builder.c
Changed
@@ -4,7 +4,8 @@ #include "es-builder.h" -#include "aecp-aem-descriptors.h" +#include "aecp-aem-state.h" +#include "utils.h" /** * \brief The goal of this modules is to create a an entity and @@ -31,9 +32,124 @@ es_builder_cb_t build_descriptor_cb; }; -/** All callback that needs a status information */ -static const struct es_builder_st es_builderAVB_AEM_DESC_LAST_RESERVED_17221 = +/* + * \brief The Entity keeps track of multiple things, the locks the current + * configuration use for instance. That tragets the Milan V1.2 mode only + */ +static void *es_builder_desc_entity_milan_v12(struct server *server, + uint16_t type, uint16_t index, size_t size, void *ptr) +{ + struct aecp_aem_entity_milan_state entity_state = {0}; + void *ptr_alloc; + struct aecp_aem_entity_state *state = + (struct aecp_aem_entity_state *) &entity_state; + + memcpy(&state->desc, ptr, size); + + ptr_alloc = server_add_descriptor(server, type, index, sizeof(entity_state), + &entity_state); + + if (!ptr_alloc) { + pw_log_error("Error durring allocation\n"); + spa_assert(0); + } + + return ptr_alloc; +} + +/** + * \brief A generic function to avoid code duplicate for the streams */ +static void *es_buidler_desc_stream_general_prepare(struct server *server, + uint16_t type, uint16_t index, size_t size, void *ptr) +{ + void *ptr_alloc; + struct stream *stream; + enum spa_direction direction; + + switch (type) { + case AVB_AEM_DESC_STREAM_INPUT: + struct aecp_aem_stream_input_state *pstream_input; + struct aecp_aem_stream_input_state stream_input = { 0 }; + + memcpy(&stream_input.desc, ptr, size); + ptr_alloc = server_add_descriptor(server, type, index, + sizeof(stream_input), &stream_input); + if (!ptr_alloc) { + pw_log_error("Allocation failed\n"); + return NULL; + } + + pstream_input = ptr_alloc; + stream = &pstream_input->stream; + direction = SPA_DIRECTION_INPUT; + break; + case AVB_AEM_DESC_STREAM_OUTPUT: + struct aecp_aem_stream_output_state *pstream_output; + struct aecp_aem_stream_output_state stream_output = { 0 }; + + memcpy(&stream_output.desc, ptr, size); + ptr_alloc = server_add_descriptor(server, type, index, + sizeof(stream_output), &stream_output); + if (!ptr_alloc) { + pw_log_error("Allocation failed\n"); + return NULL; + } + + pstream_output = ptr_alloc; + stream = &pstream_output->stream; + direction = SPA_DIRECTION_OUTPUT; + + break; + default: + pw_log_error("Only STREAM_INPUT and STREAM_OUTPUT\n"); + return NULL; + } + + if (!server_create_stream(server, stream, direction, index)) { + pw_log_error("Could not create/initialize a stream"); + return NULL; + } + + return ptr_alloc; +} + + +// Assign a ID to an specific builder +#define HELPER_ES_BUIDLER(type, callback) \ + type = { .build_descriptor_cb = callback } + +/** All callback that needs a status information for the AVB/Milan V1.2 */ +static const struct es_builder_st es_builder_milan_v12 = { + HELPER_ES_BUIDLER(AVB_AEM_DESC_ENTITY, es_builder_desc_entity_milan_v12), + HELPER_ES_BUIDLER(AVB_AEM_DESC_STREAM_OUTPUT, es_buidler_desc_stream_general_prepare), + HELPER_ES_BUIDLER(AVB_AEM_DESC_STREAM_INPUT, es_buidler_desc_stream_general_prepare), +}; + +/** All callback that needs a status information for Legacy AVB*/ +static const struct es_builder_st es_builder_legacy_avb = +{ + HELPER_ES_BUIDLER(AVB_AEM_DESC_STREAM_OUTPUT, es_buidler_desc_stream_general_prepare), + HELPER_ES_BUIDLER(AVB_AEM_DESC_STREAM_INPUT, es_buidler_desc_stream_general_prepare), +}; + +/** + * \brief keep the list of the supported avb flavors here + */ +static const struct { + const struct es_builder_st *es_builder; + /** Number of elements in the es_builder */ + size_t count; +} es_builders = { + AVB_MODE_LEGACY = { + .es_builder = es_builder_legacy_avb, + .count = ARRAY_SIZE(es_builder_legacy_avb), + }, + + AVB_MODE_MILAN_V12 = { + .es_builder = es_builder_milan_v12, + .count = ARRAY_SIZE(es_builder_milan_v12), + }, }; /** @@ -44,24 +160,37 @@ void es_builder_add_descriptor(struct server *server, uint16_t type, uint16_t index, size_t size, void *ptr_aem) { + const struct es_builder_st *es_builder; void *desc_ptr; struct descriptor *d; + enum avb_mode avb_mode; + bool std_processing = false; if (!server) { pw_log_error("Invalid server, it is empty %p\n", server); spa_assert(0); } - if (type >= AVB_AEM_DESC_LAST_RESERVED_17221) { - pw_log_error("Invalid Type %u\n", type); + avb_mode = server->avb_mode; + if (avb_mode >= AVB_MODE_MAX) { + pw_log_error("AVB mode is not valid received %d\n", avb_mode); spa_assert(0); } - /* Look if the descriptor has a callback to attach more status data */ - if (!es_buildertype.build_descriptor_cb) { + + es_builder = es_buildersavb_mode.es_builder; + if (type > es_buildersavb_mode.count) { + std_processing = true; + } else { + if (!es_buildertype.build_descriptor_cb) { + std_processing = true; + } + } + + if (std_processing) { if (!server_add_descriptor(server, type, index, size, ptr_aem)) { pw_log_error("Could not allocate descriptor %u at " - "index %u the avb aem type\n", type, index); + "index %u the avb aem type\n", type, index); spa_assert(0); } @@ -75,6 +204,7 @@ spa_assert(0); } + d = (struct descriptor *) desc_ptr; d->size = size; }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/internal.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/internal.h
Changed
@@ -52,11 +52,27 @@ void *ptr; }; + +enum avb_mode { + /** The legacy AVB Mode */ + AVB_MODE_LEGACY, + /** + * \brief Milan version 1.2, which subset of the AVB, + * \see Milan Specifications https://avnu.org/resource/milan-specification/ + */ + AVB_MODE_MILAN_V12, + + /** Future AVB mode will be added here if necessary */ + AVB_MODE_MAX +}; + struct server { struct spa_list link; struct impl *impl; char *ifname; + /** Parsed from the configuration pipewire-avb.conf */ + enum avb_mode avb_mode; uint8_t mac_addr6; uint64_t entity_id; int ifindex; @@ -67,7 +83,6 @@ struct spa_hook_list listener_list; struct spa_list descriptors; - struct spa_list streams; unsigned debug_messages:1; @@ -82,7 +97,18 @@ #include "stream.h" -static inline const struct descriptor *server_find_descriptor(struct server *server, +static inline void server_destroy_descriptors(struct server *server) +{ + struct descriptor *d, *t; + + spa_list_for_each_safe(d, t, &server->descriptors, link) { + free(d->ptr); + spa_list_remove(&d->link); + free(d); + } +} + +static inline struct descriptor *server_find_descriptor(struct server *server, uint16_t type, uint16_t index) { struct descriptor *d; @@ -108,20 +134,10 @@ if (ptr) memcpy(d->ptr, ptr, size); spa_list_append(&server->descriptors, &d->link); - return d->ptr; + return d; } -static inline struct stream *server_find_stream(struct server *server, - enum spa_direction direction, uint16_t index) -{ - struct stream *s; - spa_list_for_each(s, &server->streams, link) { - if (s->direction == direction && - s->index == index) - return s; - } - return NULL; -} +const char *get_avb_mode_str(enum avb_mode mode); struct server *avdecc_server_new(struct impl *impl, struct spa_dict *props); void avdecc_server_free(struct server *server);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/mrp.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/mrp.c
Changed
@@ -60,7 +60,14 @@ static void mrp_destroy(void *data) { struct mrp *mrp = data; + struct attribute *a, *t; spa_hook_remove(&mrp->server_listener); + + spa_list_for_each_safe(a, t, &mrp->attributes, link) { + spa_list_remove(&a->link); + free(a); + } + free(mrp); }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/stream.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/stream.c
Changed
@@ -15,8 +15,8 @@ #include "iec61883.h" #include "stream.h" +#include "aecp-aem-state.h" #include "utils.h" -#include "aecp-aem-descriptors.h" static void on_stream_destroy(void *d) { @@ -235,34 +235,17 @@ .process = on_sink_stream_process }; -struct stream *server_create_stream(struct server *server, +struct stream *server_create_stream(struct server *server, struct stream *stream, enum spa_direction direction, uint16_t index) { - struct stream *stream; - const struct descriptor *desc; uint32_t n_params; const struct spa_pod *params1; uint8_t buffer1024; struct spa_pod_builder b; int res; - desc = server_find_descriptor(server, - direction == SPA_DIRECTION_INPUT ? - AVB_AEM_DESC_STREAM_INPUT : - AVB_AEM_DESC_STREAM_OUTPUT, index); - if (desc == NULL) - return NULL; - - stream = calloc(1, sizeof(*stream)); - if (stream == NULL) - return NULL; - stream->server = server; stream->direction = direction; - stream->index = index; - stream->desc = desc; - spa_list_append(&server->streams, &stream->link); - stream->prio = AVB_MSRP_PRIORITY_DEFAULT; stream->vlan_id = AVB_DEFAULT_VLAN; @@ -361,8 +344,6 @@ void stream_destroy(struct stream *stream) { avb_mrp_attribute_destroy(stream->listener_attr->mrp); - spa_list_remove(&stream->link); - free(stream); } static int setup_socket(struct stream *stream) @@ -496,7 +477,7 @@ } } -int stream_activate(struct stream *stream, uint64_t now) +int stream_activate(struct stream *stream, uint16_t index, uint64_t now) { struct server *server = stream->server; struct avb_frame_header *h = (void*)stream->pdu; @@ -528,7 +509,7 @@ stream->talker_attr->attr.talker.stream_id = htobe64(stream->peer_id); avb_mrp_attribute_begin(stream->talker_attr->mrp, now); } else { - if ((res = avb_maap_get_address(server->maap, stream->addr, stream->index)) < 0) + if ((res = avb_maap_get_address(server->maap, stream->addr, index)) < 0) return res; stream->listener_attr->attr.listener.stream_id = htobe64(stream->id);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/stream.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/stream.h
Changed
@@ -24,8 +24,6 @@ struct server *server; uint16_t direction; - uint16_t index; - const struct descriptor *desc; uint64_t id; uint64_t peer_id; @@ -73,12 +71,12 @@ #include "mvrp.h" #include "maap.h" -struct stream *server_create_stream(struct server *server, +struct stream *server_create_stream(struct server *server, struct stream *stream, enum spa_direction direction, uint16_t index); void stream_destroy(struct stream *stream); -int stream_activate(struct stream *stream, uint64_t now); +int stream_activate(struct stream *stream, uint16_t index, uint64_t now); int stream_deactivate(struct stream *stream, uint64_t now); #endif /* AVB_STREAM_H */
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-avb/utils.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-avb/utils.h
Changed
@@ -9,6 +9,8 @@ #include "internal.h" +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x0)) + static inline char *avb_utils_format_id(char *str, size_t size, const uint64_t id) { snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x:%04x",
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-client-node/client-node.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-client-node/client-node.c
Changed
@@ -894,6 +894,7 @@ case SPA_DATA_MemPtr: spa_log_debug(impl->log, "mem %d %zd", j, SPA_PTRDIFF(d->data, baseptr)); b->datasj.data = SPA_INT_TO_PTR(SPA_PTRDIFF(d->data, baseptr)); + SPA_FLAG_CLEAR(b->datasj.flags, SPA_DATA_FLAG_MAPPABLE); break; default: b->datasj.type = SPA_ID_INVALID;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-client-node/remote-node.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-client-node/remote-node.c
Changed
@@ -735,7 +735,7 @@ static int client_node_port_set_io(void *_data, - uint32_t direction, + enum spa_direction direction, uint32_t port_id, uint32_t mix_id, uint32_t id,
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-filter-chain.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-filter-chain.c
Changed
@@ -193,6 +193,10 @@ * graph will then be duplicated as many times to match the number of input/output * channels of the streams. * + * If the graph has no inputs and the capture channels is set as 0, only the + * playback stream will be created. Likewise, if there are no outputs and the + * playback channels is 0, there will be no capture stream created. + * * ### Volumes * * Normally the volume of the sink/source is handled by the stream software volume. @@ -420,11 +424,12 @@ * * ### Delay * - * The delay can be used to delay a signal in time. + * The delay can be used to delay a signal in time. With the Feedback and Feedforward + * controls it can also be used as a comb and an allpass filter. * * The delay has an input port "In" and an output port "Out". It also has - * a "Delay (s)" control port. It requires a config section in the node declaration - * in this format: + * a "Delay (s)" control port and a "Feedback" and "Feedforward" port. It requires a + * config section in the node declaration in this format: * *\code{.unparsed} * filter.graph = { @@ -439,6 +444,8 @@ * } * control = { * "Delay (s)" = ... + * "Feedback" = ... + * "Feedforward" = ... * } * ... * } @@ -452,6 +459,10 @@ * - `latency` the latency in seconds. This is 0 by default but in some cases * the delay can be used to introduce latency with this option. * + * With the "Feedback" port one can create a comb filter. With the "Feedback" + * port and "Feedforward" port set to A and -A respectively, one can create + * an allpass filter. These settings can be used to create custom reverb units. + * * ### Invert * * The invert plugin can be used to invert the phase of the signal. @@ -645,6 +656,40 @@ * of "Attack (s)" seconds. The noise gate stays open for at least "Hold (s)" * seconds before it can close again. * + * ### Busy + * + * The `busy` plugin has no input or output ports and it can be used to keep the + * CPU or graph busy for the given percent of time. + * + * The node requires a `config` section with extra configuration: + * + *\code{.unparsed} + * filter.graph = { + * nodes = + * { + * type = builtin + * name = ... + * label = busy + * config = { + * wait-percent = 0.0 + * cpu-percent = 50.0 + * } + * ... + * } + * } + * ... + * } + *\endcode + * + * - `wait-percent` the percentage of time to wait. This keeps the graph busy but + * not the CPU. Default 0.0 + * - `cpu-percent` the percentage of time to keep the CPU busy. This keeps both the + * graph and CPU busy. Default 0.0 + * + * ### Null + * + * The `null` plugin has one data input "In" and one control input "Control" that + * simply discards the data. * * ## SOFA filters * @@ -672,6 +717,7 @@ * blocksize = ... * tailsize = ... * filename = ... + * gain = ... * } * control = { * "Azimuth" = ... @@ -688,9 +734,10 @@ * - `blocksize` specifies the size of the blocks to use in the FFT. It is a value * between 64 and 256. When not specified, this value is * computed automatically from the number of samples in the file. - * - `tailsize` specifies the size of the tail blocks to use in the FFT. - * - `filename` The SOFA file to load. SOFA files usually end in the .sofa extension - * and contain the HRTF for the various spatial positions. + * - `tailsize` specifies the size of the tail blocks to use in the FFT. + * - `filename` The SOFA file to load. SOFA files usually end in the .sofa extension + * and contain the HRTF for the various spatial positions. + * - `gain` the overall gain to apply to the IR file. * * - `Azimuth` controls the azimuth, this is the direction the sound is coming from * in degrees between 0 and 360. 0 is straight ahead. 90 is left, 180 @@ -1208,94 +1255,107 @@ impl->capture = NULL; } -static void capture_process(void *d) +static void do_process(struct impl *impl) { - struct impl *impl = d; - int res; - if ((res = pw_stream_trigger_process(impl->playback)) < 0) { - pw_log_debug("playback trigger error: %s", spa_strerror(res)); - while (true) { - struct pw_buffer *t; - if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL) - break; - /* playback part is not ready, consume, discard and recycle - * the capture buffers */ - pw_stream_queue_buffer(impl->capture, t); - } - } -} - -static void playback_process(void *d) -{ - struct impl *impl = d; struct pw_buffer *in, *out; - uint32_t i, data_size = 0; - int32_t stride = 0; + uint32_t i, n_in = 0, n_out = 0, data_size = 0; struct spa_data *bd; const void *cin128; void *cout128; - in = NULL; - while (true) { - struct pw_buffer *t; - if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL) - break; - if (in) - pw_stream_queue_buffer(impl->capture, in); - in = t; - } - if (in == NULL) - pw_log_debug("%p: out of capture buffers: %m", impl); - - if ((out = pw_stream_dequeue_buffer(impl->playback)) == NULL) - pw_log_debug("%p: out of playback buffers: %m", impl); - - if (in == NULL || out == NULL) - goto done; - - for (i = 0; i < in->buffer->n_datas; i++) { - uint32_t offs, size; + in = out = NULL; + if (impl->capture) { + while (true) { + struct pw_buffer *t; + if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL) + break; + if (in) + pw_stream_queue_buffer(impl->capture, in); + in = t; + } + if (in == NULL) { + pw_log_debug("%p: out of capture buffers: %m", impl); + } else { + for (i = 0; i < in->buffer->n_datas; i++) { + uint32_t offs, size; - bd = &in->buffer->datasi; + bd = &in->buffer->datasi; - offs = SPA_MIN(bd->chunk->offset, bd->maxsize); - size = SPA_MIN(bd->chunk->size, bd->maxsize - offs); + offs = SPA_MIN(bd->chunk->offset, bd->maxsize); + size = SPA_MIN(bd->chunk->size, bd->maxsize - offs); - cini = SPA_PTROFF(bd->data, offs, void); + cinn_in++ = SPA_PTROFF(bd->data, offs, void); - data_size = i == 0 ? size : SPA_MIN(data_size, size); - stride = SPA_MAX(stride, bd->chunk->stride); + data_size = i == 0 ? size : SPA_MIN(data_size, size); + } + } } - for (; i < impl->n_inputs; i++) - cini = NULL; + if (impl->playback) { + out = pw_stream_dequeue_buffer(impl->playback);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-jack-tunnel.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-jack-tunnel.c
Changed
@@ -1157,11 +1157,13 @@ pw_properties_set(impl->sink.props, PW_KEY_MEDIA_CLASS, "Audio/Sink"); pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_DRIVER, "30001"); + pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_SESSION, "2001"); pw_properties_set(impl->sink.props, PW_KEY_NODE_NAME, "jack_sink"); pw_properties_set(impl->sink.props, PW_KEY_NODE_DESCRIPTION, "JACK Sink"); pw_properties_set(impl->source.props, PW_KEY_MEDIA_CLASS, "Audio/Source"); pw_properties_set(impl->source.props, PW_KEY_PRIORITY_DRIVER, "30000"); + pw_properties_set(impl->source.props, PW_KEY_PRIORITY_SESSION, "2000"); pw_properties_set(impl->source.props, PW_KEY_NODE_NAME, "jack_source"); pw_properties_set(impl->source.props, PW_KEY_NODE_DESCRIPTION, "JACK Source");
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-netjack2-driver.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-netjack2-driver.c
Changed
@@ -1319,9 +1319,11 @@ pw_properties_set(props, PW_KEY_NODE_ALWAYS_PROCESS, "true"); pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_DRIVER, "40000"); + pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_SESSION, "2000"); pw_properties_set(impl->sink.props, PW_KEY_NODE_NAME, "netjack2_driver_send"); pw_properties_set(impl->source.props, PW_KEY_PRIORITY_DRIVER, "40001"); + pw_properties_set(impl->source.props, PW_KEY_PRIORITY_SESSION, "2001"); pw_properties_set(impl->source.props, PW_KEY_NODE_NAME, "netjack2_driver_receive"); if ((str = pw_properties_get(props, "sink.props")) != NULL)
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-parametric-equalizer.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-parametric-equalizer.c
Changed
@@ -289,6 +289,9 @@ static void impl_destroy(struct impl *impl) { + if (impl->eq_module) + pw_impl_module_destroy(impl->eq_module); + if (impl->core && impl->do_disconnect) pw_core_disconnect(impl->core); pw_properties_free(impl->props);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-profiler.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-profiler.c
Changed
@@ -114,6 +114,8 @@ uint8_t tmpTMP_BUFFER; uint8_t dataDATA_BUFFER; + uint64_t last_profile_time; + unsigned enabled:1; }; @@ -139,7 +141,6 @@ size_t flush_size; uint32_t interval; - uint64_t last_signal_time; }; struct resource_data { @@ -220,10 +221,10 @@ if (SPA_FLAG_IS_SET(pos->clock.flags, SPA_IO_CLOCK_FLAG_FREEWHEEL)) return; - if (a->signal_time - impl->last_signal_time < impl->interval) + if (a->signal_time - n->last_profile_time < impl->interval) goto done; - impl->last_signal_time = a->signal_time; + n->last_profile_time = a->signal_time; spa_pod_builder_init(&b, n->tmp, sizeof(n->tmp)); spa_pod_builder_push_object(&b, &f0, @@ -546,7 +547,6 @@ impl->interval = SPA_NSEC_PER_MSEC * pw_properties_get_uint32(props, "profile.interval.ms", DEFAULT_INTERVAL); - impl->last_signal_time = 0; impl->global = pw_global_new(context, PW_TYPE_INTERFACE_Profiler,
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-native.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-native.c
Changed
@@ -34,10 +34,6 @@ #include <spa/utils/json.h> #include <spa/debug/log.h> -#ifdef HAVE_SYSTEMD -#include <systemd/sd-daemon.h> -#endif - #ifdef HAVE_SELINUX #include <selinux/selinux.h> #endif @@ -45,6 +41,7 @@ #include <pipewire/impl.h> #include <pipewire/extensions/protocol-native.h> +#include "network-utils.h" #include "pipewire/private.h" #include "modules/module-protocol-native/connection.h" @@ -909,13 +906,12 @@ int fd = -1, res; bool activated = false; -#ifdef HAVE_SYSTEMD { - int i, n = sd_listen_fds(0); + int i, n = listen_fd(); for (i = 0; i < n; ++i) { - if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM, - 1, s->addr.sun_path, 0) > 0) { - fd = SD_LISTEN_FDS_START + i; + if (is_socket_unix(LISTEN_FDS_START + i, SOCK_STREAM, + s->addr.sun_path) > 0) { + fd = LISTEN_FDS_START + i; activated = true; pw_log_info("server %p: Found socket activation socket for '%s'", s, s->addr.sun_path); @@ -923,7 +919,6 @@ } } } -#endif if (fd < 0) { struct stat socket_stat;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/client.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/client.h
Changed
@@ -60,6 +60,15 @@ struct pw_manager_object *metadata_routes; struct pw_properties *routes; + struct pw_manager_object *metadata_schema_sm_settings; + bool have_force_mono_audio; + bool default_force_mono_audio; + bool have_bluetooth_headset_autoswitch; + bool default_bluetooth_headset_autoswitch; + struct pw_manager_object *metadata_sm_settings; + bool force_mono_audio; + bool bluetooth_headset_autoswitch; + uint32_t connect_tag; uint32_t in_index;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/defs.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/defs.h
Changed
@@ -39,6 +39,8 @@ #define MODULE_INDEX_MASK 0xfffffffu #define MODULE_FLAG (1u << 29) +#define STREAM_CREATE_TIMEOUT (35 * SPA_NSEC_PER_SEC) + #define DEFAULT_SINK "@DEFAULT_SINK@" #define DEFAULT_SOURCE "@DEFAULT_SOURCE@" #define DEFAULT_MONITOR "@DEFAULT_MONITOR@" @@ -323,5 +325,7 @@ #define METADATA_CONFIG_DEFAULT_SOURCE "default.configured.audio.source" #define METADATA_TARGET_NODE "target.node" #define METADATA_TARGET_OBJECT "target.object" +#define METADATA_FEATURES_AUDIO_MONO "node.features.audio.mono" +#define METADATA_BLUETOOTH_HEADSET_AUTOSWITCH "bluetooth.autoswitch-to-headset-profile" #endif /* PULSE_SERVER_DEFS_H */
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/format.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/format.h
Changed
@@ -11,7 +11,7 @@ struct spa_pod; struct spa_pod_builder; -#define RATE_MAX (48000u*16u) +#define RATE_MAX (48000u*32u) #define CHANNELS_MAX (64u) enum sample_format {
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/manager.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/manager.c
Changed
@@ -718,7 +718,7 @@ { struct manager *m = data; - if (id == PW_ID_CORE && res == -EPIPE) { + if (id == PW_ID_CORE && (res == -EPIPE || res == -EPROTO)) { pw_log_debug("connection error: %d, %s", res, message); manager_emit_disconnect(m); }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/message-handler.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/message-handler.c
Changed
@@ -19,6 +19,7 @@ #include "client.h" #include "collect.h" +#include "defs.h" #include "log.h" #include "manager.h" #include "module.h" @@ -89,6 +90,91 @@ return 0; } + +static int core_object_force_mono_output(struct client *client, const char *params, FILE *response) +{ + if (!client->have_force_mono_audio) { + /* Not supported, return a null value to indicate that */ + fprintf(response, "null"); + return 0; + } + + if (!params || params0 == '\0') { + /* No parameter => query the current value */ + fprintf(response, "%s", client->force_mono_audio ? "true" : "false"); + return 0; + } else { + /* The caller is trying to set a value or clear with a null */ + int ret; + + if (spa_streq(params, "true")) { + ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, + METADATA_FEATURES_AUDIO_MONO, "Spa:String:JSON", "true"); + client->force_mono_audio = true; + } else if (spa_streq(params, "false")) { + ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, + METADATA_FEATURES_AUDIO_MONO, "Spa:String:JSON", "false"); + client->force_mono_audio = false; + } else if (spa_streq(params, "null")) { + ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, + METADATA_FEATURES_AUDIO_MONO, NULL, NULL); + client->force_mono_audio = client->default_force_mono_audio; + } else { + fprintf(response, "Value must be true, false, or null"); + return -EINVAL; + } + + if (ret < 0) + fprintf(response, "Could not set metadata: %s", spa_strerror(ret)); + else + fprintf(response, "%s", params); + + return ret; + } +} + +static int core_object_bluetooth_headset_autoswitch(struct client *client, const char *params, FILE *response) +{ + if (!client->have_bluetooth_headset_autoswitch) { + /* Not supported, return a null value to indicate that */ + fprintf(response, "null"); + return 0; + } + + if (!params || params0 == '\0') { + /* No parameter => query the current value */ + fprintf(response, "%s", client->bluetooth_headset_autoswitch ? "true" : "false"); + return 0; + } else { + /* The caller is trying to set a value or clear with a null */ + int ret; + + if (spa_streq(params, "true")) { + ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, + METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, "Spa:String:JSON", "true"); + client->bluetooth_headset_autoswitch = true; + } else if (spa_streq(params, "false")) { + ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, + METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, "Spa:String:JSON", "false"); + client->bluetooth_headset_autoswitch = false; + } else if (spa_streq(params, "null")) { + ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, + METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, NULL, NULL); + client->bluetooth_headset_autoswitch = client->default_bluetooth_headset_autoswitch; + } else { + fprintf(response, "Value must be true, false, or null"); + return -EINVAL; + } + + if (ret < 0) + fprintf(response, "Could not set metadata: %s", spa_strerror(ret)); + else + fprintf(response, "%s", params); + + return ret; + } +} + static int core_object_message_handler(struct client *client, struct pw_manager_object *o, const char *message, const char *params, FILE *response) { pw_log_debug(": core %p object message:'%s' params:'%s'", o, message, params); @@ -97,13 +183,15 @@ fprintf(response, "/core <command> <params>\n" "available commands:\n" - " help this help\n" - " list-handlers show available object handlers\n" - " pipewire-pulse:malloc-info show malloc_info\n" - " pipewire-pulse:malloc-trim run malloc_trim\n" - " pipewire-pulse:log-level update log level with <params>\n" - " pipewire-pulse:list-modules list all module names\n" - " pipewire-pulse:describe-module describe module info for <params>" + " help this help\n" + " list-handlers show available object handlers\n" + " pipewire-pulse:malloc-info show malloc_info\n" + " pipewire-pulse:malloc-trim run malloc_trim\n" + " pipewire-pulse:log-level update log level with <params>\n" + " pipewire-pulse:list-modules list all module names\n" + " pipewire-pulse:describe-module describe module info for <params>\n" + " pipewire-pulse:force-mono-output force mono mixdown on all hardware outputs\n" + " pipewire-pulse:bluetooth-headset-autoswitch use bluetooth headset mic if available" ); } else if (spa_streq(message, "list-handlers")) { bool first = true; @@ -164,6 +252,10 @@ } else { fprintf(response, "Failed to open module.\n"); } + } else if (spa_streq(message, "pipewire-pulse:force-mono-output")) { + return core_object_force_mono_output(client, params, response); + } else if (spa_streq(message, "pipewire-pulse:bluetooth-headset-autoswitch")) { + return core_object_bluetooth_headset_autoswitch(client, params, response); } else { return -ENOSYS; }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/modules/module-pipe-source.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/modules/module-pipe-source.c
Changed
@@ -172,6 +172,8 @@ pw_properties_set(stream_props, PW_KEY_NODE_DRIVER, "true"); if ((str = pw_properties_get(stream_props, PW_KEY_PRIORITY_DRIVER)) == NULL) pw_properties_set(stream_props, PW_KEY_PRIORITY_DRIVER, "50000"); + if ((str = pw_properties_get(stream_props, PW_KEY_PRIORITY_SESSION)) == NULL) + pw_properties_set(stream_props, PW_KEY_PRIORITY_SESSION, "2000"); d->module = module; d->stream_props = stream_props;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/pulse-server.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/pulse-server.c
Changed
@@ -404,6 +404,14 @@ if (client->metadata_routes == old) client->metadata_routes = new; } + else if (spa_streq(name, "sm-settings")) { + if (client->metadata_sm_settings == old) + client->metadata_sm_settings = new; + } + else if (spa_streq(name, "schema-sm-settings")) { + if (client->metadata_schema_sm_settings == old) + client->metadata_schema_sm_settings = new; + } } static uint32_t frac_to_bytes_round_up(struct spa_fraction val, const struct sample_spec *ss) @@ -964,6 +972,35 @@ } if (subject == PW_ID_CORE && o == client->metadata_routes) client_update_routes(client, key, value); + if (subject == PW_ID_CORE && o == client->metadata_schema_sm_settings) { + char default_16; + + if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO)) { + client->have_force_mono_audio = true; + + if (spa_json_str_object_find(value, strlen(value), + "default", default_, sizeof(default_)) < 0) + client->default_force_mono_audio = false; + else + client->default_force_mono_audio = spa_streq(default_, "true"); + } + + if (spa_streq(key, METADATA_BLUETOOTH_HEADSET_AUTOSWITCH)) { + client->have_bluetooth_headset_autoswitch = true; + + if (spa_json_str_object_find(value, strlen(value), + "default", default_, sizeof(default_)) < 0) + client->default_bluetooth_headset_autoswitch = false; + else + client->default_bluetooth_headset_autoswitch = spa_streq(default_, "true"); + } + } + if (subject == PW_ID_CORE && o == client->metadata_sm_settings) { + if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO)) + client->force_mono_audio = spa_streq(value, "true"); + if (spa_streq(key, METADATA_BLUETOOTH_HEADSET_AUTOSWITCH)) + client->bluetooth_headset_autoswitch = spa_streq(value, "true"); + } }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/sample-play.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/sample-play.c
Changed
@@ -17,10 +17,12 @@ #include <pipewire/properties.h> #include <pipewire/stream.h> +#include "defs.h" #include "format.h" #include "log.h" #include "sample.h" #include "sample-play.h" +#include "internal.h" static void sample_play_stream_state_changed(void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error) @@ -30,17 +32,32 @@ switch (state) { case PW_STREAM_STATE_UNCONNECTED: case PW_STREAM_STATE_ERROR: + pw_timer_queue_cancel(&p->timer); sample_play_emit_done(p, -EIO); break; case PW_STREAM_STATE_PAUSED: p->id = pw_stream_get_node_id(p->stream); sample_play_emit_ready(p, p->id); break; + case PW_STREAM_STATE_STREAMING: + pw_timer_queue_cancel(&p->timer); + break; default: break; } } +static void sample_play_start_timeout(void *user_data) +{ + struct sample_play *p = user_data; + + pw_log_info("timeout on sample %s", p->sample->name); + + if (p->stream) + pw_stream_set_active(p->stream, false); + sample_play_emit_done(p, -ETIMEDOUT); +} + static void sample_play_stream_destroy(void *data) { struct sample_play *p = data; @@ -163,6 +180,10 @@ if (res < 0) goto error_cleanup; + /* Time out if we don't get a link; same timeout as for normal streams */ + pw_timer_queue_add(sample->impl->timer_queue, &p->timer, NULL, + STREAM_CREATE_TIMEOUT, sample_play_start_timeout, p); + return p; error_cleanup: @@ -181,6 +202,8 @@ spa_hook_list_clean(&p->hooks); + pw_timer_queue_cancel(&p->timer); + free(p); }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/sample-play.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/sample-play.h
Changed
@@ -11,6 +11,8 @@ #include <spa/utils/list.h> #include <spa/utils/hook.h> +#include <pipewire/pipewire.h> + struct sample; struct pw_core; struct pw_loop; @@ -41,6 +43,7 @@ uint32_t offset; uint32_t stride; struct spa_hook_list hooks; + struct pw_timer timer; void *user_data; };
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/server.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/server.c
Changed
@@ -21,9 +21,6 @@ #include <netinet/ip.h> #include <unistd.h> -#ifdef HAVE_SYSTEMD -#include <systemd/sd-daemon.h> -#endif #include <spa/utils/cleanup.h> #include <spa/utils/defs.h> @@ -577,26 +574,19 @@ return false; } -#ifdef HAVE_SYSTEMD -static int check_systemd_activation(const char *path) +static int check_socket_activation(const char *path) { - const int n = sd_listen_fds(0); + const int n = listen_fd(); for (int i = 0; i < n; i++) { - const int fd = SD_LISTEN_FDS_START + i; + const int fd = LISTEN_FDS_START + i; - if (sd_is_socket_unix(fd, SOCK_STREAM, 1, path, 0) > 0) + if (is_socket_unix(fd, SOCK_STREAM, path) > 0) return fd; } return -1; } -#else -static inline int check_systemd_activation(SPA_UNUSED const char *path) -{ - return -1; -} -#endif static int start_unix_server(struct server *server, const struct sockaddr_storage *addr) { @@ -606,10 +596,10 @@ spa_assert(addr_un->sun_family == AF_UNIX); - fd = check_systemd_activation(addr_un->sun_path); + fd = check_socket_activation(addr_un->sun_path); if (fd >= 0) { server->activated = true; - pw_log_info("server %p: found systemd socket activation socket for '%s'", + pw_log_info("server %p: found socket activation socket for '%s'", server, addr_un->sun_path); goto done; }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-protocol-pulse/stream.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-protocol-pulse/stream.c
Changed
@@ -107,7 +107,7 @@ /* Time out if we don't get a link and can't send a reply to create in 35s. Client will time out in * 30s and clean up its stream anyway. */ pw_timer_queue_add(stream->impl->timer_queue, &stream->timer, NULL, - 35 * SPA_NSEC_PER_SEC, create_stream_timeout, stream); + STREAM_CREATE_TIMEOUT, create_stream_timeout, stream); return stream;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-raop-discover.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-raop-discover.c
Changed
@@ -384,10 +384,8 @@ } avahi_address_snprint(at, sizeof(at), a); - if (spa_strstartswith(at, link_local_range)) { - pw_log_info("found link-local ip address %s - skipping tunnel creation", at); - goto done; - } + if (spa_strstartswith(at, link_local_range)) + pw_log_info("found link-local ip address %s for '%s'", at, name); tinfo = TUNNEL_INFO(.name = name); @@ -414,6 +412,11 @@ (a->data.ipv6.address1 & 0xc0) == 0x80) snprintf(if_suffix, sizeof(if_suffix), "%%%d", interface); + /* For IPv4 link-local, bind to the discovery interface */ + if (a->proto == AVAHI_PROTO_INET && + spa_strstartswith(at, link_local_range)) + snprintf(if_suffix, sizeof(if_suffix), "%%%d", interface); + pw_properties_setf(props, "raop.ip", "%s%s", at, if_suffix); pw_properties_setf(props, "raop.ifindex", "%d", interface); pw_properties_setf(props, "raop.port", "%u", port);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-roc-sink.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-roc-sink.c
Changed
@@ -46,6 +46,9 @@ * - `remote.repair.port = <str>`: remote receiver TCP/UDP port for receiver packets * - `remote.control.port = <str>`: remote receiver TCP/UDP port for control packets * - `fec.code = <str>`: Possible values: `disable`, `rs8m`, `ldpc` + * - `log.level = <str>`: log level for roc-toolkit. Possible values: `DEFAULT`, + * `NONE`, `ERROR`, `INFO`, `DEBUG`, `TRACE`; `DEFAULT` follows the log + * level of the PipeWire context. * * ## General options * @@ -75,6 +78,7 @@ * node.name = "roc-sink" * } * audio.position = FL FR + * log.level = DEFAULT * } * } * @@ -84,8 +88,9 @@ #define NAME "roc-sink" -PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +PW_LOG_TOPIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic +PW_LOG_TOPIC_EXTERN(roc_log_topic); struct module_roc_sink_data { struct pw_impl_module *module; @@ -115,6 +120,8 @@ roc_endpoint *remote_control_addr; int remote_control_port; + + roc_log_level loglevel; }; static void stream_destroy(void *d) @@ -300,6 +307,8 @@ pw_properties_setf(data->capture_props, PW_KEY_NODE_RATE, "1/%d", info.rate); + pw_roc_log_init(); + res = roc_sender_open(data->context, &sender_config, &data->sender); if (res) { pw_log_error("failed to create roc sender: %d", res); @@ -382,7 +391,8 @@ "( remote.repair.port=<remote receiver port for repair packets> ) " "( remote.control.port=<remote receiver port for control packets> ) " "( audio.position=<channel map, default:"PW_ROC_STEREO_POSITIONS"> ) " - "( sink.props= { key=val ... } ) " }, + "( sink.props= { key=val ... } ) " + "( log.level=<empty>|DEFAULT|NONE|RROR|INFO|DEBUG|TRACE ) " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; @@ -396,6 +406,7 @@ int res = 0; PW_LOG_TOPIC_INIT(mod_topic); + PW_LOG_TOPIC_INIT(roc_log_topic); data = calloc(1, sizeof(struct module_roc_sink_data)); if (data == NULL) @@ -502,6 +513,14 @@ pw_log_error("can't connect: %m"); goto out; } + if ((str = pw_properties_get(props, "log.level")) != NULL) { + const struct spa_log *log_conf = pw_log_get(); + const roc_log_level default_level = pw_roc_log_level_pw_2_roc(log_conf->level); + if (pw_roc_parse_log_level(&data->loglevel, str, default_level)) { + pw_log_error("Invalid log level %s, using default", str); + data->loglevel = default_level; + } + } pw_proxy_add_listener((struct pw_proxy*)data->core, &data->core_proxy_listener,
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-roc-source.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-roc-source.c
Changed
@@ -56,6 +56,9 @@ * - `fec.code = <str>`: Possible values: `default`, `disable`, `rs8m`, `ldpc` * * - `resampler.profile = <str>`: Deprecated, use roc.resampler.profile + * - `log.level = <str>`: log level for roc-toolkit. Possible values: `DEFAULT`, + * `NONE`, `ERROR`, `INFO`, `DEBUG`, `TRACE`; `DEFAULT` follows the log + * level of the PipeWire context. * * ## General options * @@ -89,6 +92,7 @@ * node.name = "roc-source" * } * audio.position = FL FR + * log.level = DEFAULT * } * } * @@ -98,8 +102,9 @@ #define NAME "roc-source" -PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +PW_LOG_TOPIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic +PW_LOG_TOPIC_EXTERN(roc_log_topic); struct module_roc_source_data { struct pw_impl_module *module; @@ -135,6 +140,8 @@ roc_endpoint *local_control_addr; int local_control_port; + + roc_log_level loglevel; }; static void stream_destroy(void *d) @@ -333,6 +340,8 @@ */ receiver_config.target_latency = (unsigned long long)data->sess_latency_msec * SPA_NSEC_PER_MSEC; + pw_roc_log_init(); + res = roc_receiver_open(data->context, &receiver_config, &data->receiver); if (res) { pw_log_error("failed to create roc receiver: %d", res); @@ -421,7 +430,8 @@ "( local.repair.port=<local receiver port for repair packets> ) " "( local.control.port=<local receiver port for control packets> ) " "( audio.position=<channel map, default:"PW_ROC_STEREO_POSITIONS"> ) " - "( source.props= { key=value ... } ) " }, + "( source.props= { key=value ... } ) " + "( log.level=<empty>|DEFAULT|NONE|RROR|INFO|DEBUG|TRACE ) " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; @@ -557,6 +567,14 @@ } else { data->fec_code = ROC_FEC_ENCODING_DEFAULT; } + if ((str = pw_properties_get(props, "log.level")) != NULL) { + const struct spa_log *log_conf = pw_log_get(); + const roc_log_level default_level = pw_roc_log_level_pw_2_roc(log_conf->level); + if (pw_roc_parse_log_level(&data->loglevel, str, default_level)) { + pw_log_error("Invalid log level %s, using default", str); + data->loglevel = default_level; + } + } data->core = pw_context_get_object(data->module_context, PW_TYPE_INTERFACE_Core); if (data->core == NULL) {
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-roc/common.c
Added
@@ -0,0 +1,21 @@ +#include <pipewire/log.h> +#include <roc/log.h> + +#include "common.h" + +PW_LOG_TOPIC(roc_log_topic, "mod.roc.lib"); + +void pw_roc_log_init(void) +{ + roc_log_set_handler(pw_roc_log_handler, NULL); + roc_log_set_level(pw_roc_log_level_pw_2_roc(roc_log_topic->has_custom_level ? roc_log_topic->level : pw_log_level)); +} + +void pw_roc_log_handler(const roc_log_message *message, void *argument) +{ + const enum spa_log_level log_level = pw_roc_log_level_roc_2_pw(message->level); + if (SPA_UNLIKELY(pw_log_topic_enabled(log_level, roc_log_topic))) { + pw_log_logt(log_level, roc_log_topic, message->file, message->line, message->module, message->text, ""); + } +} +
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-roc/common.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-roc/common.h
Changed
@@ -3,8 +3,10 @@ #include <roc/config.h> #include <roc/endpoint.h> +#include <roc/log.h> #include <spa/utils/string.h> +#include <spa/support/log.h> #define PW_ROC_DEFAULT_IP "0.0.0.0" #define PW_ROC_DEFAULT_SOURCE_PORT 10001 @@ -18,6 +20,9 @@ #define PW_ROC_MULTITRACK_ENCODING_ID 100 #define PW_ROC_STEREO_POSITIONS " FL FR " +void pw_roc_log_init(void); +void pw_roc_log_handler(const roc_log_message *message, void *argument); + static inline int pw_roc_parse_fec_encoding(roc_fec_encoding *out, const char *str) { if (!str || !*str || spa_streq(str, "default")) @@ -132,4 +137,62 @@ } } +static inline roc_log_level pw_roc_log_level_pw_2_roc(const enum spa_log_level pw_log_level) +{ + switch (pw_log_level) { + case SPA_LOG_LEVEL_NONE: + return ROC_LOG_NONE; + case SPA_LOG_LEVEL_ERROR: + return ROC_LOG_ERROR; + case SPA_LOG_LEVEL_WARN: + return ROC_LOG_ERROR; + case SPA_LOG_LEVEL_INFO: + return ROC_LOG_INFO; + case SPA_LOG_LEVEL_DEBUG: + return ROC_LOG_DEBUG; + case SPA_LOG_LEVEL_TRACE: + return ROC_LOG_TRACE; + default: + return ROC_LOG_NONE; + } +} + +static inline enum spa_log_level pw_roc_log_level_roc_2_pw(const roc_log_level roc_log_level) +{ + switch (roc_log_level) { + case ROC_LOG_NONE: + return SPA_LOG_LEVEL_NONE; + case ROC_LOG_ERROR: + return SPA_LOG_LEVEL_ERROR; + case ROC_LOG_INFO: + return SPA_LOG_LEVEL_INFO; + case ROC_LOG_DEBUG: + return SPA_LOG_LEVEL_DEBUG; + case ROC_LOG_TRACE: + return SPA_LOG_LEVEL_TRACE; + default: + return SPA_LOG_LEVEL_NONE; + } +} + +static inline int pw_roc_parse_log_level(roc_log_level *loglevel, const char *str, + roc_log_level default_level) +{ + if (spa_streq(str, "DEFAULT")) + *loglevel = default_level; + else if (spa_streq(str, "NONE")) + *loglevel = ROC_LOG_NONE; + else if (spa_streq(str, "ERROR")) + *loglevel = ROC_LOG_ERROR; + else if (spa_streq(str, "INFO")) + *loglevel = ROC_LOG_INFO; + else if (spa_streq(str, "DEBUG")) + *loglevel = ROC_LOG_DEBUG; + else if (spa_streq(str, "TRACE")) + *loglevel = ROC_LOG_TRACE; + else + return -EINVAL; + return 0; +} + #endif /* MODULE_ROC_COMMON_H */
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-rt.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-rt.c
Changed
@@ -1132,7 +1132,7 @@ } if (IS_VALID_NICE_LEVEL(impl->nice_level)) { - if (set_nice(impl, impl->nice_level, !can_use_rtkit) < 0) + if (set_nice(impl, impl->nice_level, !use_rtkit) < 0) use_rtkit = can_use_rtkit; } if (!use_rtkit)
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-rtp/audio.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-rtp/audio.c
Changed
@@ -22,6 +22,15 @@ memset(iov1.iov_base, 0, iov1.iov_len); } +static inline uint64_t scale_u64(uint64_t val, uint32_t num, uint32_t denom) +{ +#if 0 + return ((__uint128_t)val * num) / denom; +#else + return (uint64_t)((double)val / denom * num); +#endif +} + static void rtp_audio_process_playback(void *data) { struct impl *impl = data; @@ -61,6 +70,9 @@ * read or write index itself.) */ if (impl->direct_timestamp) { + uint32_t num_samples_to_read; + uint32_t read_index; + /* In direct timestamp mode, the focus lies on synchronized playback, not * on a constant latency. The ring buffer fill level is not of interest * here. The code in rtp_audio_receive() writes to the ring buffer at @@ -89,19 +101,32 @@ * timestamp mode, since all of them shift the timestamp by the same * `sess.latency.msec` into the future. * - * "Fill level" makes no sense in this mode, since a constant latency - * is not important in this mode, so no DLL is needed. Also, matching - * the pace of the synchronized clock is done by having the graph - * driver be synchronized to that clock, which will in turn cause - * any output sinks to adjust their DLLs (or similar control loop - * mechanisms) to match the pace of their data consumption with the - * pace of the driver. */ + * Since in this mode, a constant latency is not important, tracking + * the fill level to keep it steady makes no sense. Consequently, + * no DLL is needed. Also, matching the pace of the synchronized clock + * is done by having the graph driver be synchronized to that clock, + * which will in turn cause any output sinks to adjust their DLLs + * (or similar control loop mechanisms) to match the pace of their + * data consumption with the pace of the driver. + * + * The fill level is still important though to correctly handle corner + * cases where the ring buffer is (almost) empty. If fewer samples + * are available than what the read operation wants, the deficit + * has to be compensated with nullbytes. To that end, the "avail" + * quantity tracks how many samples are actually available. */ if (impl->io_position) { - /* Shift clock position by stream delay to compensate - * for processing and output delay. */ - timestamp = impl->io_position->clock.position + device_delay; + uint32_t clock_rate = impl->io_position->clock.rate.denom; + + /* Translate the clock position to an RTP timestamp and + * shift it to compensate for device delay and ASRC delay. + * The device delay is scaled along with the clock position, + * since both are expressed in clock sample units, while + * pwt.buffered is expressed in stream time. */ + timestamp = scale_u64(impl->io_position->clock.position + device_delay, + impl->rate, clock_rate) + pwt.buffered; spa_ringbuffer_read_update(&impl->ring, timestamp); + avail = spa_ringbuffer_get_read_index(&impl->ring, &read_index); } else { /* In the unlikely case that no spa_io_position pointer * was passed yet by PipeWire to this node, resort to a @@ -109,26 +134,72 @@ * This most likely is not in sync with other nodes, * but _something_ is needed as read index until the * spa_io_position is available. */ - spa_ringbuffer_get_read_index(&impl->ring, ×tamp); + avail = spa_ringbuffer_get_read_index(&impl->ring, ×tamp); + read_index = timestamp; } - spa_ringbuffer_read_data(&impl->ring, - impl->buffer, - impl->actual_max_buffer_size, - (timestamp * stride) % impl->actual_max_buffer_size, - d0.data, wanted * stride); - - /* Clear the bytes that were just retrieved. Since the fill level - * is not tracked in this buffer mode, it is possible that as soon - * as actual playback ends, the RTP source node re-reads old data. - * Make sure it reads silence when no actual new data is present - * and the RTP source node still runs. Do this by filling the - * region of the retrieved data with null bytes. */ - ringbuffer_clear(&impl->ring, - impl->buffer, - impl->actual_max_buffer_size, - (timestamp * stride) % impl->actual_max_buffer_size, - wanted * stride); + /* If avail is 0, it means that the ring buffer is empty. <0 means + * that there is an underrun, typically because the PTP time now + * is ahead of the RTP data (this can happen when the PTP master + * changes for example). And in cases where only a little bit of + * data is left, it is important to not try to use more than what + * is actually available. + * Overruns would happen if the write pointer is further ahead than + * what the ringbuffer size actually allows. This too can happen + * if the PTP time jumps. No actual buffer overflow would happen + * then, since the write operations always apply modulo to the + * timestamps to wrap around the ringbuffer borders. + */ + bool has_underrun = (avail < 0); + bool has_overrun = !has_underrun && ((uint32_t)avail) > impl->actual_max_buffer_size; + num_samples_to_read = has_underrun ? 0 : SPA_MIN((uint32_t)avail, wanted); + + /* Do some additional logging in the under/overrun cases. */ + if (SPA_UNLIKELY(pw_log_topic_enabled(SPA_LOG_LEVEL_TRACE, PW_LOG_TOPIC_DEFAULT))) + { + uint32_t write_index; + int32_t filled = spa_ringbuffer_get_write_index(&impl->ring, &write_index); + + if (has_underrun) { + pw_log_trace("Direct timestamp mode: Read index underrun: write_index: %" + PRIu32 ", read_index: %" PRIu32 ", wanted: %u - filled: %" PRIi32, + write_index, read_index, wanted, filled); + } else if (has_overrun) { + pw_log_trace("Direct timestamp mode: Read index overrun: write_index: %" + PRIu32 ", read_index: %" PRIu32 ", wanted: %u - filled: %" PRIi32 + ", buffer size: %u", write_index, read_index, wanted, filled, + impl->actual_max_buffer_size); + } + } + + if (num_samples_to_read > 0) { + spa_ringbuffer_read_data(&impl->ring, + impl->buffer, + impl->actual_max_buffer_size, + ((uint64_t)timestamp * stride) % impl->actual_max_buffer_size, + d0.data, num_samples_to_read * stride); + + /* Clear the bytes that were just retrieved. Since the fill level + * is not tracked in this buffer mode, it is possible that as soon + * as actual playback ends, the RTP source node re-reads old data. + * Make sure it reads silence when no actual new data is present + * and the RTP source node still runs. Do this by filling the + * region of the retrieved data with null bytes. */ + ringbuffer_clear(&impl->ring, + impl->buffer, + impl->actual_max_buffer_size, + ((uint64_t)timestamp * stride) % impl->actual_max_buffer_size, + num_samples_to_read * stride); + } + + if (num_samples_to_read < wanted) { + /* If fewer samples were available than what was wanted, + * fill the remaining space in the destination memory + * with nullsamples. */ + void *bytes_to_clear = SPA_PTROFF(d0.data, num_samples_to_read * stride, void); + size_t num_bytes_to_clear = (wanted - num_samples_to_read) * stride; + spa_memzero(bytes_to_clear, num_bytes_to_clear); + } if (!impl->io_position) { /* In the unlikely case that no spa_io_position pointer @@ -216,9 +287,28 @@ spa_ringbuffer_read_data(&impl->ring, impl->buffer, impl->actual_max_buffer_size, - (timestamp * stride) % impl->actual_max_buffer_size, + ((uint64_t)timestamp * stride) % impl->actual_max_buffer_size, d0.data, wanted * stride); + /* Clear the bytes that were just retrieved. Unlike in the + * direct timestamp mode, here, bytes are always read out + * of the ring buffer in sequence - the read pointer does + * not "jump around" (which can happen in direct timestamp + * mode if the last iteration has been a while ago and the + * driver clock time advanced significantly, or if the driver + * time experienced a discontinuity). However, should there + * be packet loss, it could lead to segments in the ring + * buffer that should have been written to but weren't written + * to. These segments would then contain old stale data. By + * clearing data out of the ring buffer after reading it, it + * is ensured that no stale data can exist - in the packet loss + * case, the outcome would be a gap made of nullsamples instead. */ + ringbuffer_clear(&impl->ring, + impl->buffer, + impl->actual_max_buffer_size, + ((uint64_t)timestamp * stride) % impl->actual_max_buffer_size, + wanted * stride); + timestamp += wanted; spa_ringbuffer_read_update(&impl->ring, timestamp); } @@ -316,7 +406,7 @@ spa_ringbuffer_write_data(&impl->ring, impl->buffer, impl->actual_max_buffer_size, - (write * stride) % impl->actual_max_buffer_size, + ((uint64_t)write * stride) % impl->actual_max_buffer_size, &bufferhlen, (samples * stride)); /* Only update the write index if data was actually _appended_. @@ -331,17 +421,43 @@
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-rtp/stream.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-rtp/stream.c
Changed
@@ -454,6 +454,10 @@ * meaning that the timer was no longer running, and the connection * could be closed. */ if (!timer_running) { + /* Clear the ringbuffer to prevent old invalid packets from being + * sent when processing resumes via rtp_audio_flush_packets() */ + if (impl->reset_ringbuffer) + impl->reset_ringbuffer(impl); set_internal_stream_state(impl, RTP_STREAM_INTERNAL_STATE_STOPPED); pw_log_info("stream stopped"); }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-snapcast-discover.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-snapcast-discover.c
Changed
@@ -162,7 +162,8 @@ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; -#define SERVICE_TYPE_CONTROL "_snapcast-jsonrpc._tcp" +#define SERVICE_TYPE_JSONRPC "_snapcast-jsonrpc._tcp" +#define SERVICE_TYPE_CONTROL "_snapcast-ctrl._tcp" struct impl { struct pw_context *context; @@ -176,7 +177,8 @@ AvahiPoll *avahi_poll; AvahiClient *client; - AvahiServiceBrowser *sink_browser; + AvahiServiceBrowser *jsonrpc_browser; + AvahiServiceBrowser *ctrl_browser; struct spa_list tunnel_list; uint32_t id; @@ -252,8 +254,10 @@ spa_list_consume(t, &impl->tunnel_list, link) free_tunnel(t); - if (impl->sink_browser) - avahi_service_browser_free(impl->sink_browser); + if (impl->jsonrpc_browser) + avahi_service_browser_free(impl->jsonrpc_browser); + if (impl->ctrl_browser) + avahi_service_browser_free(impl->ctrl_browser); if (impl->client) avahi_client_free(impl->client); if (impl->avahi_poll) @@ -636,10 +640,9 @@ } avahi_address_snprint(at, sizeof(at), a); - if (spa_strstartswith(at, link_local_range)) { - pw_log_info("found link-local ip address %s - skipping tunnel creation", at); - goto done; - } + if (spa_strstartswith(at, link_local_range)) + pw_log_info("found link-local ip address %s for '%s'", at, name); + pw_log_info("%s %s", name, at); tinfo = TUNNEL_INFO(.name = name, .port = port); @@ -667,6 +670,11 @@ (a->data.ipv6.address1 & 0xc0) == 0x80) snprintf(if_suffix, sizeof(if_suffix), "%%%d", interface); + /* For IPv4 link-local, bind to the discovery interface */ + if (a->proto == AVAHI_PROTO_INET && + spa_strstartswith(at, link_local_range)) + snprintf(if_suffix, sizeof(if_suffix), "%%%d", interface); + pw_properties_setf(props, "snapcast.ip", "%s%s", at, if_suffix); pw_properties_setf(props, "snapcast.ifindex", "%d", interface); pw_properties_setf(props, "snapcast.port", "%u", port); @@ -818,9 +826,13 @@ case AVAHI_CLIENT_S_REGISTERING: case AVAHI_CLIENT_S_RUNNING: case AVAHI_CLIENT_S_COLLISION: - if (impl->sink_browser == NULL) - impl->sink_browser = make_browser(impl, SERVICE_TYPE_CONTROL); - if (impl->sink_browser == NULL) + if (impl->ctrl_browser == NULL) + impl->ctrl_browser = make_browser(impl, SERVICE_TYPE_CONTROL); + if (impl->ctrl_browser == NULL) + goto error; + if (impl->jsonrpc_browser == NULL) + impl->jsonrpc_browser = make_browser(impl, SERVICE_TYPE_JSONRPC); + if (impl->jsonrpc_browser == NULL) goto error; break; case AVAHI_CLIENT_FAILURE: @@ -829,9 +841,13 @@ SPA_FALLTHROUGH; case AVAHI_CLIENT_CONNECTING: - if (impl->sink_browser) { - avahi_service_browser_free(impl->sink_browser); - impl->sink_browser = NULL; + if (impl->ctrl_browser) { + avahi_service_browser_free(impl->ctrl_browser); + impl->ctrl_browser = NULL; + } + if (impl->jsonrpc_browser) { + avahi_service_browser_free(impl->jsonrpc_browser); + impl->jsonrpc_browser = NULL; } break; default:
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/module-zeroconf-discover.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/module-zeroconf-discover.c
Changed
@@ -95,6 +95,7 @@ struct tunnel_info { const char *name; + const char *mode; }; #define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ }) @@ -117,6 +118,7 @@ return NULL; t->info.name = strdup(info->name); + t->info.mode = strdup(info->mode); spa_list_append(&impl->tunnel_list, &t->link); return t; @@ -126,7 +128,8 @@ { struct tunnel *t; spa_list_for_each(t, &impl->tunnel_list, link) { - if (spa_streq(t->info.name, info->name)) + if (spa_streq(t->info.name, info->name) && + spa_streq(t->info.mode, info->mode)) return t; } return NULL; @@ -138,6 +141,7 @@ if (t->module) pw_impl_module_destroy(t->module); free((char *) t->info.name); + free((char *) t->info.mode); free(t); } @@ -245,7 +249,7 @@ struct impl *impl = userdata; struct tunnel *t; struct tunnel_info tinfo; - const char *str, *device, *desc, *fqdn, *user; + const char *str, *device, *desc, *fqdn, *user, *mode; char if_suffix16 = ""; char atAVAHI_ADDRESS_STR_MAX; AvahiStringList *l; @@ -255,13 +259,16 @@ struct pw_impl_module *mod; struct pw_properties *props = NULL; + if (event != AVAHI_RESOLVER_FOUND) { pw_log_error("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(impl->client))); goto done; } - tinfo = TUNNEL_INFO(.name = name); + mode = strstr(type, "sink") ? "sink" : "source"; + + tinfo = TUNNEL_INFO(.name = name, .mode = mode); t = find_tunnel(impl, &tinfo); if (t == NULL) @@ -299,8 +306,7 @@ pw_properties_setf(props, PW_KEY_NODE_NAME, "tunnel.%s", host_name); - str = strstr(type, "sink") ? "sink" : "source"; - pw_properties_set(props, "tunnel.mode", str); + pw_properties_set(props, "tunnel.mode", mode); if (a->proto == AVAHI_PROTO_INET6 && a->data.ipv6.address0 == 0xfe &&
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/modules/network-utils.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/modules/network-utils.h
Changed
@@ -7,6 +7,12 @@ #include <arpa/inet.h> #include <net/if.h> #include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <limits.h> +#include <sys/un.h> + +#include <spa/utils/string.h> #ifdef __FreeBSD__ #define ifr_ifindex ifr_index @@ -131,5 +137,70 @@ return false; } +#ifndef LISTEN_FDS_START +#define LISTEN_FDS_START 3 +#endif + +/* Returns the number of file descriptors passed for socket activation. + * Returns 0 if none, -1 on error. */ +static inline int listen_fd(void) +{ + uint32_t n; + int i, flags; + + if (!spa_atou32(getenv("LISTEN_FDS"), &n, 10) || n > INT_MAX - LISTEN_FDS_START) { + errno = EINVAL; + return -1; + } + + for (i = 0; i < (int)n; i++) { + flags = fcntl(LISTEN_FDS_START + i, F_GETFD); + if (flags == -1) + return -1; + if (fcntl(LISTEN_FDS_START + i, F_SETFD, flags | FD_CLOEXEC) == -1) + return -1; + } + + unsetenv("LISTEN_FDS"); + + return (int)n; +} + +/* Check if the fd is a listening unix socket of the given type, + * optionally bound to the given path. */ +static inline int is_socket_unix(int fd, int type, const char *path) +{ + struct sockaddr_un addr; + int val; + socklen_t len = sizeof(val); + + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &val, &len) < 0) + return -errno; + if (val != type) + return 0; + + if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &val, &len) < 0) + return -errno; + if (!val) + return 0; + + if (path) { + len = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + if (getsockname(fd, (struct sockaddr *)&addr, &len) < 0) + return -errno; + if (addr.sun_family != AF_UNIX) + return 0; + size_t length = strlen(path); + if (length > 0) { + if (len < offsetof(struct sockaddr_un, sun_path) + length) + return 0; + if (memcmp(addr.sun_path, path, length) != 0) + return 0; + } + } + + return 1; +} #endif /* NETWORK_UTILS_H */
View file
_service:download_files:pipewire-1.6.0.tar.bz2/src/pipewire/capabilities.h
Added
@@ -0,0 +1,40 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2025 Red Hat */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_CAPABILITIESS_H +#define PIPEWIRE_CAPABILITIESS_H + +#include <pipewire/utils.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup pw_capabilities Capability Names + * + * A collection of capabilities that can be advertised by end points in + * streams. + * + * \addtogroup pw_capabilities + * \{ + */ + +/**< Link capable of device ID negotiation. The value is either "true" or "false" */ +#define PW_CAPABILITY_DEVICE_ID_NEGOTIATION "pipewire.device-id-negotiation" +/**< Link with device ID negotition capability supports negotiating with + * provided list of devices. The value consists of a JSON encoded string array + * of base64 encoded dev_t values. */ +#define PW_CAPABILITY_DEVICE_IDS "pipewire.device-ids" + +#define PW_CAPABILITY_DEVICE_ID "pipewire.device-id" /**< Link capable of device Id negotation */ + +/** \} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_CAPABILITIES_H */
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/pipewire/context.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/pipewire/context.c
Changed
@@ -1800,6 +1800,9 @@ n->target_rate = n->rt.position->clock.target_rate; } + if (n->info.state < PW_NODE_STATE_RUNNING) + n->rt.position->clock.nsec = get_time_ns(n->rt.target.system); + SPA_FLAG_UPDATE(n->rt.position->clock.flags, SPA_IO_CLOCK_FLAG_LAZY, have_request && n->supports_lazy > 0);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/pipewire/filter.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/pipewire/filter.c
Changed
@@ -507,8 +507,6 @@ case SPA_NODE_COMMAND_Suspend: case SPA_NODE_COMMAND_Flush: case SPA_NODE_COMMAND_Pause: - pw_loop_invoke(impl->main_loop, - NULL, 0, NULL, 0, false, impl); if (filter->state == PW_FILTER_STATE_STREAMING && id != SPA_NODE_COMMAND_Flush) { pw_log_debug("%p: pause", filter); filter_set_state(filter, PW_FILTER_STATE_PAUSED, 0, NULL); @@ -1389,6 +1387,28 @@ free(port); } +static void filter_free(struct pw_filter *filter) +{ + struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); + + pw_log_debug("%p: free", filter); + clear_params(impl, NULL, SPA_ID_INVALID); + + free(filter->error); + + pw_properties_free(filter->properties); + + pw_map_clear(&impl->portsSPA_DIRECTION_INPUT); + pw_map_clear(&impl->portsSPA_DIRECTION_OUTPUT); + + free(filter->name); + + if (impl->data.context) + pw_context_destroy(impl->data.context); + + free(impl); +} + SPA_EXPORT void pw_filter_destroy(struct pw_filter *filter) { @@ -1411,26 +1431,13 @@ spa_hook_remove(&filter->core_listener); spa_list_remove(&filter->link); } - - clear_params(impl, NULL, SPA_ID_INVALID); - - pw_log_debug("%p: free", filter); - free(filter->error); - - pw_properties_free(filter->properties); - spa_hook_list_clean(&impl->hooks); spa_hook_list_clean(&filter->listener_list); - pw_map_clear(&impl->portsSPA_DIRECTION_INPUT); - pw_map_clear(&impl->portsSPA_DIRECTION_OUTPUT); - - free(filter->name); - - if (impl->data.context) - pw_context_destroy(impl->data.context); + /* Make sure there are no queued invokes from us anymore */ + pw_loop_invoke(impl->main_loop, NULL, 0, NULL, 0, false, impl); - free(impl); + filter_free(filter); } static int @@ -1979,7 +1986,8 @@ pw_log_trace("%p: %"PRIi64" %"PRIi64" %"PRIu64" %d/%d ", filter, time->now, time->delay, time->ticks, time->rate.num, time->rate.denom); - return 0; + + return filter->state == PW_FILTER_STATE_STREAMING ? 0 : -EIO; } SPA_EXPORT
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/pipewire/impl-node.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/pipewire/impl-node.c
Changed
@@ -537,6 +537,21 @@ if (this->info.state > 0 && this->info.state <= PW_NODE_STATE_SUSPENDED) return 0; + spa_list_for_each(p, &this->input_ports, link) { + if (p->busy_count > 0) { + pw_log_debug("%p: can't suspend, input port %d busy:%d", + this, p->port_id, p->busy_count); + return -EBUSY; + } + } + spa_list_for_each(p, &this->output_ports, link) { + if (p->busy_count > 0) { + pw_log_debug("%p: can't suspend, output port %d busy:%d", + this, p->port_id, p->busy_count); + return -EBUSY; + } + } + node_deactivate(this); pw_log_debug("%p: suspend node driving:%d driver:%d prepared:%d", this,
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/pipewire/impl-port.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/pipewire/impl-port.c
Changed
@@ -1595,6 +1595,8 @@ pw_param_clear(&impl->pending_list, SPA_ID_INVALID); free(port->tagSPA_DIRECTION_INPUT); free(port->tagSPA_DIRECTION_OUTPUT); + free(port->capSPA_DIRECTION_INPUT); + free(port->capSPA_DIRECTION_OUTPUT); pw_map_clear(&port->mix_port_map);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/pipewire/meson.build -> _service:download_files:pipewire-1.6.0.tar.bz2/src/pipewire/meson.build
Changed
@@ -45,6 +45,7 @@ 'type.h', 'utils.h', 'work-queue.h', + 'capabilities.h', pipewire_sources =
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/pipewire/stream.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/pipewire/stream.c
Changed
@@ -715,8 +715,6 @@ case SPA_NODE_COMMAND_Suspend: case SPA_NODE_COMMAND_Flush: case SPA_NODE_COMMAND_Pause: - pw_loop_invoke(impl->main_loop, - NULL, 0, NULL, 0, false, impl); if (stream->state == PW_STREAM_STATE_STREAMING && id != SPA_NODE_COMMAND_Flush) { pw_log_debug("%p: pause", stream); stream_set_state(stream, PW_STREAM_STATE_PAUSED, 0, NULL); @@ -1748,11 +1746,35 @@ return 0; } +static void stream_free(struct pw_stream *stream) +{ + struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); + struct control *c; + + pw_log_debug("%p: free", stream); + clear_params(impl, SPA_ID_INVALID, 0); + + free(stream->error); + + pw_properties_free(stream->properties); + + free(stream->name); + + spa_list_consume(c, &stream->controls, link) { + spa_list_remove(&c->link); + free(c); + } + if (impl->data.context) + pw_context_destroy(impl->data.context); + + pw_properties_free(impl->port_props); + free(impl); +} + SPA_EXPORT void pw_stream_destroy(struct pw_stream *stream) { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); - struct control *c; ensure_loop(impl->main_loop, return); @@ -1768,29 +1790,13 @@ spa_list_remove(&stream->link); stream->core = NULL; } - - clear_params(impl, SPA_ID_INVALID, 0); - - pw_log_debug("%p: free", stream); - free(stream->error); - - pw_properties_free(stream->properties); - - free(stream->name); - - spa_list_consume(c, &stream->controls, link) { - spa_list_remove(&c->link); - free(c); - } - spa_hook_list_clean(&impl->hooks); spa_hook_list_clean(&stream->listener_list); - if (impl->data.context) - pw_context_destroy(impl->data.context); + /* Make sure there are no queued invokes from us anymore */ + pw_loop_invoke(impl->main_loop, NULL, 0, NULL, 0, false, impl); - pw_properties_free(impl->port_props); - free(impl); + stream_free(stream); } static int @@ -2475,8 +2481,9 @@ time->delay += (int64_t)(((latency->min_quantum + latency->max_quantum) / 2.0f) * quantum); time->delay += (latency->min_rate + latency->max_rate) / 2; - time->delay += ((latency->min_ns + latency->max_ns) / 2) * - (int64_t)time->rate.denom / (int64_t)SPA_NSEC_PER_SEC; + if (time->rate.num != 0) + time->delay += ((latency->min_ns + latency->max_ns) / 2) * + (int64_t)time->rate.denom / ((int64_t)SPA_NSEC_PER_SEC * time->rate.num); avail_buffers = spa_ringbuffer_get_read_index(&impl->dequeued.ring, &index); avail_buffers = SPA_CLAMP(avail_buffers, 0, (int32_t)impl->n_buffers); @@ -2497,7 +2504,8 @@ impl->dequeued.outcount, impl->dequeued.incount, impl->queued.outcount, impl->queued.incount, avail_buffers, impl->n_buffers); - return 0; + + return stream->state == PW_STREAM_STATE_STREAMING ? 0 : -EIO; } SPA_EXPORT
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/pipewire/stream.h -> _service:download_files:pipewire-1.6.0.tar.bz2/src/pipewire/stream.h
Changed
@@ -293,7 +293,8 @@ * Use pw_stream_get_time_n() to get an updated time snapshot of the stream. * The time snapshot can give information about the time in the driver of the * graph, the delay to the edge of the graph and the internal queuing in the - * stream. + * stream. This function should only be called in the STREAMING state and will + * return an error when called in any other state. * * pw_time.ticks gives a monotonic increasing counter of the time in the graph * driver. I can be used to generate a timeline to schedule samples as well @@ -594,7 +595,7 @@ /** Set control values */ int pw_stream_set_control(struct pw_stream *stream, uint32_t id, uint32_t n_values, float *values, ...); -/** Query the time on the stream, RT safe */ +/** Query the time on the stream. Returns an error when the stream is not running. RT safe */ int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time *time, size_t size); /** Get the current time in nanoseconds. This value can be compared with
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/tests/test-stream.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/tests/test-stream.c
Changed
@@ -151,7 +151,7 @@ /* check id, only when connected */ spa_assert_se(pw_stream_get_node_id(stream) == SPA_ID_INVALID); - spa_assert_se(pw_stream_get_time_n(stream, &tm, sizeof(tm)) == 0); + spa_assert_se(pw_stream_get_time_n(stream, &tm, sizeof(tm)) == -EIO); spa_assert_se(tm.now == 0); spa_assert_se(tm.rate.num == 0); spa_assert_se(tm.rate.denom == 0);
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/tools/pw-cat.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/tools/pw-cat.c
Changed
@@ -113,19 +113,19 @@ #define TYPE_SYSEX 4 #define TYPE_MIDI2 5 int data_type; - bool raw; + bool rawfile; const char *remote_name; const char *media_type; const char *media_category; const char *media_role; const char *channel_map; const char *format; + const char *container; const char *target; const char *latency; struct pw_properties *props; const char *filename; - SNDFILE *file; unsigned int bitrate; unsigned int rate; @@ -148,6 +148,9 @@ uint64_t clock_time; struct { + SNDFILE *file; + } sndfile; + struct { struct midi_file *file; struct midi_file_info info; #define MIDI_FORCE_NONE 0 @@ -181,14 +184,17 @@ #endif struct { FILE *file; + bool close; } sysex; + struct { + FILE *file; + bool close; + } raw; uint64_t sample_limit; /* 0 means unlimited */ uint64_t samples_processed; }; -#define STR_FMTS "(ulaw|alaw|u8|s8|s16|s32|f32|f64)" - static const struct format_info { const char *name; int sf_format; @@ -204,6 +210,35 @@ { "s32", SF_FORMAT_PCM_32, SPA_AUDIO_FORMAT_S32, 4 }, { "f32", SF_FORMAT_FLOAT, SPA_AUDIO_FORMAT_F32, 4 }, { "f64", SF_FORMAT_DOUBLE, SPA_AUDIO_FORMAT_F32, 8 }, + + { "mp1", SF_FORMAT_MPEG_LAYER_I, SPA_AUDIO_FORMAT_F32, 1 }, + { "mp2", SF_FORMAT_MPEG_LAYER_II, SPA_AUDIO_FORMAT_F32, 1 }, + { "mp3", SF_FORMAT_MPEG_LAYER_III, SPA_AUDIO_FORMAT_F32, 1 }, + { "vorbis", SF_FORMAT_VORBIS, SPA_AUDIO_FORMAT_F32, 1 }, + { "opus", SF_FORMAT_OPUS, SPA_AUDIO_FORMAT_F32, 1 }, + + { "ima-adpcm", SF_FORMAT_IMA_ADPCM, SPA_AUDIO_FORMAT_F32, 1 }, + { "ms-adpcm", SF_FORMAT_MS_ADPCM, SPA_AUDIO_FORMAT_F32, 1 }, + { "nms-adpcm-16", SF_FORMAT_NMS_ADPCM_16, SPA_AUDIO_FORMAT_F32, 1 }, + { "nms-adpcm-24", SF_FORMAT_NMS_ADPCM_24, SPA_AUDIO_FORMAT_F32, 1 }, + { "nms-adpcm-32", SF_FORMAT_NMS_ADPCM_32, SPA_AUDIO_FORMAT_F32, 1 }, + + { "alac-16", SF_FORMAT_ALAC_16, SPA_AUDIO_FORMAT_F32, 1 }, + { "alac-20", SF_FORMAT_ALAC_20, SPA_AUDIO_FORMAT_F32, 1 }, + { "alac-24", SF_FORMAT_ALAC_24, SPA_AUDIO_FORMAT_F32, 1 }, + { "alac-32", SF_FORMAT_ALAC_32, SPA_AUDIO_FORMAT_F32, 1 }, + + { "gsm610", SF_FORMAT_GSM610, SPA_AUDIO_FORMAT_F32, 1 }, + { "g721-32", SF_FORMAT_G721_32, SPA_AUDIO_FORMAT_F32, 1 }, + { "g723-24", SF_FORMAT_G723_24, SPA_AUDIO_FORMAT_F32, 1 }, + { "g723-40", SF_FORMAT_G723_40, SPA_AUDIO_FORMAT_F32, 1 }, + { "dwvw-12", SF_FORMAT_DWVW_12, SPA_AUDIO_FORMAT_F32, 1 }, + { "dwvw-16", SF_FORMAT_DWVW_16, SPA_AUDIO_FORMAT_F32, 1 }, + { "dwvw-24", SF_FORMAT_DWVW_24, SPA_AUDIO_FORMAT_F32, 1 }, + { "vox", SF_FORMAT_VOX_ADPCM, SPA_AUDIO_FORMAT_F32, 1 }, + { "dpcm-16", SF_FORMAT_DPCM_16, SPA_AUDIO_FORMAT_F32, 1 }, + { "dpcm-8", SF_FORMAT_DPCM_8, SPA_AUDIO_FORMAT_F32, 1 }, + }; static const struct format_info *format_info_by_name(const char *str) @@ -223,11 +258,19 @@ return NULL; } +static void list_formats(struct data *d) +{ + + fprintf(stdout, _("Supported formats:\n")); + SPA_FOR_EACH_ELEMENT_VAR(format_info, i) + fprintf(stdout, " %s\n", i->name); +} + static int sf_playback_fill_x8(struct data *d, void *dest, unsigned int n_frames, bool *null_frame) { sf_count_t rn; - rn = sf_read_raw(d->file, dest, n_frames * d->stride); + rn = sf_read_raw(d->sndfile.file, dest, n_frames * d->stride); return (int)rn / d->stride; } @@ -236,7 +279,7 @@ sf_count_t rn; assert(sizeof(short) == sizeof(int16_t)); - rn = sf_readf_short(d->file, dest, n_frames); + rn = sf_readf_short(d->sndfile.file, dest, n_frames); return (int)rn; } @@ -245,7 +288,7 @@ sf_count_t rn; assert(sizeof(int) == sizeof(int32_t)); - rn = sf_readf_int(d->file, dest, n_frames); + rn = sf_readf_int(d->sndfile.file, dest, n_frames); return (int)rn; } @@ -254,7 +297,7 @@ sf_count_t rn; assert(sizeof(float) == 4); - rn = sf_readf_float(d->file, dest, n_frames); + rn = sf_readf_float(d->sndfile.file, dest, n_frames); return (int)rn; } @@ -263,7 +306,7 @@ sf_count_t rn; assert(sizeof(double) == 8); - rn = sf_readf_double(d->file, dest, n_frames); + rn = sf_readf_double(d->sndfile.file, dest, n_frames); return (int)rn; } @@ -550,7 +593,7 @@ { sf_count_t rn; - rn = sf_write_raw(d->file, src, n_frames * d->stride); + rn = sf_write_raw(d->sndfile.file, src, n_frames * d->stride); return (int)rn / d->stride; } @@ -559,7 +602,7 @@ sf_count_t rn; assert(sizeof(short) == sizeof(int16_t)); - rn = sf_writef_short(d->file, src, n_frames); + rn = sf_writef_short(d->sndfile.file, src, n_frames); return (int)rn; } @@ -568,7 +611,7 @@ sf_count_t rn; assert(sizeof(int) == sizeof(int32_t)); - rn = sf_writef_int(d->file, src, n_frames); + rn = sf_writef_int(d->sndfile.file, src, n_frames); return (int)rn; } @@ -577,7 +620,7 @@ sf_count_t rn; assert(sizeof(float) == 4); - rn = sf_writef_float(d->file, src, n_frames); + rn = sf_writef_float(d->sndfile.file, src, n_frames); return (int)rn; } @@ -586,7 +629,7 @@ sf_count_t rn; assert(sizeof(double) == 8); - rn = sf_writef_double(d->file, src, n_frames); + rn = sf_writef_double(d->sndfile.file, src, n_frames); return (int)rn; } @@ -701,6 +744,34 @@ return 0; } +static void list_layouts(struct data *d) +{ + fprintf(stderr, _("Supported channel layouts:\n")); + SPA_FOR_EACH_ELEMENT_VAR(spa_type_audio_layout_info, i) { + if (i->name == NULL) + break;
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/tools/pw-link.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/tools/pw-link.c
Changed
@@ -314,6 +314,24 @@ return buffer; } +static void print_port_latency(struct data *data, const char *prefix, + struct object *p, enum spa_direction direction) +{ + const char *state; + struct spa_latency_info *info = &p->latencydirection; + + if (p->state == STATE_NONE || p->state == STATE_CHANGED) + state = p->latency_changeddirection ? "*" : "="; + else + state = state_name(data, p); + + printf("%s%s %s latency: { quantum= %f %f , rate= %d %d , ns= %"PRIi64" %"PRIi64" }\n", + state, prefix, direction == SPA_DIRECTION_INPUT ? "input ": "output", + info->min_quantum, info->max_quantum, + info->min_rate, info->max_rate, info->min_ns, info->max_ns); + p->latency_changeddirection = false; +} + static void print_port(struct data *data, const char *prefix, const char *state, struct object *n, struct object *p, bool verbose) { @@ -338,6 +356,10 @@ if (buffer0 != '\0') printf("%s %s%s%s\n", state, prefix2, prefix, buffer); } + if (data->opt_list & LIST_LATENCY) { + print_port_latency(data, "", p, SPA_DIRECTION_INPUT); + print_port_latency(data, "", p, SPA_DIRECTION_OUTPUT); + } } static void print_port_id(struct data *data, const char *prefix, uint32_t peer, struct object *l) @@ -350,24 +372,6 @@ print_port(data, prefix, state_name(data, l), n, p, false); } -static void print_port_latency(struct data *data, const char *prefix, - struct object *p, enum spa_direction direction) -{ - const char *state; - struct spa_latency_info *info = &p->latencydirection; - - if (p->state == STATE_NONE || p->state == STATE_CHANGED) - state = p->latency_changeddirection ? "*" : "="; - else - state = state_name(data, p); - - printf("%s%s %s latency: { quantum= %f %f , rate= %d %d , ns= %"PRIi64" %"PRIi64" }\n", - state, prefix, direction == SPA_DIRECTION_INPUT ? "input ": "output", - info->min_quantum, info->max_quantum, - info->min_rate, info->max_rate, info->min_ns, info->max_ns); - p->latency_changeddirection = false; -} - static void do_list_port_links(struct data *data, struct object *node, struct object *port) { struct object *o; @@ -462,10 +466,6 @@ if (data->opt_list & LIST_PORTS) print_port(data, "", NULL, node, o, data->opt_verbose); - if (data->opt_list & LIST_LATENCY) { - print_port_latency(data, "", o, SPA_DIRECTION_INPUT); - print_port_latency(data, "", o, SPA_DIRECTION_OUTPUT); - } if (data->opt_list & LIST_LINKS) do_list_port_links(data, node, o); }
View file
_service:download_files:pipewire-1.5.84.tar.bz2/src/tools/pw-mon.c -> _service:download_files:pipewire-1.6.0.tar.bz2/src/tools/pw-mon.c
Changed
@@ -780,7 +780,7 @@ " -N, --no-colors disable color output\n" " -C, --color=WHEN whether to enable color support. WHEN is `never`, `always`, or `auto`\n" " -o, --hide-props hide node properties\n" - " -a, --hide-params hide node properties\n" + " -a, --hide-params hide node parameters\n" " -p, --print-separator print empty line after every event to help streaming parser\n", name); }
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.