Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 35
View file
pipewire-aptx.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Sun Oct 8 16:26:36 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> + +- Update to version 0.3.81 + +------------------------------------------------------------------- Fri Sep 22 17:21:32 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> - Update to version 0.3.80
View file
pipewire-aptx.spec
Changed
@@ -7,7 +7,7 @@ %define soversion 0_2 Name: pipewire-aptx -Version: 0.3.80 +Version: 0.3.81 Release: 0 Summary: PipeWire Bluetooth aptX codec plugin License: MIT
View file
pipewire-0.3.80.tar.gz/.gitlab-ci.yml -> pipewire-0.3.81.tar.gz/.gitlab-ci.yml
Changed
@@ -458,8 +458,15 @@ extends: - .build_on_fedora stage: analysis + variables: + MESON_OPTIONS: >- + -Dpipewire-v4l2=enabled + -Dpipewire-jack=enabled script: - - shellcheck $(git grep -l "#\!/.*bin/.*sh") + - echo "Configuring with meson options $MESON_OPTIONS" + - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS + - shellcheck $(git ls-files '*.sh') + - shellcheck $(grep -rl "#\!/.*bin/.*sh" "$BUILD_DIR") spellcheck: extends:
View file
pipewire-0.3.80.tar.gz/NEWS -> pipewire-0.3.81.tar.gz/NEWS
Changed
@@ -1,3 +1,88 @@ +# PipeWire 0.3.81 (2023-10-06) + +This is the first 1.0 release candidate that is API and ABI compatible +with previous 0.3.x releases. + +## Highlights + - jackdbus support is now enabled by default. + - IRQ based scheduling in ALSA was improved and enabled by default for + Pro-Audio profile. It will also link the pcms together to get lower + latency. This now matches what JACK does and gives equal latency to + PipeWire for Pro-Audio profiles. + - Support both old and new versions of webrtc-audio-processing to make + the transition easier. + - Forced quantum changes by nodes or metadata will now also force a + suspend and resume of the graph, like the rate changes to make sure all + nodes adapt to the new quantum. This is important for Pro-Audio nodes + that need to reconfigure the hardware to a new period in IRQ based + scheduling. + - Fix a regression in regex parsing. + - Many bugfixes and improvements. + + +## PipeWire + - jackdbus is by default enabled now. The idea is that when jackdbus is + installed, the real libjack.so is in the path and we can become a + real JACK client. + - Forces quantum changes by nodes or metadata will now also force a + suspend and resume in the graph, like the rate changes to make sure all + nodes adapt to the new quantum. This is important for Pro-Audio nodes + that need to reconfigure the hardware to a new period. + - The stream now has an EARLY_PROCESS option that can be used to implement + custum buffer fill levels. (#3480) + - Fix a regression in regex parsing. (#3528) + - Fix a bug in position reporting in the driver node. (#3189) (#3544) + - Destroying a link will now recalculate the graph correctly. + - Fix the rate comparison for finding the best rate in the graph. + - Use malloc_trim() when available to release memory. (#1840) + +## Tools + - pw-cat now supports DFF DSD files. + - pw-cli avoid some NULL derefs in some cases. + +## Modules + - The RAOP sink has seen some cleanups and improvements. It will now ask + for feedback every 2 seconds to keep some devices alive. + - A bug in filter-chain was fixed where it would fail to apply the gain + when mixing just one source. + - The filter-chain can now pass the stream volume to a control in the + filter-chain graph. (#3434) + - Improve volume handling in RAOP sink. + +## Pulse-server + - Some cleanup in the pending_stream handling. + - Fix a regression in the event emission code where it failed to emit + a changed event when a node was linked. (#3522) + - Lower the realtime priority of pulseaudio clients. + - Set pulse.module.id on the echo-cancel streams. (#3541) + +## SPA + - Support both old and new versions of webrtc-audio-processing to make + the transition easier. + - The ALSA driver now does the sync of all followers directly from the + wakeup event. This results in more stable rate matching. + - IRQ based scheduling in ALSA was improved and enabled by default for + Pro-Audio profile. It will also link the pcms together to get lower + latency. This now matches what JACK does and gives equal latency to + PipeWire for Pro-Audio profiles. + - GNU/Hurd support was added. + - Some improvements to passthrough handling. + +## Bluetooth + - Improvements to the codec handling when PipeWire is used as Audio + Gateway. + - Adapt to new Bluez API for BAP devices. + +## JACK + - When the jack library is set in the default library path, avoid using + LD_LIBRARY_PATH because this can cause confusion. + - Handle clearing the latency on a port. + - jack_property now always manages to actually change the metadata because + it waits for a roundtrip before exiting. + +Older versions: + + # PipeWire 0.3.80 (2023-09-14) This is a bugfix release that is API and ABI compatible with previous @@ -63,9 +148,6 @@ - The mixer io areas are updated and handled safely now to avoid crashes. (#3506) -Older versions: - - # PipeWire 0.3.79 (2023-08-29) This is a quick bugfix release that is API and ABI compatible with previous
View file
pipewire-0.3.80.tar.gz/doc/input-filter-h.sh -> pipewire-0.3.81.tar.gz/doc/input-filter-h.sh
Changed
@@ -11,6 +11,7 @@ # Add \ingroup commands for the file, for each \addgroup in it BASEFILE=$(echo "$FILENAME" | sed -e 's@.*src/pipewire/@pipewire/@; s@.*spa/include/spa/@spa/@; s@.*src/test/@test/@;') +# shellcheck disable=SC2028 # \file is not an escape sequence echo "/** \file" echo "\`$BASEFILE\`" sed -n -e '/.*\\addtogroup a-zA-Z0-9_.*/ { s/.*addtogroup /\\ingroup /; p; }' < "$FILENAME" | sort | uniq
View file
pipewire-0.3.80.tar.gz/meson.build -> pipewire-0.3.81.tar.gz/meson.build
Changed
@@ -1,5 +1,5 @@ project('pipewire', 'c' , - version : '0.3.80', + version : '0.3.81', license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' , meson_version : '>= 0.61.1', default_options : 'warning_level=3', @@ -23,15 +23,15 @@ spaversion = '0.2' apiversion = '0.3' soversion = 0 -libversion = '@0@.@1@.0'.format(soversion, pipewire_version_minor.to_int() * 100 + pipewire_version_micro.to_int()) +libversion_minor = pipewire_version_major.to_int() * 1000 + pipewire_version_minor.to_int() * 100 + pipewire_version_micro.to_int() +libversion = '@0@.@1@.0'.format(soversion, libversion_minor) # LADI/jack # 3, for PipeWire being the third JACK implementation, after JACK1 and jackdmp/JACK2) jack_version_major = 3 -jack_version_minor = pipewire_version_minor.to_int() * 100 + pipewire_version_micro.to_int() +jack_version_minor = libversion_minor # libjackserver version has 0 for major (for compatibility with other implementations), -# 3 for minor, and "100*pipewire_version_minor + pipewire_version_micro" -# as micro version (the minor libpipewire soversion number) +# 3 for minor, and "1000*major + 100*minor + micro" as micro version (the minor libpipewire soversion number) libjackversion = '@0@.@1@.@2@'.format(soversion, jack_version_major, jack_version_minor) pipewire_name = 'pipewire-@0@'.format(apiversion) @@ -377,9 +377,17 @@ webrtc_dep = dependency('webrtc-audio-processing-1', version : '>= 1.2' , - required : get_option('echo-cancel-webrtc')) -summary({'WebRTC Echo Canceling': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies') -cdata.set('HAVE_WEBRTC', webrtc_dep.found()) + required : false) +cdata.set('HAVE_WEBRTC1', webrtc_dep.found()) +if webrtc_dep.found() + summary({'WebRTC Echo Canceling >= 1.2': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies') +else + webrtc_dep = dependency('webrtc-audio-processing', + version : '>= 0.2', '< 1.0', + required : get_option('echo-cancel-webrtc')) + cdata.set('HAVE_WEBRTC', webrtc_dep.found()) + summary({'WebRTC Echo Canceling < 1.0': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies') +endif # On FreeBSD and MidnightBSD, epoll-shim library is required for eventfd() and timerfd() epoll_shim_dep = (host_machine.system() == 'freebsd' or host_machine.system() == 'midnightbsd' @@ -427,6 +435,7 @@ 'reallocarray', '#include <stdlib.h>', '-D_GNU_SOURCE', , 'sigabbrev_np', '#include <string.h>', '-D_GNU_SOURCE', , 'XSetIOErrorExitHandler', '#include <X11/Xlib.h>', , x11_dep, + 'malloc_trim', '#include <malloc.h>', , , foreach f : check_functions
View file
pipewire-0.3.80.tar.gz/pipewire-jack/src/meson.build -> pipewire-0.3.81.tar.gz/pipewire-jack/src/meson.build
Changed
@@ -21,12 +21,19 @@ if libjack_path == '' libjack_path = modules_install_dir / 'jack' libjack_path_dlopen = modules_install_dir_dlopen / 'jack' + libjack_path_enable = '' +elif libjack_path == get_option('libdir') or libjack_path == pipewire_libdir + libjack_path = pipewire_libdir + libjack_path_dlopen = libjack_path + libjack_path_enable = '#' else libjack_path_dlopen = libjack_path + libjack_path_enable = '' endif tools_config = configuration_data() tools_config.set('LIBJACK_PATH', libjack_path_dlopen) +tools_config.set('LIBJACK_PATH_ENABLE', libjack_path_enable) configure_file(input : 'pw-jack.in', output : 'pw-jack',
View file
pipewire-0.3.80.tar.gz/pipewire-jack/src/metadata.c -> pipewire-0.3.81.tar.gz/pipewire-jack/src/metadata.c
Changed
@@ -230,7 +230,7 @@ pw_log_info("set id:%u (%"PRIu64") '%s' to '%s@%s'", o->id, subject, key, value, type); if (update_property(c, subject, key, type, value)) pw_metadata_set_property(c->metadata->proxy, o->id, key, type, value); - res = 0; + res = do_sync(c); done: pw_thread_loop_unlock(c->context.loop); @@ -340,7 +340,7 @@ pw_log_info("remove id:%u (%"PRIu64") '%s'", id, subject, key); pw_metadata_set_property(c->metadata->proxy, id, key, NULL, NULL); - res = 0; + res = do_sync(c); done: pw_thread_loop_unlock(c->context.loop); @@ -365,7 +365,7 @@ pw_log_info("remove id:%u (%"PRIu64")", id, subject); pw_metadata_set_property(c->metadata->proxy, id, NULL, NULL, NULL); - res = 0; + res = do_sync(c); done: pw_thread_loop_unlock(c->context.loop); @@ -375,15 +375,17 @@ SPA_EXPORT int jack_remove_all_properties (jack_client_t* client) { + int res; struct client *c = (struct client *) client; spa_return_val_if_fail(c != NULL, -EINVAL); pw_thread_loop_lock(c->context.loop); pw_metadata_clear(c->metadata->proxy); + res = do_sync(c); pw_thread_loop_unlock(c->context.loop); - return 0; + return res; } SPA_EXPORT
View file
pipewire-0.3.80.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.81.tar.gz/pipewire-jack/src/pipewire-jack.c
Changed
@@ -2441,9 +2441,11 @@ int res; if (param == NULL) - return 0; - if ((res = spa_latency_parse(param, &info)) < 0) + info = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(p->direction)); + else if ((res = spa_latency_parse(param, &info)) < 0) return res; + if (info.direction == p->direction) + return 0; current = &p->object->port.latencyinfo.direction; if (spa_latency_info_compare(current, &info) == 0) @@ -2457,8 +2459,6 @@ info.min_rate, info.max_rate, info.min_ns, info.max_ns); - if (info.direction == p->direction) - return 0; if (info.direction == SPA_DIRECTION_INPUT) mode = JackPlaybackLatency;
View file
pipewire-0.3.80.tar.gz/pipewire-jack/src/pw-jack.in -> pipewire-0.3.81.tar.gz/pipewire-jack/src/pw-jack.in
Changed
@@ -52,7 +52,9 @@ fi export PIPEWIRE_QUANTUM fi -LD_LIBRARY_PATH='@LIBJACK_PATH@'"${LD_LIBRARY_PATH+":$LD_LIBRARY_PATH"}" -export LD_LIBRARY_PATH + +# shellcheck disable=SC2016 # ${LIB} is interpreted by ld.so, not the shell +@LIBJACK_PATH_ENABLE@LD_LIBRARY_PATH='@LIBJACK_PATH@'"${LD_LIBRARY_PATH+":$LD_LIBRARY_PATH"}" +@LIBJACK_PATH_ENABLE@export LD_LIBRARY_PATH exec "$@"
View file
pipewire-0.3.80.tar.gz/pipewire-v4l2/src/pw-v4l2.in -> pipewire-0.3.81.tar.gz/pipewire-v4l2/src/pw-v4l2.in
Changed
@@ -37,6 +37,7 @@ if "$PW_UNINSTALLED" = 1 ; then PW_V4L2_LD_PRELOAD="$PW_BUILDDIR"'/pipewire-v4l2/src/libpw-v4l2.so' else + # shellcheck disable=SC2016 # ${LIB} is interpreted by ld.so, not the shell PW_V4L2_LD_PRELOAD='@LIBV4L2_PATH@/libpw-v4l2.so' fi
View file
pipewire-0.3.80.tar.gz/spa/examples/adapter-control.c -> pipewire-0.3.81.tar.gz/spa/examples/adapter-control.c
Changed
@@ -906,9 +906,9 @@ printf("got error %d\n", res); } -static char *getscale(uint32_t scale) +static const char *getscale(uint32_t scale) { - char *scale_s = NULL; + const char *scale_s = NULL; if (scale == SPA_AUDIO_VOLUME_RAMP_LINEAR) scale_s = LINEAR;
View file
pipewire-0.3.80.tar.gz/spa/include/spa/debug/log.h -> pipewire-0.3.81.tar.gz/spa/include/spa/debug/log.h
Changed
@@ -15,6 +15,10 @@ #include <spa/utils/defs.h> #include <spa/support/log.h> #include <spa/debug/context.h> +#include <spa/debug/dict.h> +#include <spa/debug/format.h> +#include <spa/debug/mem.h> +#include <spa/debug/pod.h> /** * \addtogroup spa_debug
View file
pipewire-0.3.80.tar.gz/spa/include/spa/utils/cleanup.h -> pipewire-0.3.81.tar.gz/spa/include/spa/utils/cleanup.h
Changed
@@ -35,15 +35,16 @@ #define spa_exchange(var, new_value) \ __extension__ ({ \ - __typeof__(var) _old_value = (var); \ - (var) = (new_value); \ + __typeof__(var) *_ptr = &(var); \ + __typeof__(var) _old_value = *_ptr; \ + *_ptr = (new_value); \ _old_value; \ }) -#if __GNUC__ > 10 || defined(__clang__) +#if __GNUC__ >= 10 || defined(__clang__) #define spa_steal_ptr(ptr) ((__typeof__(*(ptr)) *) spa_exchange((ptr), NULL)) #else -#define spa_steal_ptr(ptr) ((__typeof__(ptr)) spa_exchange((ptr), NULL)) +#define spa_steal_ptr(ptr) spa_exchange((ptr), NULL) #endif #define spa_steal_fd(fd) spa_exchange((fd), -1) @@ -52,16 +53,6 @@ #include <stdlib.h> - -#if __GNUC__ > 10 || defined(__clang__) -#define spa_clear_ptr(ptr, destructor) \ -__extension__ ({ \ - __typeof__(*(ptr)) *_old_value = spa_steal_ptr(ptr); \ - if (_old_value) \ - destructor(_old_value); \ - (void) 0; \ -}) -#else #define spa_clear_ptr(ptr, destructor) \ __extension__ ({ \ __typeof__(ptr) _old_value = spa_steal_ptr(ptr); \ @@ -69,7 +60,6 @@ destructor(_old_value); \ (void) 0; \ }) -#endif static inline void _spa_autofree_cleanup_func(void *p) {
View file
pipewire-0.3.80.tar.gz/spa/plugins/aec/aec-webrtc.cpp -> pipewire-0.3.81.tar.gz/spa/plugins/aec/aec-webrtc.cpp
Changed
@@ -3,6 +3,8 @@ /* SPDX-FileCopyrightText: Copyright © 2021 Arun Raghavan <arun@asymptotic.io> */ /* SPDX-License-Identifier: MIT */ +#include "config.h" + #include <memory> #include <utility> @@ -13,7 +15,13 @@ #include <spa/utils/json.h> #include <spa/support/plugin.h> +#ifdef HAVE_WEBRTC +#include <webrtc/modules/audio_processing/include/audio_processing.h> +#include <webrtc/modules/interface/module_common_types.h> +#include <webrtc/system_wrappers/include/trace.h> +#else #include <modules/audio_processing/include/audio_processing.h> +#endif struct impl_data { struct spa_handle handle; @@ -39,6 +47,54 @@ return default_value; } +#ifdef HAVE_WEBRTC +/* f0 f1 f2 */ +static int parse_point(struct spa_json *it, float (&f)3) +{ + struct spa_json arr; + int i, res; + + if (spa_json_enter_array(it, &arr) <= 0) + return -EINVAL; + + for (i = 0; i < 3; i++) { + if ((res = spa_json_get_float(&arr, &fi)) <= 0) + return -EINVAL; + } + return 0; +} + +/* point1 point2 ... */ +static int parse_mic_geometry(struct impl_data *impl, const char *mic_geometry, + std::vector<webrtc::Point>& geometry) +{ + int res; + size_t i; + struct spa_json it2; + + spa_json_init(&it0, mic_geometry, strlen(mic_geometry)); + if (spa_json_enter_array(&it0, &it1) <= 0) { + spa_log_error(impl->log, "Error: webrtc.mic-geometry expects an array"); + return -EINVAL; + } + + for (i = 0; i < geometry.size(); i++) { + float f3; + + if ((res = parse_point(&it1, f)) < 0) { + spa_log_error(impl->log, "Error: can't parse webrtc.mic-geometry points: %d", res); + return res; + } + + spa_log_info(impl->log, "mic %zd position: (%g %g %g)", i, f0, f1, f2); + geometryi.c0 = f0; + geometryi.c1 = f1; + geometryi.c2 = f2; + } + return 0; +} +#endif + static int webrtc_init2(void *object, const struct spa_dict *args, struct spa_audio_info_raw *rec_info, struct spa_audio_info_raw *out_info, struct spa_audio_info_raw *play_info) @@ -48,9 +104,18 @@ bool high_pass_filter = webrtc_get_spa_bool(args, "webrtc.high_pass_filter", true); bool noise_suppression = webrtc_get_spa_bool(args, "webrtc.noise_suppression", true); - bool transient_suppression = webrtc_get_spa_bool(args, "webrtc.transient_suppression", true); bool voice_detection = webrtc_get_spa_bool(args, "webrtc.voice_detection", true); - +#ifdef HAVE_WEBRTC + bool extended_filter = webrtc_get_spa_bool(args, "webrtc.extended_filter", true); + bool delay_agnostic = webrtc_get_spa_bool(args, "webrtc.delay_agnostic", true); + // Disable experimental flags by default + bool experimental_agc = webrtc_get_spa_bool(args, "webrtc.experimental_agc", false); + bool experimental_ns = webrtc_get_spa_bool(args, "webrtc.experimental_ns", false); + + bool beamforming = webrtc_get_spa_bool(args, "webrtc.beamforming", false); +#else + bool transient_suppression = webrtc_get_spa_bool(args, "webrtc.transient_suppression", true); +#endif // Note: AGC seems to mess up with Agnostic Delay Detection, especially with speech, // result in very poor performance, disable by default bool gain_control = webrtc_get_spa_bool(args, "webrtc.gain_control", false); @@ -59,6 +124,51 @@ // This filter will modify playback buffer (when calling ProcessReverseStream), but now // playback buffer modifications are discarded. +#ifdef HAVE_WEBRTC + webrtc::Config config; + config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(extended_filter)); + config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(delay_agnostic)); + config.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(experimental_agc)); + config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(experimental_ns)); + + if (beamforming) { + std::vector<webrtc::Point> geometry(rec_info->channels); + const char *mic_geometry, *target_direction; + + /* The beamformer gives a single mono channel */ + out_info->channels = 1; + out_info->position0 = SPA_AUDIO_CHANNEL_MONO; + + if ((mic_geometry = spa_dict_lookup(args, "webrtc.mic-geometry")) == NULL) { + spa_log_error(impl->log, "Error: webrtc.beamforming requires webrtc.mic-geometry"); + return -EINVAL; + } + + if ((res = parse_mic_geometry(impl, mic_geometry, geometry)) < 0) + return res; + + if ((target_direction = spa_dict_lookup(args, "webrtc.target-direction")) != NULL) { + webrtc::SphericalPointf direction(0.0f, 0.0f, 0.0f); + struct spa_json it; + float f3; + + spa_json_init(&it, target_direction, strlen(target_direction)); + if (parse_point(&it, f) < 0) { + spa_log_error(impl->log, "Error: can't parse target-direction %s", + target_direction); + return -EINVAL; + } + + direction.s0 = f0; + direction.s1 = f1; + direction.s2 = f2; + + config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry, direction)); + } else { + config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry)); + } + } +#else webrtc::AudioProcessing::Config config; config.echo_canceller.enabled = true; // FIXME: Example code enables both gain controllers, but that seems sus @@ -73,6 +183,7 @@ // FIXME: expose pre/postamp gain config.transient_suppression.enabled = transient_suppression; config.voice_detection.enabled = voice_detection; +#endif webrtc::ProcessingConfig pconfig = {{ webrtc::StreamConfig(rec_info->rate, rec_info->channels, false), /* input stream */ @@ -81,15 +192,35 @@ webrtc::StreamConfig(play_info->rate, play_info->channels, false), /* reverse output stream */ }}; +#ifdef HAVE_WEBRTC + auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessing::Create(config)); +#else auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessingBuilder().Create()); apm->ApplyConfig(config); +#endif if ((res = apm->Initialize(pconfig)) != webrtc::AudioProcessing::kNoError) { spa_log_error(impl->log, "Error initialising webrtc audio processing module: %d", res); return -EINVAL; } +#ifdef HAVE_WEBRTC + apm->high_pass_filter()->Enable(high_pass_filter); + // Always disable drift compensation since PipeWire will already do + // drift compensation on all sinks and sources linked to this echo-canceler + apm->echo_cancellation()->enable_drift_compensation(false); + apm->echo_cancellation()->Enable(true); + // TODO: wire up supression levels to args + apm->echo_cancellation()->set_suppression_level(webrtc::EchoCancellation::kHighSuppression); + apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh); + apm->noise_suppression()->Enable(noise_suppression); + apm->voice_detection()->Enable(voice_detection); + // TODO: wire up AGC parameters to args + apm->gain_control()->set_analog_level_limits(0, 255); + apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital); + apm->gain_control()->Enable(gain_control); +#endif impl->apm = std::move(apm); impl->rec_info = *rec_info; impl->out_info = *out_info;
View file
pipewire-0.3.80.tar.gz/spa/plugins/alsa/acp/acp.c -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/acp/acp.c
Changed
@@ -388,6 +388,8 @@ pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm); pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index); pa_proplist_setf(m->output_proplist, "device.profile.pro", "true"); + pa_proplist_setf(m->output_proplist, "node.group", "pro-audio-%u", index); + pa_proplist_setf(m->output_proplist, "node.link-group", "pro-audio-%u", index); pa_alsa_close(&m->output_pcm); m->supported = true; pa_channel_map_init_auto(&m->channel_map, m->sample_spec.channels, PA_CHANNEL_MAP_AUX); @@ -419,6 +421,8 @@ pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm); pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index); pa_proplist_setf(m->input_proplist, "device.profile.pro", "true"); + pa_proplist_setf(m->input_proplist, "node.group", "pro-audio-%u", index); + pa_proplist_setf(m->input_proplist, "node.link-group", "pro-audio-%u", index); pa_alsa_close(&m->input_pcm); m->supported = true; pa_channel_map_init_auto(&m->channel_map, m->sample_spec.channels, PA_CHANNEL_MAP_AUX);
View file
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-compress-offload-sink.c -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/alsa-compress-offload-sink.c
Changed
@@ -843,12 +843,12 @@ nextptr = device + 3; for (value_index = 0; ; ++value_index) { - char *value_label; + const char *value_label; switch (value_index) { case 0: value_label = "card"; break; case 1: value_label = "device"; break; - default: assert(false); + default: spa_assert_not_reached(); } errno = 0;
View file
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c
Changed
@@ -252,9 +252,13 @@ switch (id) { case SPA_IO_Clock: + if (size > 0 && size < sizeof(struct spa_io_clock)) + return -EINVAL; this->clock = data; break; case SPA_IO_Position: + if (size > 0 && size < sizeof(struct spa_io_position)) + return -EINVAL; this->position = data; break; default:
View file
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm-source.c -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/alsa-pcm-source.c
Changed
@@ -235,6 +235,8 @@ this->clock = data; break; case SPA_IO_Position: + if (size > 0 && size < sizeof(struct spa_io_position)) + return -EINVAL; this->position = data; break; default:
View file
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -17,6 +17,7 @@ #include "alsa-pcm.h" static struct spa_list cards = SPA_LIST_INIT(&cards); +static struct spa_list states = SPA_LIST_INIT(&states); static struct card *find_card(uint32_t index) { @@ -501,6 +502,9 @@ int err; const char *str; + spa_list_init(&state->followers); + spa_list_init(&state->rt.followers); + snd_config_update_free_global(); if ((str = spa_dict_lookup(info, "device.profile.pro")) != NULL) @@ -508,6 +512,7 @@ state->multi_rate = true; state->htimestamp = false; + state->disable_tsched = state->is_pro; for (i = 0; info && i < info->n_items; i++) { const char *k = info->itemsi.key; const char *s = info->itemsi.value; @@ -547,6 +552,8 @@ } CHECK(snd_output_stdio_attach(&state->output, state->log_file, 0), "attach failed"); + spa_list_append(&states, &state->link); + state->rate_limit.interval = 2 * SPA_NSEC_PER_SEC; state->rate_limit.burst = 1; @@ -557,6 +564,7 @@ { int err; + spa_list_remove(&state->link); release_card(state->card); state->card = NULL; @@ -584,7 +592,7 @@ err = snd_ctl_open(&state->ctl, device_name, SND_CTL_NONBLOCK); if (err < 0) { spa_log_info(state->log, "%s could not find ctl device: %s", - state->props.device, snd_strerror(err)); + device_name, snd_strerror(err)); state->ctl = NULL; goto error; } @@ -599,7 +607,7 @@ err = snd_ctl_elem_read(state->ctl, state->pitch_elem); if (err < 0) { spa_log_debug(state->log, "%s: did not find ctl %s: %s", - state->props.device, elem_name, snd_strerror(err)); + device_name, elem_name, snd_strerror(err)); snd_ctl_elem_value_free(state->pitch_elem); state->pitch_elem = NULL; @@ -613,13 +621,34 @@ CHECK(snd_ctl_elem_write(state->ctl, state->pitch_elem), "snd_ctl_elem_write"); state->last_rate = 1.0; - spa_log_info(state->log, "%s: found ctl %s", state->props.device, elem_name); + spa_log_info(state->log, "%s: found ctl %s", device_name, elem_name); err = 0; error: snd_lib_error_set_handler(NULL); return err; } +static int do_link(struct state *driver, struct state *state) +{ + int res; + snd_pcm_status_t *status; + + snd_pcm_status_alloca(&status); + snd_pcm_status(driver->hndl, status); + snd_pcm_status_dump(status, state->output); + snd_pcm_status(state->hndl, status); + snd_pcm_status_dump(status, state->output); + fflush(state->log_file); + + res = snd_pcm_link(driver->hndl, state->hndl); + if (res >= 0 || res == -EALREADY) + state->linked = true; + + spa_log_info(state->log, "%p: linked to driver %p: %u (%s)", + state, driver, state->linked, snd_strerror(res)); + return 0; +} + int spa_alsa_open(struct state *state, const char *params) { int err; @@ -632,6 +661,8 @@ spa_scnprintf(device_name, sizeof(device_name), "%s%s%s", state->card->ucm_prefix ? state->card->ucm_prefix : "", props->device, params ? params : ""); + spa_scnprintf(state->name, sizeof(state->name), "%s%s", + props->device, state->stream == SND_PCM_STREAM_CAPTURE ? "c" : "p"); spa_log_info(state->log, "%p: ALSA device open '%s' %s", state, device_name, state->stream == SND_PCM_STREAM_CAPTURE ? "capture" : "playback"); @@ -655,9 +686,6 @@ * these are initialised in spa_alsa_start() */ } - if (state->clock) - spa_scnprintf(state->clock->name, sizeof(state->clock->name), - "%s", state->clock_name); state->opened = true; state->sample_count = 0; state->sample_time = 0; @@ -667,12 +695,32 @@ return 0; error_exit_close: - spa_log_info(state->log, "%p: Device '%s' closing: %s", state, state->props.device, + spa_log_info(state->log, "%p: Device '%s' closing: %s", state, state->name, spa_strerror(err)); snd_pcm_close(state->hndl); return err; } +static void try_unlink(struct state *state) +{ + struct state *follower; + + if (state->driver != NULL && state->linked) { + snd_pcm_unlink(state->hndl); + spa_log_info(state->log, "%p: unlinked from driver %p", + state, state->driver); + state->linked = false; + } + spa_list_for_each(follower, &state->followers, driver_link) { + if (follower->opened && follower->linked) { + snd_pcm_unlink(follower->hndl); + spa_log_info(state->log, "%p: follower unlinked from driver %p", + follower, state); + follower->linked = false; + } + } +} + int spa_alsa_close(struct state *state) { int err = 0; @@ -680,11 +728,13 @@ if (!state->opened) return 0; + try_unlink(state); + spa_alsa_pause(state); - spa_log_info(state->log, "%p: Device '%s' closing", state, state->props.device); + spa_log_info(state->log, "%p: Device '%s' closing", state, state->name); if ((err = snd_pcm_close(state->hndl)) < 0) - spa_log_warn(state->log, "%s: close failed: %s", state->props.device, + spa_log_warn(state->log, "%s: close failed: %s", state->name, snd_strerror(err)); if (!state->disable_tsched) @@ -697,6 +747,7 @@ state->have_format = false; state->opened = false; + state->linked = false; if (state->pitch_elem) { snd_ctl_elem_value_free(state->pitch_elem); @@ -1094,7 +1145,7 @@ CHECK(snd_pcm_hw_params_set_channels_near(hndl, params, &rchannels), "set_channels"); if (state->default_channels != rchannels) { spa_log_warn(state->log, "%s: Channels doesn't match (requested %u, got %u)", - state->props.device, state->default_channels, rchannels); + state->name, state->default_channels, rchannels); } } if (state->default_rate != 0) { @@ -1102,7 +1153,7 @@ CHECK(snd_pcm_hw_params_set_rate_near(hndl, params, &rrate, 0), "set_rate_near"); if (state->default_rate != rrate) { spa_log_warn(state->log, "%s: Rate doesn't match (requested %u, got %u)", - state->props.device, state->default_rate, rrate); + state->name, state->default_rate, rrate); } } @@ -1164,7 +1215,7 @@ } } spa_log_warn(state->log, "%s: no format found (def:%d) formats:%s", - state->props.device, state->default_format, buf); + state->name, state->default_format, buf); for (i = 0, offs = 0; i <= SND_PCM_ACCESS_LAST; i++) { if (snd_pcm_access_mask_test(amask, (snd_pcm_access_t)i)) { @@ -1175,7 +1226,7 @@ offs += r; } } - spa_log_warn(state->log, "%s: access:%s", state->props.device, buf); + spa_log_warn(state->log, "%s: access:%s", state->name, buf); return -ENOTSUP; } @@ -1550,7 +1601,7 @@ if (rformat == SND_PCM_FORMAT_UNKNOWN) { spa_log_warn(state->log, "%s: unknown format", - state->props.device); + state->name); return -EINVAL; } @@ -1588,7 +1639,7 @@ planar ? SND_PCM_ACCESS_RW_NONINTERLEAVED : SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { spa_log_error(state->log, "%s: RW not possible: %s", - state->props.device, snd_strerror(err)); + state->name, snd_strerror(err)); return err; } } @@ -1605,7 +1656,7 @@ CHECK(snd_pcm_hw_params_set_channels_near(hndl, params, &val), "set_channels"); if (rchannels != val) { spa_log_warn(state->log, "%s: Channels doesn't match (requested %u, got %u)", - state->props.device, rchannels, val); + state->name, rchannels, val); if (!SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST)) return -EINVAL; if (fmt->media_subtype != SPA_MEDIA_SUBTYPE_raw) @@ -1628,7 +1679,7 @@ CHECK(snd_pcm_hw_params_set_rate_near(hndl, params, &val, 0), "set_rate_near"); if (rrate != val) { spa_log_warn(state->log, "%s: Rate doesn't match (requested %iHz, got %iHz)", - state->props.device, rrate, val); + state->name, rrate, val); if (!SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST)) return -EINVAL; if (fmt->media_subtype != SPA_MEDIA_SUBTYPE_raw) @@ -1639,7 +1690,7 @@ } if (rchannels == 0 || rrate == 0) { spa_log_error(state->log, "%s: invalid channels:%d or rate:%d", - state->props.device, rchannels, rrate); + state->name, rchannels, rrate); return -EIO; } @@ -1655,6 +1706,11 @@ else state->frame_size *= rchannels; + /* make sure we update threshold in check_position_config() because they depend + * on the samplerate. */ + state->driver_duration = 0; + state->driver_rate.denom = 0; + state->have_format = true; if (state->card->format_ref++ == 0) state->card->rate = rrate; @@ -1690,7 +1746,7 @@ CHECK(snd_pcm_hw_params_set_period_size_near(hndl, params, &period_size, &dir), "set_period_size_near"); if (period_size == 0) { - spa_log_error(state->log, "%s: invalid period_size 0 (driver error?)", state->props.device); + spa_log_error(state->log, "%s: invalid period_size 0 (driver error?)", state->name); return -EIO; } @@ -1710,7 +1766,7 @@ periods = state->buffer_frames / period_size; } if (state->buffer_frames == 0) { - spa_log_error(state->log, "%s: invalid buffer_frames 0 (driver error?)", state->props.device); + spa_log_error(state->log, "%s: invalid buffer_frames 0 (driver error?)", state->name); return -EIO; } @@ -1745,12 +1801,10 @@ state->latencystate->port_direction.min_rate = state->latencystate->port_direction.max_rate = latency; - spa_log_info(state->log, "%s (%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 " "buffer frames %lu, period frames %lu, periods %u, frame_size %zd " "headroom %u start-delay:%u batch:%u tsched:%u", - state->props.device, - state->stream == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", - snd_pcm_format_name(state->format), + 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, @@ -1791,7 +1845,7 @@ CHECK(snd_ctl_elem_write(state->ctl, state->pitch_elem), "snd_ctl_elem_write"); spa_log_trace_fp(state->log, "%s %u set rate to %g", - state->props.device, state->stream, state->rate_match->rate); + state->name, state->stream, state->rate_match->rate); state->last_rate = state->rate_match->rate; @@ -1854,10 +1908,6 @@ static int set_timeout(struct state *state, uint64_t time) { struct itimerspec ts; - - if (state->disable_tsched) - return 0; - ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC; ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC; ts.it_interval.tv_sec = 0; @@ -1867,7 +1917,7 @@ return 0; } -int spa_alsa_silence(struct state *state, snd_pcm_uframes_t silence) +static int spa_alsa_silence(struct state *state, snd_pcm_uframes_t silence) { snd_pcm_t *hndl = state->hndl; const snd_pcm_channel_area_t *my_areas; @@ -1879,7 +1929,7 @@ if (SPA_UNLIKELY((res = snd_pcm_mmap_begin(hndl, &my_areas, &offset, &frames)) < 0)) { spa_log_error(state->log, "%s: snd_pcm_mmap_begin error: %s", - state->props.device, snd_strerror(res)); + state->name, snd_strerror(res)); return res; } silence = SPA_MIN(silence, frames); @@ -1890,7 +1940,7 @@ if (SPA_UNLIKELY((res = snd_pcm_mmap_commit(hndl, offset, silence)) < 0)) { spa_log_error(state->log, "%s: snd_pcm_mmap_commit error: %s", - state->props.device, snd_strerror(res)); + state->name, snd_strerror(res)); return res; } } else { @@ -1909,14 +1959,79 @@ return 0; } +static void reset_buffers(struct state *this) +{ + uint32_t i; + + spa_list_init(&this->free); + spa_list_init(&this->ready); + + for (i = 0; i < this->n_buffers; i++) { + struct buffer *b = &this->buffersi; + if (this->stream == SND_PCM_STREAM_PLAYBACK) { + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); + spa_node_call_reuse_buffer(&this->callbacks, 0, b->id); + } else { + spa_list_append(&this->free, &b->link); + SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); + } + } +} + + +static int do_prepare(struct state *state) +{ + int err; + + state->last_threshold = state->threshold; + + spa_log_debug(state->log, "%p: start threshold:%d duration:%d rate:%d follower:%d match:%d resample:%d", + state, state->threshold, state->driver_duration, state->driver_rate.denom, + state->following, state->matching, state->resample); + + CHECK(set_swparams(state), "swparams"); + + if ((err = snd_pcm_prepare(state->hndl)) < 0 && err != -EBUSY) { + spa_log_error(state->log, "%s: snd_pcm_prepare error: %s", + state->name, snd_strerror(err)); + return err; + } + if (state->stream == SND_PCM_STREAM_PLAYBACK) { + snd_pcm_uframes_t silence = state->start_delay + state->threshold + state->headroom; + if (state->disable_tsched) + silence += state->threshold; + spa_alsa_silence(state, silence); + } + + reset_buffers(state); + state->alsa_sync = true; + state->alsa_sync_warning = false; + state->alsa_recovering = false; + state->alsa_started = false; + + return 0; +} + +static inline int do_drop(struct state *state) +{ + int res; + spa_log_debug(state->log, "%p: snd_pcm_drop %u", state, state->linked); + if (!state->linked && (res = snd_pcm_drop(state->hndl)) < 0) { + spa_log_error(state->log, "%s: snd_pcm_drop: %s", + state->name, snd_strerror(res)); + return res; + } + return 0; +} + static inline int do_start(struct state *state) { int res; if (SPA_UNLIKELY(!state->alsa_started)) { - spa_log_trace(state->log, "%p: snd_pcm_start", state); - if ((res = snd_pcm_start(state->hndl)) < 0) { + spa_log_debug(state->log, "%p: snd_pcm_start %u", state, state->linked); + if (!state->linked && (res = snd_pcm_start(state->hndl)) < 0) { spa_log_error(state->log, "%s: snd_pcm_start: %s", - state->props.device, snd_strerror(res)); + state->name, snd_strerror(res)); return res; } state->alsa_started = true; @@ -1924,15 +2039,18 @@ return 0; } +static inline int check_position_config(struct state *state); + static int alsa_recover(struct state *state, int err) { int res, st; snd_pcm_status_t *status; + struct state *driver, *follower; snd_pcm_status_alloca(&status); if (SPA_UNLIKELY((res = snd_pcm_status(state->hndl, status)) < 0)) { spa_log_error(state->log, "%s: snd_pcm_status error: %s", - state->props.device, snd_strerror(res)); + state->name, snd_strerror(res)); goto recover; } @@ -1964,7 +2082,7 @@ } case SND_PCM_STATE_SUSPENDED: spa_log_info(state->log, "%s: recover from state %s", - state->props.device, snd_pcm_state_name(st)); + state->name, snd_pcm_state_name(st)); res = snd_pcm_resume(state->hndl); if (res >= 0) return res; @@ -1972,24 +2090,48 @@ break; default: spa_log_error(state->log, "%s: recover from error state %s", - state->props.device, snd_pcm_state_name(st)); + state->name, snd_pcm_state_name(st)); break; } recover: if (SPA_UNLIKELY((res = snd_pcm_recover(state->hndl, err, true)) < 0)) { spa_log_error(state->log, "%s: snd_pcm_recover error: %s", - state->props.device, snd_strerror(res)); + state->name, snd_strerror(res)); return res; } - spa_dll_init(&state->dll); - state->alsa_recovering = true; - state->alsa_started = false; + if (state->driver && state->linked) + driver = state->driver; + else + driver = state; - if (state->stream == SND_PCM_STREAM_PLAYBACK) - spa_alsa_silence(state, state->start_delay + state->threshold + state->headroom); + do_drop(driver); + spa_list_for_each(follower, &driver->rt.followers, rt.driver_link) { + if (follower != driver && follower->linked) { + do_drop(follower); + check_position_config(follower); + } + } + do_prepare(driver); + spa_list_for_each(follower, &driver->rt.followers, rt.driver_link) { + if (follower != driver && follower->linked) + do_prepare(follower); + } + do_start(driver); + spa_list_for_each(follower, &driver->rt.followers, rt.driver_link) { + if (follower != driver && follower->linked) + do_start(follower); + } + + return res; +} - return do_start(state); +static inline snd_pcm_sframes_t alsa_avail(struct state *state) +{ + if (state->disable_tsched) + return snd_pcm_avail_update(state->hndl); + else + return snd_pcm_avail(state->hndl); } static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes_t *delay) @@ -1997,13 +2139,13 @@ int res, suppressed; snd_pcm_sframes_t avail; - if (SPA_UNLIKELY((avail = snd_pcm_avail(state->hndl)) < 0)) { + if (SPA_UNLIKELY((avail = alsa_avail(state)) < 0)) { if ((res = alsa_recover(state, avail)) < 0) return res; - if ((avail = snd_pcm_avail(state->hndl)) < 0) { + if ((avail = alsa_avail(state)) < 0) { if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { spa_log_warn(state->log, "%s: (%d suppressed) snd_pcm_avail after recover: %s", - state->props.device, suppressed, snd_strerror(avail)); + state->name, suppressed, snd_strerror(avail)); } avail = state->threshold * 2; } @@ -2020,7 +2162,7 @@ if ((res = snd_pcm_htimestamp(state->hndl, &havail, &tstamp)) < 0) { if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { spa_log_warn(state->log, "%s: (%d suppressed) snd_pcm_htimestamp error: %s", - state->props.device, suppressed, snd_strerror(res)); + state->name, suppressed, snd_strerror(res)); } return avail; } @@ -2042,13 +2184,13 @@ } else { if (++state->htimestamp_error > MAX_HTIMESTAMP_ERROR) { spa_log_error(state->log, "%s: wrong htimestamps from driver, disabling", - state->props.device); + state->name); state->htimestamp_error = 0; state->htimestamp = false; } else if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { spa_log_warn(state->log, "%s: (%d suppressed) impossible htimestamp diff:%"PRIi64, - state->props.device, suppressed, diff); + state->name, suppressed, diff); } } } @@ -2141,7 +2283,7 @@ spa_log_debug(state->log, "%s: follower:%d match:%d rate:%f " "bw:%f thr:%u del:%ld target:%ld err:%f max:%f", - state->props.device, follower, state->matching, + state->name, follower, state->matching, corr, state->dll.bw, state->threshold, delay, target, err, state->max_error); } @@ -2162,9 +2304,9 @@ if (SPA_LIKELY(!follower && state->clock)) { state->clock->nsec = current_time; - state->clock->rate = state->clock->target_rate; + state->clock->rate = state->driver_rate; state->clock->position += state->clock->duration; - state->clock->duration = state->duration; + state->clock->duration = state->driver_duration; state->clock->delay = delay + state->delay; state->clock->rate_diff = corr; state->clock->next_nsec = state->next_time; @@ -2177,11 +2319,6 @@ return 0; } -static inline bool is_following(struct state *state) -{ - return state->position && state->clock && state->position->clock.id != state->clock->id; -} - static int setup_matching(struct state *state) { state->matching = state->following; @@ -2195,10 +2332,10 @@ if (spa_streq(state->position->clock.name, state->clock_name)) state->matching = false; - state->resample = !state->pitch_elem && (((uint32_t)state->rate != state->rate_denom) || state->matching); + state->resample = !state->pitch_elem && (((uint32_t)state->rate != state->driver_rate.denom) || state->matching); spa_log_info(state->log, "driver clock:'%s'@%d our clock:'%s'@%d matching:%d resample:%d", - state->position->clock.name, state->rate_denom, + state->position->clock.name, state->driver_rate.denom, state->clock_name, state->rate, state->matching, state->resample); return 0; @@ -2206,7 +2343,7 @@ static void update_sources(struct state *state, bool active) { - if (state->disable_tsched && state->source0.data != NULL) { + if (state->disable_tsched && state->rt.sources_added) { for (int i = 0; i < state->n_fds; i++) { state->sourcei.mask = active ? state->pfdsi.events : 0; spa_loop_update_source(state->data_loop, &state->sourcei); @@ -2218,61 +2355,64 @@ { uint64_t target_duration; struct spa_fraction target_rate; + struct spa_io_position *pos; - if (SPA_UNLIKELY(state->position == NULL)) + if (SPA_UNLIKELY((pos = state->position) == NULL)) return 0; if (state->disable_tsched && state->started && !state->following) { target_duration = state->period_frames; target_rate = SPA_FRACTION(1, state->rate); - state->position->clock.target_duration = target_duration; - state->position->clock.target_rate = target_rate; + pos->clock.target_duration = target_duration; + pos->clock.target_rate = target_rate; } else { - target_duration = state->position->clock.target_duration; - target_rate = state->position->clock.target_rate; + target_duration = pos->clock.target_duration; + target_rate = pos->clock.target_rate; } + if (target_duration == 0 || target_rate.denom == 0) + return -EIO; - if (SPA_UNLIKELY((state->duration != target_duration) || - (state->rate_denom != target_rate.denom))) { - state->duration = target_duration; - state->rate_denom = target_rate.denom; - if (state->rate_denom == 0 || state->duration == 0) - return -EIO; - state->threshold = SPA_SCALE32_UP(state->duration, state->rate, state->rate_denom); + if (SPA_UNLIKELY((state->driver_duration != target_duration) || + (state->driver_rate.denom != target_rate.denom))) { + spa_log_info(state->log, "%p: follower:%d duration:%u->%"PRIu64" rate:%d->%d", + state, state->following, state->driver_duration, target_duration, + state->driver_rate.denom, target_rate.denom); + + state->driver_duration = target_duration; + state->driver_rate = target_rate; + state->threshold = SPA_SCALE32_UP(state->driver_duration, state->rate, state->driver_rate.denom); state->max_error = SPA_MAX(256.0f, state->threshold / 2.0f); state->max_resync = SPA_MIN(state->threshold, state->max_error); - state->resample = ((uint32_t)state->rate != state->rate_denom) || state->matching; + state->resample = ((uint32_t)state->rate != state->driver_rate.denom) || state->matching; state->alsa_sync = true; } return 0; } -int spa_alsa_write(struct state *state) +static int alsa_write_sync(struct state *state, uint64_t current_time) { - snd_pcm_t *hndl = state->hndl; - const snd_pcm_channel_area_t *my_areas; - snd_pcm_uframes_t written, frames, offset, off, to_write, total_written, max_write; - snd_pcm_sframes_t commitres; int res, suppressed; - size_t frame_size = state->frame_size; + snd_pcm_uframes_t avail, delay, target; + bool following = state->following; - if ((res = check_position_config(state)) < 0) + if (SPA_UNLIKELY((res = check_position_config(state)) < 0)) return res; - max_write = state->buffer_frames; - - if (state->following && state->alsa_started) { - uint64_t current_time; - snd_pcm_uframes_t avail, delay, target; - - current_time = state->position->clock.nsec; - - if (SPA_UNLIKELY((res = get_status(state, current_time, &avail, &delay, &target)) < 0)) - return res; + if (SPA_UNLIKELY((res = get_status(state, current_time, &avail, &delay, &target)) < 0)) + return res; - if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, true)) < 0)) - return res; + if (SPA_UNLIKELY(!following && delay > target + state->max_error)) { + spa_log_trace(state->log, "%p: early wakeup %ld %lu %lu", state, + avail, delay, target); + if (delay > target * 3) + delay = target * 3; + state->next_time = current_time + (delay - target) * SPA_NSEC_PER_SEC / state->rate; + return -EAGAIN; + } + if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, following)) < 0)) + return res; + if (following && !state->linked) { if (SPA_UNLIKELY(state->alsa_sync)) { enum spa_log_level lev; @@ -2284,7 +2424,7 @@ if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { spa_log_lev(state->log, lev, "%s: follower avail:%lu delay:%ld " "target:%ld thr:%u, resync (%d suppressed)", - state->props.device, avail, delay, + state->name, avail, delay, target, state->threshold, suppressed); } @@ -2297,15 +2437,25 @@ } else state->alsa_sync_warning = true; } + return 0; +} + +static int alsa_write_frames(struct state *state) +{ + snd_pcm_t *hndl = state->hndl; + const snd_pcm_channel_area_t *my_areas; + snd_pcm_uframes_t written, frames, offset, off, to_write, total_written; + snd_pcm_sframes_t commitres; + int res = 0; + size_t frame_size = state->frame_size; total_written = 0; again: - - frames = max_write; + frames = state->buffer_frames; if (state->use_mmap && frames > 0) { if (SPA_UNLIKELY((res = snd_pcm_mmap_begin(hndl, &my_areas, &offset, &frames)) < 0)) { spa_log_error(state->log, "%s: snd_pcm_mmap_begin error: %s", - state->props.device, snd_strerror(res)); + state->name, snd_strerror(res)); alsa_recover(state, res); return res; } @@ -2378,13 +2528,13 @@ if (state->use_mmap && written > 0) { if (SPA_UNLIKELY((commitres = snd_pcm_mmap_commit(hndl, offset, written)) < 0)) { spa_log_error(state->log, "%s: snd_pcm_mmap_commit error: %s", - state->props.device, snd_strerror(commitres)); + state->name, snd_strerror(commitres)); if (commitres != -EPIPE && commitres != -ESTRPIPE) return res; } if (commitres > 0 && written != (snd_pcm_uframes_t) commitres) { spa_log_warn(state->log, "%s: mmap_commit wrote %ld instead of %ld", - state->props.device, commitres, written); + state->name, commitres, written); } } @@ -2401,6 +2551,17 @@ return 0; } +int spa_alsa_write(struct state *state) +{ + int res = 0; + if (state->following && state->rt.driver == NULL) { + uint64_t current_time = state->position->clock.nsec; + if ((res = alsa_write_sync(state, current_time)) < 0) + return res; + } + return alsa_write_frames(state); +} + void spa_alsa_recycle_buffer(struct state *this, uint32_t buffer_id) { struct buffer *b = &this->buffersbuffer_id; @@ -2421,7 +2582,7 @@ snd_pcm_uframes_t total_frames = 0; if (spa_list_is_empty(&state->free)) { - spa_log_warn(state->log, "%s: no more buffers", state->props.device); + spa_log_warn(state->log, "%s: no more buffers", state->name); total_frames = frames; } else { size_t n_bytes, left, frame_size = state->frame_size; @@ -2483,33 +2644,34 @@ return total_frames; } - -int spa_alsa_read(struct state *state) +static int alsa_read_sync(struct state *state, uint64_t current_time) { - snd_pcm_t *hndl = state->hndl; - snd_pcm_uframes_t total_read = 0, to_read, max_read; - const snd_pcm_channel_area_t *my_areas; - snd_pcm_uframes_t read, frames, offset; - snd_pcm_sframes_t commitres; int res, suppressed; + snd_pcm_uframes_t avail, delay, target, max_read; + bool following = state->following; - if ((res = check_position_config(state)) < 0) - return res; - - max_read = state->buffer_frames; + if (SPA_UNLIKELY(!state->alsa_started)) + return 0; - if (state->following && state->alsa_started) { - uint64_t current_time; - snd_pcm_uframes_t avail, delay, target; + if (SPA_UNLIKELY((res = check_position_config(state)) < 0)) + return res; - current_time = state->position->clock.nsec; + if (SPA_UNLIKELY((res = get_status(state, current_time, &avail, &delay, &target)) < 0)) + return res; - if ((res = get_status(state, current_time, &avail, &delay, &target)) < 0) - return res; + if (SPA_UNLIKELY(!following && avail < state->read_size)) { + spa_log_trace(state->log, "%p: early wakeup %ld %ld %ld %d", state, + delay, avail, target, state->read_size); + state->next_time = current_time + (state->read_size - avail) * SPA_NSEC_PER_SEC / + state->rate; + return -EAGAIN; + } - if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, true)) < 0)) - return res; + if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, following)) < 0)) + return res; + max_read = state->buffer_frames; + if (following) { if (state->alsa_sync) { enum spa_log_level lev; @@ -2520,7 +2682,7 @@ if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { spa_log_lev(state->log, lev, "%s: follower delay:%ld target:%ld thr:%u, " - "resync (%d suppressed)", state->props.device, delay, + "resync (%d suppressed)", state->name, delay, target, state->threshold, suppressed); } @@ -2537,14 +2699,26 @@ if (avail < state->read_size) max_read = 0; } + state->max_read = SPA_MIN(max_read, state->read_size); + return 0; +} + +static int alsa_read_frames(struct state *state) +{ + snd_pcm_t *hndl = state->hndl; + snd_pcm_uframes_t total_read = 0, to_read; + const snd_pcm_channel_area_t *my_areas; + snd_pcm_uframes_t read, frames, offset; + snd_pcm_sframes_t commitres; + int res = 0; - frames = SPA_MIN(max_read, state->read_size); + frames = state->max_read; if (state->use_mmap) { to_read = state->buffer_frames; if ((res = snd_pcm_mmap_begin(hndl, &my_areas, &offset, &to_read)) < 0) { spa_log_error(state->log, "%s: snd_pcm_mmap_begin error: %s", - state->props.device, snd_strerror(res)); + state->name, snd_strerror(res)); alsa_recover(state, res); return res; } @@ -2568,14 +2742,21 @@ spa_log_trace_fp(state->log, "%p: commit offs:%ld read:%ld count:%"PRIi64, state, offset, read, state->sample_count); if ((commitres = snd_pcm_mmap_commit(hndl, offset, read)) < 0) { - spa_log_error(state->log, "%s: snd_pcm_mmap_commit error %lu %lu: %s", - state->props.device, frames, read, snd_strerror(commitres)); + enum spa_log_level lev; + + if (SPA_UNLIKELY(state->alsa_sync_warning)) + lev = SPA_LOG_LEVEL_ERROR; + else + lev = SPA_LOG_LEVEL_INFO; + + spa_log_lev(state->log, lev, "%s: snd_pcm_mmap_commit error %lu %lu %lu: %s", + state->name, frames, to_read, read, snd_strerror(commitres)); if (commitres != -EPIPE && commitres != -ESTRPIPE) return res; } if (commitres > 0 && read != (snd_pcm_uframes_t) commitres) { spa_log_warn(state->log, "%s: mmap_commit read %ld instead of %ld", - state->props.device, commitres, read); + state->name, commitres, read); } } @@ -2584,14 +2765,25 @@ return 0; } +int spa_alsa_read(struct state *state) +{ + int res; + if (state->following && state->rt.driver == NULL) { + uint64_t current_time = state->position->clock.nsec; + if ((res = alsa_read_sync(state, current_time)) < 0) + return res; + } + return alsa_read_frames(state); +} + int spa_alsa_skip(struct state *state) { struct buffer *b; struct spa_data *d; uint32_t i, avail, total_frames, n_bytes, frames; - if (spa_list_is_empty(&state->free)) { - spa_log_warn(state->log, "%s: no more buffers", state->props.device); + if (SPA_UNLIKELY(spa_list_is_empty(&state->free))) { + spa_log_warn(state->log, "%s: no more buffers", state->name); return -EPIPE; } @@ -2618,23 +2810,9 @@ } -static int handle_play(struct state *state, uint64_t current_time, snd_pcm_uframes_t avail, - snd_pcm_uframes_t delay, snd_pcm_uframes_t target) +static int playback_ready(struct state *state) { struct spa_io_buffers *io = state->io; - int res; - - if (state->alsa_started && SPA_UNLIKELY(delay > target + state->max_error)) { - spa_log_trace(state->log, "%p: early wakeup %ld %lu %lu", state, - avail, delay, target); - if (delay > target * 3) - delay = target * 3; - state->next_time = current_time + (delay - target) * SPA_NSEC_PER_SEC / state->rate; - return -EAGAIN; - } - - if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, false)) < 0)) - return res; spa_log_trace_fp(state->log, "%p: %d", state, io->status); @@ -2644,46 +2822,35 @@ return spa_node_call_ready(&state->callbacks, SPA_STATUS_NEED_DATA); } -static int handle_capture(struct state *state, uint64_t current_time, snd_pcm_uframes_t avail, - snd_pcm_uframes_t delay, snd_pcm_uframes_t target) +static int capture_ready(struct state *state) { - int res; struct spa_io_buffers *io; + bool have_data; - if (SPA_UNLIKELY(avail < state->read_size)) { - spa_log_trace(state->log, "%p: early wakeup %ld %ld %ld %d", state, - delay, avail, target, state->read_size); - state->next_time = current_time + (state->read_size - avail) * SPA_NSEC_PER_SEC / - state->rate; - return -EAGAIN; - } - - if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, false)) < 0)) - return res; - - if ((res = spa_alsa_read(state)) < 0) - return res; - - if (spa_list_is_empty(&state->ready)) - return 0; + have_data = !spa_list_is_empty(&state->ready); io = state->io; if (io != NULL && (io->status != SPA_STATUS_HAVE_DATA || state->rate_match != NULL)) { struct buffer *b; - if (io->buffer_id < state->n_buffers) + if (SPA_LIKELY(io->buffer_id < state->n_buffers)) spa_alsa_recycle_buffer(state, io->buffer_id); - b = spa_list_first(&state->ready, struct buffer, link); - spa_list_remove(&b->link); - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); + if (SPA_LIKELY(have_data)) { + b = spa_list_first(&state->ready, struct buffer, link); + spa_list_remove(&b->link); + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - io->buffer_id = b->id; - io->status = SPA_STATUS_HAVE_DATA; - spa_log_trace_fp(state->log, "%p: output buffer:%d", state, b->id); + io->buffer_id = b->id; + io->status = SPA_STATUS_HAVE_DATA; + } else { + io->buffer_id = SPA_ID_INVALID; + } + spa_log_trace_fp(state->log, "%p: output buffer:%d", state, io->buffer_id); } - spa_node_call_ready(&state->callbacks, SPA_STATUS_HAVE_DATA); + if (have_data) + spa_node_call_ready(&state->callbacks, SPA_STATUS_HAVE_DATA); return 0; } @@ -2697,8 +2864,7 @@ static void alsa_wakeup_event(struct spa_source *source) { - struct state *state = source->data; - snd_pcm_uframes_t avail, delay, target; + struct state *state = source->data, *follower; uint64_t expire, current_time; int res, suppressed; @@ -2742,154 +2908,151 @@ current_time = state->next_time; } - if (SPA_UNLIKELY((res = check_position_config(state)) < 0)) { - spa_log_warn(state->log, "%p: error invalid position: %s", - state, spa_strerror(res)); - return; - } - - if (SPA_UNLIKELY(get_status(state, current_time, &avail, &delay, &target) < 0)) { - spa_log_error(state->log, "get_status error"); - state->next_time += state->threshold * 1e9 / state->rate; + /* first do all the sync */ + if (state->stream == SND_PCM_STREAM_CAPTURE) + res = alsa_read_sync(state, current_time); + else + res = alsa_write_sync(state, current_time); + /* we can get -EAGAIN when we need to wait some more */ + if (SPA_UNLIKELY(res == -EAGAIN)) goto done; - } -#ifndef FASTPATH - if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_TRACE))) { - uint64_t nsec = get_time_ns(state); - spa_log_trace_fp(state->log, "%p: wakeup %lu %lu %lu %"PRIu64" %"PRIu64" %"PRIi64 - " %d %"PRIi64, state, avail, delay, target, nsec, nsec, - nsec - current_time, state->threshold, state->sample_count); + spa_list_for_each(follower, &state->rt.followers, rt.driver_link) { + if (follower == state) + continue; + if (follower->stream == SND_PCM_STREAM_CAPTURE) + alsa_read_sync(follower, current_time); + else + alsa_write_sync(follower, current_time); } -#endif + /* then read this source, the sinks will be written to when the + * graph completes. We can't read other follower sources yet because + * the resampler first needs to run. */ + if (state->stream == SND_PCM_STREAM_CAPTURE) + alsa_read_frames(state); + + /* and then trigger the graph */ if (state->stream == SND_PCM_STREAM_PLAYBACK) - handle_play(state, current_time, avail, delay, target); + playback_ready(state); else - handle_capture(state, current_time, avail, delay, target); + capture_ready(state); done: - if (!state->disable_tsched && - (state->next_time > current_time + SPA_NSEC_PER_SEC || - current_time > state->next_time + SPA_NSEC_PER_SEC)) { - if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { - spa_log_error(state->log, "%s: impossible timeout %lu %lu %lu %" + if (!state->disable_tsched) { + if (state->next_time > current_time + SPA_NSEC_PER_SEC || + current_time > state->next_time + SPA_NSEC_PER_SEC) { + if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { + spa_log_error(state->log, "%s: impossible timeout %" PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64" (%d suppressed)", - state->props.device, avail, delay, target, - current_time, state->next_time, state->next_time - current_time, - state->threshold, state->sample_count, suppressed); + state->name, current_time, state->next_time, + state->next_time - current_time, state->threshold, + state->sample_count, suppressed); + } + state->next_time = current_time + state->threshold * 1e9 / state->rate; } - state->next_time = current_time + state->threshold * 1e9 / state->rate; + set_timeout(state, state->next_time); } - set_timeout(state, state->next_time); } -static void reset_buffers(struct state *this) +static void remove_sources(struct state *state) { - uint32_t i; - - spa_list_init(&this->free); - spa_list_init(&this->ready); - - for (i = 0; i < this->n_buffers; i++) { - struct buffer *b = &this->buffersi; - if (this->stream == SND_PCM_STREAM_PLAYBACK) { - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - spa_node_call_reuse_buffer(&this->callbacks, 0, b->id); - } else { - spa_list_append(&this->free, &b->link); - SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); - } + int i; + if (state->rt.sources_added) { + for (i = 0; i < state->n_fds; i++) + spa_loop_remove_source(state->data_loop, &state->sourcei); + state->rt.sources_added = false; } } -static void clear_period_sources(struct state *state) { - /* This check is to make sure we've actually added the sources - * previously */ - if (state->source0.data) { - for (int i = 0; i < state->n_fds; i++) { - spa_loop_remove_source(state->data_loop, &state->sourcei); - state->sourcei.func = NULL; - state->sourcei.data = NULL; - state->sourcei.fd = -1; - state->sourcei.mask = 0; - state->sourcei.rmask = 0; - } +static void add_sources(struct state *state) +{ + int i; + if (!state->rt.sources_added) { + for (i = 0; i < state->n_fds; i++) + spa_loop_add_source(state->data_loop, &state->sourcei); + state->rt.sources_added = true; } } -static int setup_sources(struct state *state) +static int do_state_sync(struct spa_loop *loop, bool async, uint32_t seq, + const void *data, size_t size, void *user_data) { - state->next_time = get_time_ns(state); + struct state *state = user_data; + struct rt_state *rt = &state->rt; + + if (state->started) { + state->next_time = get_time_ns(state); + + if (rt->driver != state->driver) { + spa_dll_init(&state->dll); - if (state->following) { - /* Disable wakeups from this node */ - if (!state->disable_tsched) { + if (rt->driver != NULL) + spa_list_remove(&rt->driver_link); + if (state->driver != NULL) + spa_list_append(&state->driver->rt.followers, &rt->driver_link); + rt->driver = state->driver; + spa_log_debug(state->log, "state:%p -> driver:%p", state, state->driver); + } + if (state->following) { + remove_sources(state); set_timeout(state, 0); } else { - clear_period_sources(state); + add_sources(state); + if (!state->disable_tsched) + set_timeout(state, state->next_time); } } else { - /* We're driving, so let's wake up when data is ready/needed */ - if (!state->disable_tsched) { - set_timeout(state, state->next_time); - } else { - for (int i = 0; i < state->n_fds; i++) { - state->sourcei.func = alsa_wakeup_event; - state->sourcei.data = state; - state->sourcei.fd = state->pfdsi.fd; - state->sourcei.mask = state->pfdsi.events; - state->sourcei.rmask = 0; - spa_loop_add_source(state->data_loop, &state->sourcei); - } + if (rt->driver) { + spa_list_remove(&rt->driver_link); + rt->driver = NULL; } + if (!state->disable_tsched) + set_timeout(state, 0); + remove_sources(state); } return 0; } -static int do_setup_sources(struct spa_loop *loop, - bool async, - uint32_t seq, - const void *data, - size_t size, - void *user_data) -{ - struct state *state = user_data; - spa_dll_init(&state->dll); - setup_sources(state); - return 0; -} - -int spa_alsa_start(struct state *state) +int spa_alsa_prepare(struct state *state) { + struct state *follower; int err; - if (state->started) - return 0; + spa_alsa_pause(state); - state->following = is_following(state); + if (state->prepared) + return 0; if (check_position_config(state) < 0) { - spa_log_error(state->log, "%s: invalid position config", state->props.device); + spa_log_error(state->log, "%s: invalid position config", state->name); return -EIO; } + if ((err = do_prepare(state)) < 0) + return err; - setup_matching(state); + spa_list_for_each(follower, &state->followers, driver_link) { + if (follower != state && !follower->matching) { + spa_alsa_prepare(follower); + if (!follower->linked) + do_link(state, follower); + } + } - spa_dll_init(&state->dll); - state->last_threshold = state->threshold; + state->prepared = true; - spa_log_debug(state->log, "%p: start %d duration:%d rate:%d follower:%d match:%d resample:%d", - state, state->threshold, state->duration, state->rate_denom, - state->following, state->matching, state->resample); + return 0; +} - CHECK(set_swparams(state), "swparams"); +int spa_alsa_start(struct state *state) +{ + struct state *follower; + int err; - if ((err = snd_pcm_prepare(state->hndl)) < 0 && err != -EBUSY) { - spa_log_error(state->log, "%s: snd_pcm_prepare error: %s", - state->props.device, snd_strerror(err)); - return err; - } + if (state->started) + return 0; + + spa_alsa_prepare(state); if (!state->disable_tsched) { /* Timer-based scheduling */ @@ -2898,7 +3061,7 @@ state->source0.fd = state->timerfd; state->source0.mask = SPA_IO_IN; state->source0.rmask = 0; - spa_loop_add_source(state->data_loop, &state->source0); + state->n_fds = 1; } else { /* ALSA period-based scheduling */ err = snd_pcm_poll_descriptors_count(state->hndl); @@ -2922,102 +3085,112 @@ /* We only add the source to the data loop if we're driving. * This is done in setup_sources() */ for (int i = 0; i < state->n_fds; i++) { - state->sourcei.func = NULL; - state->sourcei.data = NULL; - state->sourcei.fd = -1; - state->sourcei.mask = 0; + state->sourcei.func = alsa_wakeup_event; + state->sourcei.data = state; + state->sourcei.fd = state->pfdsi.fd; + state->sourcei.mask = state->pfdsi.events; state->sourcei.rmask = 0; } } - reset_buffers(state); - state->alsa_sync = true; - state->alsa_sync_warning = false; - state->alsa_recovering = false; - state->alsa_started = false; + spa_list_for_each(follower, &state->followers, driver_link) + if (follower != state) + spa_alsa_start(follower); - /* start capture now, playback will start after first write. Without tsched, we start - * right away so that the fds become active in poll right away. */ - if (state->stream == SND_PCM_STREAM_PLAYBACK) { - spa_alsa_silence(state, state->start_delay + state->threshold + state->headroom); - if (state->disable_tsched) - do_start(state); + /* start capture now. We should have some data when the timer or IRQ + * goes off later */ + if (state->stream == SND_PCM_STREAM_CAPTURE) { + if ((err = do_start(state)) < 0) + return err; } - else if ((err = do_start(state)) < 0) - return err; - - spa_loop_invoke(state->data_loop, do_setup_sources, 0, NULL, 0, true, state); state->started = true; + spa_loop_invoke(state->data_loop, do_state_sync, 0, NULL, 0, true, state); + /* playback will start after first write. Without tsched, we start + * right away so that the fds become active in poll right away. */ + if (state->stream == SND_PCM_STREAM_PLAYBACK) { + if (state->disable_tsched) + if ((err = do_start(state)) < 0) + return err; + } return 0; } +static struct state *find_state(uint32_t id) +{ + struct state *state; + spa_list_for_each(state, &states, link) { + if (state->clock != NULL && state->clock->id == id) + return state; + } + return NULL; +} + int spa_alsa_reassign_follower(struct state *state) { bool following, freewheel; + struct spa_io_position *pos = state->position; + struct spa_io_clock *clock = state->clock; + struct state *driver; - if (!state->started) - return 0; + if (clock != NULL) + spa_scnprintf(clock->name, sizeof(clock->name), "%s", state->clock_name); - following = is_following(state); + following = pos && clock && pos->clock.id != clock->id; + + driver = pos != NULL ? find_state(pos->clock.id) : NULL; + + if (driver != state->driver) { + spa_log_debug(state->log, "%p: reassign driver %p->%p", state, state->driver, driver); + if (state->driver != NULL) + spa_list_remove(&state->driver_link); + if (driver != NULL) { + spa_list_append(&driver->followers, &state->driver_link); + } + state->driver = driver; + } if (following != state->following) { spa_log_debug(state->log, "%p: reassign follower %d->%d", state, state->following, following); state->following = following; - spa_loop_invoke(state->data_loop, do_setup_sources, 0, NULL, 0, true, state); + setup_matching(state); } - setup_matching(state); - - freewheel = state->position && - SPA_FLAG_IS_SET(state->position->clock.flags, SPA_IO_CLOCK_FLAG_FREEWHEEL); + if (state->started) + spa_loop_invoke(state->data_loop, do_state_sync, 0, NULL, 0, true, state); + freewheel = pos != NULL && SPA_FLAG_IS_SET(pos->clock.flags, SPA_IO_CLOCK_FLAG_FREEWHEEL); if (state->freewheel != freewheel) { spa_log_debug(state->log, "%p: freewheel %d->%d", state, state->freewheel, freewheel); state->freewheel = freewheel; - if (freewheel) - snd_pcm_pause(state->hndl, 1); - else - snd_pcm_pause(state->hndl, 0); + if (state->started) { + if (freewheel) + snd_pcm_pause(state->hndl, 1); + else + snd_pcm_pause(state->hndl, 0); + } } - state->alsa_sync_warning = false; return 0; } -static int do_remove_source(struct spa_loop *loop, - bool async, - uint32_t seq, - const void *data, - size_t size, - void *user_data) -{ - struct state *state = user_data; - - if (!state->disable_tsched) { - spa_loop_remove_source(state->data_loop, &state->source0); - set_timeout(state, 0); - } else { - clear_period_sources(state); - } - return 0; -} - int spa_alsa_pause(struct state *state) { - int err; + struct state *follower; if (!state->started) return 0; spa_log_debug(state->log, "%p: pause", state); - spa_loop_invoke(state->data_loop, do_remove_source, 0, NULL, 0, true, state); + state->started = false; + spa_loop_invoke(state->data_loop, do_state_sync, 0, NULL, 0, true, state); - if ((err = snd_pcm_drop(state->hndl)) < 0) - spa_log_error(state->log, "%s: snd_pcm_drop %s", state->props.device, - snd_strerror(err)); + spa_list_for_each(follower, &state->followers, driver_link) + spa_alsa_pause(follower); - state->started = false; + do_drop(state); + + state->prepared = false; return 0; }
View file
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm.h -> pipewire-0.3.81.tar.gz/spa/plugins/alsa/alsa-pcm.h
Changed
@@ -82,6 +82,15 @@ uint32_t rate; }; +struct rt_state { + struct spa_list followers; + struct state *driver; + struct spa_list driver_link; + + unsigned int sources_added:1; + unsigned int following:1; +}; + struct state { struct spa_handle handle; struct spa_node node; @@ -97,6 +106,7 @@ struct card *card; snd_pcm_stream_t stream; snd_output_t *output; + char name64; struct spa_hook_list hooks; struct spa_callbacks callbacks; @@ -111,7 +121,9 @@ struct spa_param_info paramsN_NODE_PARAMS; struct props props; - bool opened; + unsigned int opened:1; + unsigned int prepared:1; + unsigned int started:1; snd_pcm_t *hndl; bool have_format; @@ -127,9 +139,9 @@ uint32_t allowed_ratesMAX_RATES; uint32_t n_allowed_rates; struct channel_map default_pos; - unsigned int disable_mmap; - unsigned int disable_batch; - unsigned int disable_tsched; + unsigned int disable_mmap:1; + unsigned int disable_batch:1; + unsigned int disable_tsched:1; char clock_name64; uint32_t quantum_limit; @@ -141,9 +153,9 @@ size_t frame_size; size_t frame_scale; int blocks; - uint32_t rate_denom; uint32_t delay; uint32_t read_size; + uint32_t max_read; uint64_t port_info_all; struct spa_port_info port_info; @@ -169,7 +181,6 @@ size_t ready_offset; - bool started; /* Either a single source for tsched, or a set of pollfds from ALSA */ struct spa_source sourceMAX_POLL; int timerfd; @@ -183,7 +194,9 @@ uint32_t max_delay; uint32_t htimestamp_error; - uint32_t duration; + struct spa_fraction driver_rate; + uint32_t driver_duration; + unsigned int alsa_started:1; unsigned int alsa_sync:1; unsigned int alsa_sync_warning:1; @@ -200,6 +213,8 @@ unsigned int multi_rate:1; unsigned int htimestamp:1; unsigned int is_pro:1; + unsigned int sources_added:1; + unsigned int linked:1; uint64_t iec958_codecs; @@ -222,6 +237,14 @@ snd_ctl_t *ctl; snd_ctl_elem_value_t *pitch_elem; double last_rate; + + struct spa_list link; + + struct spa_list followers; + struct state *driver; + struct spa_list driver_link; + + struct rt_state rt; }; struct spa_pod *spa_alsa_enum_propinfo(struct state *state, @@ -240,6 +263,7 @@ int spa_alsa_clear(struct state *state); int spa_alsa_open(struct state *state, const char *params); +int spa_alsa_prepare(struct state *state); int spa_alsa_start(struct state *state); int spa_alsa_reassign_follower(struct state *state); int spa_alsa_pause(struct state *state);
View file
pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.81.tar.gz/spa/plugins/audioconvert/audioadapter.c
Changed
@@ -373,9 +373,6 @@ struct spa_data *datas; uint64_t follower_flags, conv_flags; - if (this->target == this->follower) - return 0; - spa_log_debug(this->log, "%p: n_buffers:%d", this, this->n_buffers); if (this->n_buffers > 0) @@ -516,10 +513,9 @@ this->have_format = format != NULL; if (format == NULL) { this->n_buffers = 0; - } else { + } else if (this->target != this->follower) { res = negotiate_buffers(this); } - return res; } @@ -555,7 +551,7 @@ struct spa_latency_info latency; int res; - spa_log_info(this->log, "%p: %d:%d", this, direction, port_id); + spa_log_debug(this->log, "%p: %d:%d", this, direction, port_id); if (this->target == this->follower) return 0; @@ -625,7 +621,7 @@ int res = 0; struct spa_hook l; - spa_log_info(this->log, "%p: passthrough mode %d", this, passthrough); + spa_log_debug(this->log, "%p: passthrough mode %d", this, passthrough); if (this->passthrough != passthrough) { if (passthrough) { @@ -846,9 +842,6 @@ struct spa_pod_builder b = { 0 }; int res; - if (this->target == this->follower) - return 0; - spa_log_debug(this->log, "%p: have_format:%d", this, this->have_format); if (this->have_format) @@ -920,12 +913,12 @@ switch (SPA_NODE_COMMAND_ID(command)) { case SPA_NODE_COMMAND_Start: spa_log_debug(this->log, "%p: starting %d", this, this->started); - if (this->started) - return 0; - if ((res = negotiate_format(this)) < 0) - return res; - if ((res = negotiate_buffers(this)) < 0) - return res; + if (this->target != this->follower) { + if (this->started) + return 0; + if ((res = negotiate_format(this)) < 0) + return res; + } this->ready = true; this->warned = false; break; @@ -1043,7 +1036,7 @@ uint32_t i; int res; - spa_log_info(this->log, "%p: convert port info %s %p %08"PRIx64, this, + spa_log_debug(this->log, "%p: convert port info %s %p %08"PRIx64, this, this->direction == SPA_DIRECTION_INPUT ? "Input" : "Output", info, info->change_mask);
View file
pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.81.tar.gz/spa/plugins/audioconvert/audioconvert.c
Changed
@@ -370,8 +370,9 @@ } spa_list_init(&port->queue); - spa_log_info(this->log, "%p: add port %d:%d position:%s %d %d %d", - this, direction, port_id, port->position, is_dsp, is_monitor, is_control); + spa_log_debug(this->log, "%p: add port %d:%d position:%s %d %d %d", + this, direction, port_id, port->position, is_dsp, + is_monitor, is_control); emit_port_info(this, port, true); return 0; @@ -1266,8 +1267,9 @@ (info == NULL || memcmp(&dir->format, info, sizeof(*info)) == 0)) return 0; - spa_log_info(this->log, "%p: port config direction:%d monitor:%d control:%d mode:%d %d", this, - direction, monitor, control, mode, dir->n_ports); + spa_log_debug(this->log, "%p: port config direction:%d monitor:%d " + "control:%d mode:%d %d", this, direction, monitor, + control, mode, dir->n_ports); for (i = 0; i < dir->n_ports; i++) { spa_node_emit_port_info(&this->hooks, direction, i, NULL); @@ -2443,6 +2445,53 @@ SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_QUEUED); } +static void free_tmp(struct impl *this) +{ + uint32_t i; + + spa_log_debug(this->log, "free tmp %d", this->empty_size); + + free(this->empty); + this->empty = NULL; + this->empty_size = 0; + free(this->scratch); + this->scratch = NULL; + free(this->tmp0); + this->tmp0 = NULL; + free(this->tmp1); + this->tmp1 = NULL; + for (i = 0; i < MAX_PORTS; i++) { + this->tmp_datas0i = NULL; + this->tmp_datas1i = NULL; + } +} + +static int ensure_tmp(struct impl *this, uint32_t maxsize) +{ + if (maxsize > this->empty_size) { + float *empty, *scratch, *tmp2; + + spa_log_debug(this->log, "resize tmp %d -> %d", this->empty_size, maxsize); + + if ((empty = realloc(this->empty, maxsize + MAX_ALIGN)) != NULL) + this->empty = empty; + if ((scratch = realloc(this->scratch, maxsize + MAX_ALIGN)) != NULL) + this->scratch = scratch; + if ((tmp0 = realloc(this->tmp0, (maxsize + MAX_ALIGN) * MAX_PORTS)) != NULL) + this->tmp0 = tmp0; + if ((tmp1 = realloc(this->tmp1, (maxsize + MAX_ALIGN) * MAX_PORTS)) != NULL) + this->tmp1 = tmp1; + + if (empty == NULL || scratch == NULL || tmp0 == NULL || tmp1 == NULL) { + free_tmp(this); + return -ENOMEM; + } + memset(this->empty, 0, maxsize + MAX_ALIGN); + this->empty_size = maxsize; + } + return 0; +} + static int impl_node_port_use_buffers(void *object, enum spa_direction direction, @@ -2454,6 +2503,7 @@ struct impl *this = object; struct port *port; uint32_t i, j, maxsize; + int res; spa_return_val_if_fail(this != NULL, -EINVAL); @@ -2510,17 +2560,9 @@ if (direction == SPA_DIRECTION_OUTPUT) queue_buffer(this, port, i); } - if (maxsize > this->empty_size) { - this->empty = realloc(this->empty, maxsize + MAX_ALIGN); - this->scratch = realloc(this->scratch, maxsize + MAX_ALIGN); - this->tmp0 = realloc(this->tmp0, (maxsize + MAX_ALIGN) * MAX_PORTS); - this->tmp1 = realloc(this->tmp1, (maxsize + MAX_ALIGN) * MAX_PORTS); - if (this->empty == NULL || this->scratch == NULL || - this->tmp0 == NULL || this->tmp1 == NULL) - return -errno; - memset(this->empty, 0, maxsize + MAX_ALIGN); - this->empty_size = maxsize; - } + if ((res = ensure_tmp(this, maxsize)) < 0) + return res; + port->n_buffers = n_buffers; return 0; @@ -3248,10 +3290,7 @@ free_dir(&this->dirSPA_DIRECTION_INPUT); free_dir(&this->dirSPA_DIRECTION_OUTPUT); - free(this->empty); - free(this->scratch); - free(this->tmp0); - free(this->tmp1); + free_tmp(this); if (this->resample.free) resample_free(&this->resample);
View file
pipewire-0.3.80.tar.gz/spa/plugins/audiomixer/audiomixer.c -> pipewire-0.3.81.tar.gz/spa/plugins/audiomixer/audiomixer.c
Changed
@@ -920,6 +920,12 @@ this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); spa_log_topic_init(this->log, log_topic); + this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); + if (this->data_loop == NULL) { + spa_log_error(this->log, "a data loop is needed"); + return -EINVAL; + } + this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); if (this->cpu) { this->cpu_flags = spa_cpu_get_flags(this->cpu);
View file
pipewire-0.3.80.tar.gz/spa/plugins/bluez5/a2dp-codec-opus.c -> pipewire-0.3.81.tar.gz/spa/plugins/bluez5/a2dp-codec-opus.c
Changed
@@ -443,7 +443,7 @@ OPUS_05_SET_LOCATION(caps->bidi, props->bidi_location); break; default: - spa_assert(false); + spa_assert_not_reached(); }; return 0;
View file
pipewire-0.3.80.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.81.tar.gz/spa/plugins/bluez5/backend-native.c
Changed
@@ -1915,11 +1915,12 @@ .destroy = sco_destroy_cb, }; -static struct rfcomm *device_find_rfcomm(struct impl *backend, struct spa_bt_device *device) +static struct rfcomm *device_find_rfcomm(struct impl *backend, struct spa_bt_device *device, + enum spa_bt_profile profile) { struct rfcomm *rfcomm; spa_list_for_each(rfcomm, &backend->rfcomm_list, link) { - if (rfcomm->device == device) + if (rfcomm->device == device && (rfcomm->profile & profile)) return rfcomm; } return NULL; @@ -1931,8 +1932,8 @@ struct impl *backend = data; struct rfcomm *rfcomm; - rfcomm = device_find_rfcomm(backend, device); - if (rfcomm == NULL || rfcomm->profile != SPA_BT_PROFILE_HFP_HF) + rfcomm = device_find_rfcomm(backend, device, SPA_BT_PROFILE_HFP_HF); + if (rfcomm == NULL) return -ENOTSUP; if (codec == HFP_AUDIO_CODEC_CVSD) @@ -2097,10 +2098,12 @@ int res; res = backend_native_supports_codec(data, device, codec); - if (res <= 0) + if (res < 0) + return res; + else if (!res) return -EINVAL; - rfcomm = device_find_rfcomm(backend, device); + rfcomm = device_find_rfcomm(backend, device, SPA_BT_PROFILE_HFP_HF); if (rfcomm == NULL) return -ENOTSUP; @@ -2399,7 +2402,7 @@ DBusMessageIter it4; dbus_bool_t autoconnect; dbus_uint16_t version, chan, features; - char *str; + const char *str; if (!(backend->enabled_profiles & spa_bt_profile_from_uuid(uuid))) return -ECANCELED;
View file
pipewire-0.3.80.tar.gz/spa/plugins/bluez5/bap-codec-caps.h -> pipewire-0.3.81.tar.gz/spa/plugins/bluez5/bap-codec-caps.h
Changed
@@ -97,14 +97,17 @@ }; struct bap_endpoint_qos { - uint8_t framing; - uint8_t phy; - uint8_t retransmission; - uint16_t latency; - uint32_t delay_min; - uint32_t delay_max; - uint32_t preferred_delay_min; - uint32_t preferred_delay_max; + uint8_t framing; + uint8_t phy; + uint8_t retransmission; + uint16_t latency; + uint32_t delay_min; + uint32_t delay_max; + uint32_t preferred_delay_min; + uint32_t preferred_delay_max; + uint32_t locations; + uint16_t supported_context; + uint16_t context; }; struct bap_codec_qos { @@ -118,4 +121,12 @@ uint8_t target_latency; }; +struct bap_codec_qos_full { + uint8_t cig; + uint8_t cis; + uint8_t big; + uint8_t bis; + struct bap_codec_qos qos; +}; + #endif
View file
pipewire-0.3.80.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.81.tar.gz/spa/plugins/bluez5/bluez5-dbus.c
Changed
@@ -627,6 +627,242 @@ static void append_basic_array_variant_dict_entry(DBusMessageIter *dict, const char* key, const char* variant_type_str, const char* array_type_str, int array_type_int, void* data, int data_size); static struct spa_bt_remote_endpoint *remote_endpoint_find(struct spa_bt_monitor *monitor, const char *path); +static bool check_iter_signature(DBusMessageIter *it, const char *sig) +{ + char *v; + bool res; + v = dbus_message_iter_get_signature(it); + res = spa_streq(v, sig); + dbus_free(v); + return res; +} + +static void parse_codec_qos(struct spa_bt_monitor *monitor, DBusMessageIter *iter, struct bap_codec_qos_full *qos) +{ + DBusMessageIter dict_iter = *iter; + + memset(qos, 0, sizeof(*qos)); + qos->cig = 0xff; + qos->cis = 0xff; + qos->big = 0xff; + qos->bis = 0xff; + + if (!check_iter_signature(&dict_iter, "{sv}")) { + spa_log_warn(monitor->log, "Invalid BAP QoS in DBus"); + return; + } + + while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { + DBusMessageIter it2; + const char *key; + int type; + + dbus_message_iter_recurse(&dict_iter, &it0); + dbus_message_iter_get_basic(&it0, &key); + dbus_message_iter_next(&it0); + dbus_message_iter_recurse(&it0, &it1); + + type = dbus_message_iter_get_arg_type(&it1); + + if (type == DBUS_TYPE_BYTE) { + uint8_t value; + + dbus_message_iter_get_basic(&it1, &value); + spa_log_debug(monitor->log, "qos: %s=%d", key, (int)value); + + if (spa_streq(key, "PHY")) + qos->qos.phy = value; + else if (spa_streq(key, "Retransmissions")) + qos->qos.retransmission = value; + else if (spa_streq(key, "CIG")) + qos->cig = value; + else if (spa_streq(key, "CIS")) + qos->cis = value; + else if (spa_streq(key, "BIG")) + qos->big = value; + else if (spa_streq(key, "BIS")) + qos->bis = value; + else if (spa_streq(key, "TargetLatency")) + qos->qos.target_latency = value; + else if (spa_streq(key, "Framing")) + qos->qos.framing = value; + } + else if (type == DBUS_TYPE_UINT16) { + dbus_uint16_t value; + + dbus_message_iter_get_basic(&it1, &value); + spa_log_debug(monitor->log, "qos: %s=%d", key, (int)value); + + if (spa_streq(key, "SDU")) + qos->qos.sdu = value; + else if (spa_streq(key, "Latency") || spa_streq(key, "MaximumLatency")) + qos->qos.latency = value; + } + else if (type == DBUS_TYPE_UINT32) { + dbus_uint32_t value; + + dbus_message_iter_get_basic(&it1, &value); + spa_log_debug(monitor->log, "qos: %s=%d", key, (int)value); + + if (spa_streq(key, "Interval")) + qos->qos.interval = value; + else if (spa_streq(key, "PresentationDelay")) + qos->qos.delay = value; + } + + dbus_message_iter_next(&dict_iter); + } +} + +static void parse_endpoint_qos(struct spa_bt_monitor *monitor, DBusMessageIter *iter, + struct bap_endpoint_qos *qos) +{ + DBusMessageIter dict_iter = *iter; + + if (!check_iter_signature(&dict_iter, "{sv}")) { + spa_log_warn(monitor->log, "Invalid BAP Endpoint QoS in DBus"); + return; + } + + while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { + DBusMessageIter it2; + const char *key; + int type; + + dbus_message_iter_recurse(&dict_iter, &it0); + dbus_message_iter_get_basic(&it0, &key); + dbus_message_iter_next(&it0); + dbus_message_iter_recurse(&it0, &it1); + + type = dbus_message_iter_get_arg_type(&it1); + + if (type == DBUS_TYPE_BYTE) { + uint8_t value; + + dbus_message_iter_get_basic(&it1, &value); + spa_log_debug(monitor->log, "ep qos: %s=%d", key, (int)value); + + if (spa_streq(key, "Framing")) + qos->framing = value; + else if (spa_streq(key, "PHY")) + qos->phy = value; + else if (spa_streq(key, "Retransmissions")) + qos->retransmission = value; + } else if (type == DBUS_TYPE_UINT16) { + dbus_uint16_t value; + + dbus_message_iter_get_basic(&it1, &value); + spa_log_debug(monitor->log, "ep qos: %s=%d", key, (int)value); + + if (spa_streq(key, "Latency") || spa_streq(key, "MaximumLatency")) + qos->latency = value; + else if (spa_streq(key, "Context")) + qos->context = value; + else if (spa_streq(key, "SupportedContext")) + qos->supported_context = value; + } else if (type == DBUS_TYPE_UINT32) { + dbus_uint32_t value; + + dbus_message_iter_get_basic(&it1, &value); + spa_log_debug(monitor->log, "ep qos: %s=%d", key, (int)value); + + if (spa_streq(key, "MinimumDelay")) + qos->delay_min = value; + else if (spa_streq(key, "MaximumDelay")) + qos->delay_max = value; + else if (spa_streq(key, "PreferredMinimumDelay")) + qos->preferred_delay_min = value; + else if (spa_streq(key, "PreferredMaximumDelay")) + qos->preferred_delay_max = value; + else if (spa_streq(key, "Locations") || spa_streq(key, "Location")) + qos->locations = value; + } + + dbus_message_iter_next(&dict_iter); + } +} + +static int parse_endpoint_props(struct spa_bt_monitor *monitor, DBusMessageIter *iter, + uint8_t capsA2DP_MAX_CAPS_SIZE, int *caps_size, const char **endpoint_path, + struct bap_endpoint_qos *qos) +{ + DBusMessageIter dict_iter = *iter; + const char *key = NULL; + int type = 0; + + memset(caps, 0, A2DP_MAX_CAPS_SIZE); + *endpoint_path = NULL; + memset(qos, 0, sizeof(*qos)); + + if (!check_iter_signature(&dict_iter, "{sv}")) { + spa_log_warn(monitor->log, "Invalid BAP Endpoint QoS in DBus"); + return -EINVAL; + } + + while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { + DBusMessageIter it3; + + dbus_message_iter_recurse(&dict_iter, &it0); + dbus_message_iter_get_basic(&it0, &key); + dbus_message_iter_next(&it0); + dbus_message_iter_recurse(&it0, &it1); + + type = dbus_message_iter_get_arg_type(&it1); + + if (spa_streq(key, "Capabilities")) { + uint8_t *buf; + + if (type != DBUS_TYPE_ARRAY) + 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, &buf, caps_size); + if (*caps_size > A2DP_MAX_CAPS_SIZE) { + spa_log_error(monitor->log, "%s size:%d too large", key, (int)*caps_size); + return -EINVAL; + } + memcpy(caps, buf, *caps_size); + + spa_log_info(monitor->log, "%p: %s size:%d", monitor, key, *caps_size); + spa_debug_log_mem(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', caps, (size_t)*caps_size); + } else if (spa_streq(key, "Endpoint")) { + if (type != DBUS_TYPE_OBJECT_PATH) + goto bad_property; + + dbus_message_iter_get_basic(&it1, endpoint_path); + + spa_log_info(monitor->log, "%p: %s %s", monitor, key, *endpoint_path); + } else if (spa_streq(key, "QoS")) { + if (!check_iter_signature(&it1, "a{sv}")) + goto bad_property; + + dbus_message_iter_recurse(&it1, &it2); + parse_endpoint_qos(monitor, &it2, qos); + } else if (spa_streq(key, "Locations")) { + dbus_uint32_t value; + + if (type != DBUS_TYPE_UINT32) + goto bad_property; + + dbus_message_iter_get_basic(&it1, &value); + spa_log_debug(monitor->log, "ep qos: %s=%d", key, (int)value); + qos->locations = value; + } + + dbus_message_iter_next(&dict_iter); + } + + return 0; + +bad_property: + spa_log_error(monitor->log, "Property %s of wrong type %c", key, (char)type); + return -EINVAL; +} + static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMessage *m, void *userdata) { struct spa_bt_monitor *monitor = userdata; @@ -635,6 +871,7 @@ spa_autoptr(DBusMessage) r = NULL; int res; const struct media_codec *codec; + struct spa_bt_remote_endpoint *ep; bool sink; const char *err_msg = "Unknown error"; struct spa_dict settings; @@ -670,121 +907,28 @@ */ codec = media_endpoint_to_codec(monitor, path, &sink, NULL); spa_log_debug(monitor->log, "%p: %s codec:%s", monitor, path, codec ? codec->name : "<null>"); - if (!codec) { + if (!codec || !codec->bap || !codec->get_qos) { spa_log_error(monitor->log, "Unsupported codec"); err_msg = "Unsupported codec"; goto error; } - /* Parse transport properties */ - while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) { - const char *key; - DBusMessageIter value, entry; - int type; - - dbus_message_iter_recurse(&props, &entry); - dbus_message_iter_get_basic(&entry, &key); - - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); - - type = dbus_message_iter_get_arg_type(&value); - - if (spa_streq(key, "Capabilities")) { - DBusMessageIter array; - uint8_t *buf; - - if (type != DBUS_TYPE_ARRAY) { - spa_log_error(monitor->log, "Property %s of wrong type %c", key, (char)type); - goto error_invalid; - } - - dbus_message_iter_recurse(&value, &array); - type = dbus_message_iter_get_arg_type(&array); - if (type != DBUS_TYPE_BYTE) { - spa_log_error(monitor->log, "%s is an array of wrong type %c", key, (char)type); - goto error_invalid; - } - - dbus_message_iter_get_fixed_array(&array, &buf, &caps_size); - if (caps_size > (int)sizeof(caps)) { - spa_log_error(monitor->log, "%s size:%d too large", key, (int)caps_size); - goto error_invalid; - } - memcpy(caps, buf, caps_size); - - spa_log_info(monitor->log, "%p: %s %s size:%d", monitor, path, key, caps_size); - spa_debug_log_mem(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', caps, (size_t)caps_size); - } else if (spa_streq(key, "Endpoint")) { - if (type != DBUS_TYPE_OBJECT_PATH) { - spa_log_error(monitor->log, "Property %s of wrong type %c", key, (char)type); - goto error_invalid; - } - - dbus_message_iter_get_basic(&value, &endpoint_path); - - spa_log_info(monitor->log, "%p: %s %s %s", monitor, path, key, endpoint_path); - } else if (type == DBUS_TYPE_BYTE) { - uint8_t v; - dbus_message_iter_get_basic(&value, &v); - - spa_log_info(monitor->log, "%p: %s %s 0x%x", monitor, path, key, (unsigned int)v); - - if (spa_streq(key, "Framing")) - endpoint_qos.framing = v; - else if (spa_streq(key, "PHY")) - endpoint_qos.phy = v; - else - spa_log_info(monitor->log, "Unknown property %s", key); - } else if (type == DBUS_TYPE_UINT16) { - dbus_uint16_t v; - dbus_message_iter_get_basic(&value, &v); - - spa_log_info(monitor->log, "%p: %s %s 0x%x", monitor, path, key, (unsigned int)v); - - if (spa_streq(key, "Latency")) - endpoint_qos.latency = v; - else - spa_log_info(monitor->log, "Unknown property %s", key); - } else if (type == DBUS_TYPE_UINT32) { - dbus_uint32_t v; - dbus_message_iter_get_basic(&value, &v); - - spa_log_info(monitor->log, "%p: %s %s 0x%x", monitor, path, key, (unsigned int)v); - - if (spa_streq(key, "MinimumDelay")) - endpoint_qos.delay_min = v; - else if (spa_streq(key, "MaximumDelay")) - endpoint_qos.delay_max = v; - else if (spa_streq(key, "PreferredMinimumDelay")) - endpoint_qos.preferred_delay_min = v; - else if (spa_streq(key, "PreferredMaximumDelay")) - endpoint_qos.preferred_delay_max = v; - else if (spa_streq(key, "Location")) - spa_scnprintf(locations, sizeof(locations), "%"PRIu32, v); - else - spa_log_info(monitor->log, "Unknown property %s", key); - } else { - spa_log_info(monitor->log, "Unknown property %s", key); - } + /* Parse endpoint properties */ + if (parse_endpoint_props(monitor, &props, caps, &caps_size, &endpoint_path, &endpoint_qos) < 0) + goto error_invalid; + if (endpoint_qos.locations) + spa_scnprintf(locations, sizeof(locations), "%"PRIu32, endpoint_qos.locations); - dbus_message_iter_next(&props); + ep = remote_endpoint_find(monitor, endpoint_path); + if (!ep) { + spa_log_warn(monitor->log, "Unable to find remote endpoint for %s", endpoint_path); + goto error_invalid; } - if (codec->bap) { - struct spa_bt_remote_endpoint *ep; - - ep = remote_endpoint_find(monitor, endpoint_path); - if (!ep) { - spa_log_warn(monitor->log, "Unable to find remote endpoint for %s", endpoint_path); - goto error_invalid; - } - - /* Call of SelectProperties means that local device acts as an initiator - * and therefor remote endpoint is an acceptor - */ - ep->acceptor = true; - } + /* Call of SelectProperties means that local device acts as an initiator + * and therefor remote endpoint is an acceptor + */ + ep->acceptor = true; for (i = 0; i < (int)monitor->global_settings.n_items; ++i) setting_itemsi = monitor->global_settings.itemsi; @@ -812,10 +956,10 @@ &dict); append_basic_array_variant_dict_entry(&dict, "Capabilities", "ay", "y", DBUS_TYPE_BYTE, &config, conf_size); - if (codec->get_qos) { + { struct bap_codec_qos qos; - dbus_bool_t framing; - const char *phy_str; + DBusMessageIter entry, variant, qos_dict; + const char *entry_key = "QoS"; spa_zero(qos); @@ -826,21 +970,29 @@ goto error_invalid; } - append_basic_variant_dict_entry(&dict, "Interval", DBUS_TYPE_UINT32, "u", &qos.interval); - framing = (qos.framing ? TRUE : FALSE); - append_basic_variant_dict_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN, "b", &framing); - if (qos.phy == 0x1) - phy_str = "1M"; - else if (qos.phy == 0x2) - phy_str = "2M"; - else - spa_assert_not_reached(); - append_basic_variant_dict_entry(&dict, "PHY", DBUS_TYPE_STRING, "s", &phy_str); - append_basic_variant_dict_entry(&dict, "SDU", DBUS_TYPE_UINT16, "q", &qos.sdu); - append_basic_variant_dict_entry(&dict, "Retransmissions", DBUS_TYPE_BYTE, "y", &qos.retransmission); - append_basic_variant_dict_entry(&dict, "Latency", DBUS_TYPE_UINT16, "q", &qos.latency); - append_basic_variant_dict_entry(&dict, "Delay", DBUS_TYPE_UINT32, "u", &qos.delay); - append_basic_variant_dict_entry(&dict, "TargetLatency", DBUS_TYPE_BYTE, "y", &qos.target_latency); + dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &entry_key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", &variant); + + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &qos_dict); + + append_basic_variant_dict_entry(&qos_dict, "Interval", DBUS_TYPE_UINT32, "u", &qos.interval); + append_basic_variant_dict_entry(&qos_dict, "Framing", DBUS_TYPE_BYTE, "y", &qos.framing); + append_basic_variant_dict_entry(&qos_dict, "PHY", DBUS_TYPE_BYTE, "y", &qos.phy); + append_basic_variant_dict_entry(&qos_dict, "SDU", DBUS_TYPE_UINT16, "q", &qos.sdu); + append_basic_variant_dict_entry(&qos_dict, "Retransmissions", DBUS_TYPE_BYTE, "y", &qos.retransmission); + append_basic_variant_dict_entry(&qos_dict, "Latency", DBUS_TYPE_UINT16, "q", &qos.latency); + append_basic_variant_dict_entry(&qos_dict, "PresentationDelay", DBUS_TYPE_UINT32, "u", &qos.delay); + append_basic_variant_dict_entry(&qos_dict, "TargetLatency", DBUS_TYPE_BYTE, "y", &qos.target_latency); + + dbus_message_iter_close_container(&variant, &qos_dict); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(&dict, &entry); } dbus_message_iter_close_container(&iter, &dict); @@ -869,16 +1021,6 @@ return NULL; } -static bool check_iter_signature(DBusMessageIter *it, const char *sig) -{ - char *v; - bool res; - v = dbus_message_iter_get_signature(it); - res = spa_streq(v, sig); - dbus_free(v); - return res; -} - static int parse_modalias(const char *modalias, uint16_t *source, uint16_t *vendor, uint16_t *product, uint16_t *version) { @@ -1435,7 +1577,7 @@ int spa_bt_format_vendor_product_id(uint16_t source_id, uint16_t vendor_id, uint16_t product_id, char *vendor_str, int vendor_str_size, char *product_str, int product_str_size) { - char *source_str; + const char *source_str; switch (source_id) { case SOURCE_ID_USB: @@ -3149,40 +3291,36 @@ spa_bt_transport_volume_changed(transport); } else if (spa_streq(key, "Delay")) { - if (transport->profile & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE)) { - uint32_t value; - - if (type != DBUS_TYPE_UINT32) - goto next; - dbus_message_iter_get_basic(&it1, &value); - - spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); - - transport->delay_us = value; - } else { - uint16_t value; + uint16_t value; - if (type != DBUS_TYPE_UINT16) - goto next; - dbus_message_iter_get_basic(&it1, &value); + if (type != DBUS_TYPE_UINT16) + goto next; + dbus_message_iter_get_basic(&it1, &value); - spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); + spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); - transport->delay_us = value * 100; - } + transport->delay_us = value * 100; spa_bt_transport_emit_delay_changed(transport); } - else if (spa_streq(key, "Latency")) { - uint16_t value; + else if (spa_streq(key, "QoS")) { + struct bap_codec_qos_full qos; + DBusMessageIter value; - if (type != DBUS_TYPE_UINT16) + if (!check_iter_signature(&it1, "a{sv}")) goto next; - dbus_message_iter_get_basic(&it1, &value); - spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); + dbus_message_iter_recurse(&it1, &value); + parse_codec_qos(monitor, &value, &qos); + + transport->bap_cig = qos.cig; + transport->bap_cis = qos.cis; + transport->bap_big = qos.big; + transport->bap_bis = qos.bis; + transport->delay_us = qos.qos.delay; + transport->latency_us = (unsigned int)qos.qos.latency * 1000; + transport->bap_interval = qos.qos.interval; - transport->latency_us = value * 1000; spa_bt_transport_emit_delay_changed(transport); } else if (spa_streq(key, "Links")) { @@ -3214,71 +3352,7 @@ dbus_message_iter_next(&iter); } } - else if (spa_streq(key, "Interval")) { - uint32_t value; - - if (type != DBUS_TYPE_UINT32) - goto next; - dbus_message_iter_get_basic(&it1, &value); - - spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); - transport->bap_interval = value; - } - else if (spa_streq(key, "Framing")) { - dbus_bool_t value; - - if (type != DBUS_TYPE_BOOLEAN) - goto next; - dbus_message_iter_get_basic(&it1, &value); - - spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); - } - else if (spa_streq(key, "SDU")) { - uint16_t value; - - if (type != DBUS_TYPE_UINT16) - goto next; - dbus_message_iter_get_basic(&it1, &value); - - spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); - } - else if (spa_streq(key, "Retransmissions")) { - uint8_t value; - - if (type != DBUS_TYPE_BYTE) - goto next; - dbus_message_iter_get_basic(&it1, &value); - - spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); - } - else if (spa_streq(key, "CIG") || spa_streq(key, "CIS")) { - uint8_t value; - if (type != DBUS_TYPE_BYTE) - goto next; - dbus_message_iter_get_basic(&it1, &value); - - spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); - - if (spa_streq(key, "CIG")) - transport->bap_cig = value; - else - transport->bap_cis = value; - } - else if (spa_streq(key, "BIG") || spa_streq(key, "BIS")) { - uint8_t value; - - if (type != DBUS_TYPE_BYTE) - goto next; - dbus_message_iter_get_basic(&it1, &value); - - spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); - - if (spa_streq(key, "BIG")) - transport->bap_big = value; - else - transport->bap_bis = value; - } next: dbus_message_iter_next(props_iter); }
View file
pipewire-0.3.80.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.81.tar.gz/spa/plugins/bluez5/bluez5-device.c
Changed
@@ -1608,7 +1608,17 @@ name = "audio-gateway"; desc = _("Audio Gateway (A2DP Source & HSP/HFP AG)"); } - priority = 256; + + /* + * If the remote is A2DP sink and HF, we likely should prioritize being + * A2DP sender, not gateway. This can occur in PW<->PW if RFCOMM gets + * connected both as AG and HF. + */ + if ((device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) && + (device->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT)) + priority = 15; + else + priority = 256; break; } case DEVICE_PROFILE_A2DP:
View file
pipewire-0.3.80.tar.gz/spa/plugins/support/loop.c -> pipewire-0.3.81.tar.gz/spa/plugins/support/loop.c
Changed
@@ -16,6 +16,7 @@ #include <spa/support/plugin.h> #include <spa/utils/list.h> #include <spa/utils/names.h> +#include <spa/utils/ratelimit.h> #include <spa/utils/result.h> #include <spa/utils/type.h> #include <spa/utils/ringbuffer.h> @@ -64,6 +65,7 @@ struct spa_source *wakeup; int ack_fd; + struct spa_ratelimit rate_limit; struct spa_ringbuffer buffer; uint8_t *buffer_data; @@ -94,6 +96,13 @@ }; /** \endcond */ +static inline uint64_t get_time_ns(struct spa_system *system) +{ + struct timespec ts; + spa_system_clock_gettime(system, CLOCK_MONOTONIC, &ts); + return SPA_TIMESPEC_TO_NSEC(&ts); +} + static int loop_add_source(void *object, struct spa_source *source) { struct impl *impl = object; @@ -267,8 +276,12 @@ item->item_size = SPA_ROUND_UP_N(l0 + size, ITEM_ALIGN); } if (avail < item->item_size) { - spa_log_warn(impl->log, "%p: queue full %d, need %zd", impl, avail, - item->item_size); + int suppressed; + uint64_t nsec = get_time_ns(impl->system); + if ((suppressed = spa_ratelimit_test(&impl->rate_limit, nsec)) >= 0) { + spa_log_warn(impl->log, "%p: queue full %d, need %zd (%d suppressed)", + impl, avail, item->item_size, suppressed); + } return -EPIPE; } if (data && size > 0) @@ -1004,6 +1017,8 @@ res = -EINVAL; goto error_exit; } + impl->rate_limit.interval = 2 * SPA_NSEC_PER_SEC; + impl->rate_limit.burst = 1; if ((res = spa_system_pollfd_create(impl->system, SPA_FD_CLOEXEC)) < 0) { spa_log_error(impl->log, "%p: can't create pollfd: %s",
View file
pipewire-0.3.80.tar.gz/spa/plugins/support/node-driver.c -> pipewire-0.3.81.tar.gz/spa/plugins/support/node-driver.c
Changed
@@ -95,14 +95,18 @@ #ifdef CLOCK_MONOTONIC_RAW { "monotonic-raw", CLOCK_MONOTONIC_RAW }, #endif +#ifdef CLOCK_BOOTTIME { "boottime", CLOCK_BOOTTIME }, +#endif }; static bool clock_for_timerfd(clockid_t id) { return id == CLOCK_REALTIME || - id == CLOCK_MONOTONIC || - id == CLOCK_BOOTTIME; +#ifdef CLOCK_BOOTTIME + id == CLOCK_BOOTTIME || +#endif + id == CLOCK_MONOTONIC; } static clockid_t clock_name_to_id(const char *name)
View file
pipewire-0.3.80.tar.gz/spa/tests/stress-ringbuffer.c -> pipewire-0.3.81.tar.gz/spa/tests/stress-ringbuffer.c
Changed
@@ -11,10 +11,11 @@ #define ARRAY_SIZE 63 #define MAX_VALUE 0x10000 -#if defined(__FreeBSD__) || defined(__MidnightBSD__) +#if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined (__GNU__) #include <sys/param.h> #if (__FreeBSD_version >= 1400000 && __FreeBSD_version < 1400043) \ - || (__FreeBSD_version < 1300523) || defined(__MidnightBSD__) + || (__FreeBSD_version < 1300523) || defined(__MidnightBSD__) \ + || defined (__GNU__) static int sched_getcpu(void) { return -1; }; #endif #endif
View file
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/demonic.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/demonic.conf
Changed
@@ -4,8 +4,9 @@ # ~/.config/pipewire/filter-chain.conf.d/ # context.modules = - { name = libpipewire-module-filter-chain - args = { + { name = libpipewire-module-filter-chain + flags = nofail + args = { #audio.format = F32 #audio.rate = 48000 audio.channels = 2
View file
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/sink-dolby-surround.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/sink-dolby-surround.conf
Changed
@@ -5,6 +5,7 @@ # context.modules = { name = libpipewire-module-filter-chain + flags = nofail args = { node.description = "Dolby Surround Sink" media.name = "Dolby Surround Sink"
View file
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/sink-matrix-spatialiser.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/sink-matrix-spatialiser.conf
Changed
@@ -8,6 +8,7 @@ context.modules = { name = libpipewire-module-filter-chain + flags = nofail args = { node.description = "Matrix Spatialiser" media.name = "Matrix Spatialiser"
View file
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/sink-virtual-surround-5.1-kemar.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/sink-virtual-surround-5.1-kemar.conf
Changed
@@ -3,8 +3,11 @@ # Copy this file into a conf.d/ directory such as # ~/.config/pipewire/filter-chain.conf.d/ # +# Adjust the paths to the convolver files to match your system +# context.modules = { name = libpipewire-module-filter-chain + flags = nofail args = { node.description = "Virtual Surround Sink" media.name = "Virtual Surround Sink"
View file
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/sink-virtual-surround-7.1-hesuvi.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/sink-virtual-surround-7.1-hesuvi.conf
Changed
@@ -3,8 +3,11 @@ # Copy this file into a conf.d/ directory such as # ~/.config/pipewire/filter-chain.conf.d/ # +# Adjust the paths to the convolver files to match your system +# context.modules = { name = libpipewire-module-filter-chain + flags = nofail args = { node.description = "Virtual Surround Sink" media.name = "Virtual Surround Sink"
View file
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/source-rnnoise.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/source-rnnoise.conf
Changed
@@ -3,8 +3,11 @@ # Copy this file into a conf.d/ directory such as # ~/.config/pipewire/filter-chain.conf.d/ # +# Adjust the paths to the rnnoise plugin to match your system +# context.modules = { name = libpipewire-module-filter-chain + flags = nofail args = { node.description = "Noise Canceling source" media.name = "Noise Canceling source" @@ -13,7 +16,13 @@ { type = ladspa name = rnnoise - plugin = librnnoise_ladspa + # The path to the plugin. The suffix .so is appended to + # this string and then the file is then located in the directories + # listed in the environment variable LADSPA_PATH or + # /usr/lib64/ladspa, /usr/lib/ladspa or the system library directory + # as a fallback. + # You might want to use an absolute path here to avoid problems. + plugin = "librnnoise_ladspa" label = noise_suppressor_stereo control = { "VAD Threshold (%)" 50.0
View file
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/spatializer-7.1.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/spatializer-7.1.conf
Changed
@@ -3,8 +3,11 @@ # Copy this file into a conf.d/ directory such as # ~/.config/pipewire/filter-chain.conf.d/ # +# Adjust the paths to the sofa file to match your system. +# context.modules = { name = libpipewire-module-filter-chain + flags = nofail args = { node.description = "Spatial Sink" media.name = "Spatial Sink"
View file
pipewire-0.3.80.tar.gz/src/daemon/filter-chain/spatializer-single.conf -> pipewire-0.3.81.tar.gz/src/daemon/filter-chain/spatializer-single.conf
Changed
@@ -4,8 +4,11 @@ # Copy this file into a conf.d/ directory such as # ~/.config/pipewire/filter-chain.conf.d/ # +# Adjust the paths to the sofa files to match your system +# context.modules = { name = libpipewire-module-filter-chain + flags = nofail args = { node.description = "3D Sink" media.name = "3D Sink"
View file
pipewire-0.3.80.tar.gz/src/daemon/pipewire-pulse.conf.in -> pipewire-0.3.81.tar.gz/src/daemon/pipewire-pulse.conf.in
Changed
@@ -28,6 +28,7 @@ args = { nice.level = -11 #rt.prio = 88 + rt.prio = 65 #rt.time.soft = -1 #rt.time.hard = -1 }
View file
pipewire-0.3.80.tar.gz/src/daemon/pipewire.conf.in -> pipewire-0.3.81.tar.gz/src/daemon/pipewire.conf.in
Changed
@@ -50,6 +50,8 @@ # enables autoloading of access module, when disabled an alternative # access module needs to be loaded. module.access = true + # enables autoloading of module-jackdbus-detect + module.jackdbus-detect = true } context.spa-libs = { @@ -179,6 +181,29 @@ flags = ifexists nofail condition = { module.x11.bell = true } } + { name = libpipewire-module-jackdbus-detect + args { + #jack.library = libjack.so.0 + #jack.server = null + #jack.client-name = PipeWire + #jack.connect = true + #tunnel.mode = duplex # source|sink|duplex + source.props = { + #audio.channels = 2 + #midi.ports = 1 + #audio.position = FL FR + # extra sink properties + } + sink.props = { + #audio.channels = 2 + #midi.ports = 1 + #audio.position = FL FR + # extra sink properties + } + } + flags = ifexists nofail + condition = { module.jackdbus-detect = true } + } context.objects =
View file
pipewire-0.3.80.tar.gz/src/modules/module-access.c -> pipewire-0.3.81.tar.gz/src/modules/module-access.c
Changed
@@ -143,7 +143,7 @@ * (in the current namespace). */ -#if defined(__linux__) +#if defined(__linux__) || defined(__GNU__) spa_scnprintf(path, sizeof(path), "/proc/%u/exe", pid); #elif defined(__FreeBSD__) || defined(__MidnightBSD__) spa_scnprintf(path, sizeof(path), "/proc/%u/file", pid);
View file
pipewire-0.3.80.tar.gz/src/modules/module-ffado-driver.c -> pipewire-0.3.81.tar.gz/src/modules/module-ffado-driver.c
Changed
@@ -39,7 +39,7 @@ * ## Module Options * * - `driver.mode`: the driver mode, sink|source|duplex, default duplex - * - `ffado.devices`: array of devices to open, default hw:0 + * - `ffado.devices`: array of devices to open, default "hw:0" * - `ffado.period-size`: period size,default 1024 * - `ffado.period-num`: period number,default 3 * - `ffado.sample-rate`: sample-rate, default 48000 @@ -71,7 +71,7 @@ * { name = libpipewire-module-ffado-driver * args = { * #driver.mode = duplex - * #ffado.devices = hw:0 + * #ffado.devices = "hw:0" * #ffado.period-size = 1024 * #ffado.period-num = 3 * #ffado.sample-rate = 48000 @@ -100,7 +100,7 @@ #define MAX_PORTS 128 -#define DEFAULT_DEVICES " hw:0 " +#define DEFAULT_DEVICES " \"hw:0\" " #define DEFAULT_PERIOD_SIZE 1024 #define DEFAULT_PERIOD_NUM 3 #define DEFAULT_SAMPLE_RATE 48000 @@ -113,7 +113,7 @@ #define MODULE_USAGE "( remote.name=<remote> ) " \ "( driver.mode=<sink|source|duplex> ) " \ - "( ffado.devices=<devices array size, default hw:0> ) " \ + "( ffado.devices=<devices array size, default \"hw:0\"> ) " \ "( ffado.period-size=<period size, default 1024> ) " \ "( ffado.period-num=<period num, default 3> ) " \ "( ffado.sample-rate=<sampe rate, default 48000> ) " \
View file
pipewire-0.3.80.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.81.tar.gz/src/modules/module-filter-chain.c
Changed
@@ -82,6 +82,12 @@ * * inputs = <portname> ... * outputs = <portname> ... + * capture.volumes = + * { control = <portname> min = <value> max = <value> scale = <scale> } ... + * + * playback.volumes = + * { control = <portname> min = <value> max = <value> scale = <scale> } ... + * * } *\endcode * @@ -137,6 +143,20 @@ * graph will then be duplicated as many times to match the number of input/output * channels of the streams. * + * ### Volumes + * + * Normally the volume of the sink/source is handled by the stream software volume. + * With the capture.volumes and playback.volumes properties this can be handled + * by a control port in the graph instead. + * + * The min and max values (defaults 0.0 and 1.0) respectively can be used to scale + * and translate the volume min and max values. + * + * Normally the control values are linear and it is assumed that the plugin does not + * perform any scaling to the values. This can be changed with the scale property. By + * default this is linear but it can be set to cubic when the control applies a + * cubic transformation. + * * ## Builtin filters * * There are some useful builtin filters available. You select them with the label @@ -576,7 +596,7 @@ uint32_t n_links; uint32_t external; - float control_data; + float control_dataMAX_HNDL; float *audio_dataMAX_HNDL; }; @@ -625,6 +645,20 @@ void **hndl; }; +struct volume { + bool mute; + uint32_t n_volumes; + float volumesSPA_AUDIO_MAX_CHANNELS; + + uint32_t n_ports; + struct port *portsSPA_AUDIO_MAX_CHANNELS; + float minSPA_AUDIO_MAX_CHANNELS; + float maxSPA_AUDIO_MAX_CHANNELS; +#define SCALE_LINEAR 0 +#define SCALE_CUBIC 1 + int scaleSPA_AUDIO_MAX_CHANNELS; +}; + struct graph { struct impl *impl; @@ -643,6 +677,9 @@ uint32_t n_control; struct port **control_port; + struct volume capture_volume; + struct volume playback_volume; + unsigned instantiated:1; }; @@ -962,35 +999,48 @@ spa_pod_builder_string(b, name); if (p->hint & FC_HINT_BOOLEAN) { - spa_pod_builder_bool(b, port->control_data <= 0.0f ? false : true); + spa_pod_builder_bool(b, port->control_data0 <= 0.0f ? false : true); } else if (p->hint & FC_HINT_INTEGER) { - spa_pod_builder_int(b, port->control_data); + spa_pod_builder_int(b, port->control_data0); } else { - spa_pod_builder_float(b, port->control_data); + spa_pod_builder_float(b, port->control_data0); } } spa_pod_builder_pop(b, &f1); return spa_pod_builder_pop(b, &f0); } +static int port_set_control_value(struct port *port, float *value, uint32_t id) +{ + struct node *node = port->node; + struct descriptor *desc = node->desc; + float old; + + old = port->control_dataid; + port->control_dataid = value ? *value : desc->default_controlport->idx; + pw_log_info("control %d %d ('%s') from %f to %f", port->idx, id, + desc->desc->portsport->p.name, old, port->control_dataid); + node->control_changed = old != port->control_dataid; + return node->control_changed ? 1 : 0; +} + static int set_control_value(struct node *node, const char *name, float *value) { - struct descriptor *desc; struct port *port; - float old; + int count = 0; + uint32_t i, n_hndl; port = find_port(node, name, FC_PORT_INPUT | FC_PORT_CONTROL); if (port == NULL) return -ENOENT; - node = port->node; - desc = node->desc; + /* 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); - old = port->control_data; - port->control_data = value ? *value : desc->default_controlport->idx; - pw_log_info("control %d ('%s') from %f to %f", port->idx, name, old, port->control_data); - node->control_changed = old != port->control_data; - return node->control_changed ? 1 : 0; + return count; } static int parse_params(struct graph *graph, const struct spa_pod *pod) @@ -1082,17 +1132,115 @@ spa_pod_dynamic_builder_clean(&b); } -static void param_props_changed(struct impl *impl, const struct spa_pod *param) +static int sync_volume(struct graph *graph, struct volume *vol) +{ + uint32_t i; + int res = 0; + + if (vol->n_ports == 0) + return 0; + for (i = 0; i < vol->n_volumes; i++) { + uint32_t n_port = i % vol->n_ports, n_hndl; + struct port *p = vol->portsn_port; + float v = vol->mute ? 0.0f : vol->volumesi; + switch (vol->scalen_port) { + case SCALE_CUBIC: + v = cbrt(v); + break; + } + 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); + } + return res; +} + +static void param_props_changed(struct impl *impl, const struct spa_pod *param, + bool capture) { struct spa_pod_object *obj = (struct spa_pod_object *) param; + struct spa_pod_frame f1; const struct spa_pod_prop *prop; struct graph *graph = &impl->graph; int changed = 0; + char buf1024; + struct spa_pod_dynamic_builder b; + struct volume *vol = capture ? &graph->capture_volume : + &graph->playback_volume; + bool do_volume = false; + + spa_pod_dynamic_builder_init(&b, buf, sizeof(buf), 1024); + spa_pod_builder_push_object(&b.b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); SPA_POD_OBJECT_FOREACH(obj, prop) { - if (prop->key == SPA_PROP_params) + switch (prop->key) { + case SPA_PROP_params: changed += parse_params(graph, &prop->value); + spa_pod_builder_raw_padded(&b.b, prop, SPA_POD_PROP_SIZE(prop)); + break; + case SPA_PROP_mute: + { + bool mute; + if (spa_pod_get_bool(&prop->value, &mute) == 0) { + if (vol->mute != mute) { + vol->mute = mute; + do_volume = true; + } + } + spa_pod_builder_raw_padded(&b.b, prop, SPA_POD_PROP_SIZE(prop)); + break; + } + case SPA_PROP_channelVolumes: + { + uint32_t i, n_vols; + float volsSPA_AUDIO_MAX_CHANNELS; + + if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, + SPA_AUDIO_MAX_CHANNELS)) > 0) { + if (vol->n_volumes != n_vols) + do_volume = true; + vol->n_volumes = n_vols; + for (i = 0; i < n_vols; i++) { + float v = volsi; + if (v != vol->volumesi) { + vol->volumesi = v; + do_volume = true; + } + } + } + spa_pod_builder_raw_padded(&b.b, prop, SPA_POD_PROP_SIZE(prop)); + break; + } + case SPA_PROP_softVolumes: + case SPA_PROP_softMute: + break; + default: + spa_pod_builder_raw_padded(&b.b, prop, SPA_POD_PROP_SIZE(prop)); + break; + } } + if (do_volume && vol->n_ports != 0) { + float soft_volsSPA_AUDIO_MAX_CHANNELS; + uint32_t i; + + for (i = 0; i < vol->n_volumes; i++) + soft_volsi = (vol->mute || vol->volumesi == 0.0f) ? 0.0f : 1.0f; + + spa_pod_builder_prop(&b.b, SPA_PROP_softMute, 0); + spa_pod_builder_bool(&b.b, vol->mute); + spa_pod_builder_prop(&b.b, SPA_PROP_softVolumes, 0); + spa_pod_builder_array(&b.b, sizeof(float), SPA_TYPE_Float, + vol->n_volumes, soft_vols); + param = spa_pod_builder_pop(&b.b, &f0); + + sync_volume(graph, vol); + pw_stream_set_param(capture ? impl->capture : + impl->playback, SPA_PARAM_Props, param); + } + + spa_pod_dynamic_builder_clean(&b); + if (changed > 0) { struct node *node; @@ -1101,6 +1249,7 @@ update_props_param(impl); } + } static void param_latency_changed(struct impl *impl, const struct spa_pod *param) @@ -1196,7 +1345,8 @@ } } -static void param_changed(void *data, uint32_t id, const struct spa_pod *param) +static void param_changed(void *data, uint32_t id, const struct spa_pod *param, + bool capture) { struct impl *impl = data; struct graph *graph = &impl->graph; @@ -1219,7 +1369,7 @@ } case SPA_PARAM_Props: if (param != NULL) - param_props_changed(impl, param); + param_props_changed(impl, param, capture); break; case SPA_PARAM_Latency: param_latency_changed(impl, param); @@ -1231,8 +1381,13 @@ return; error: - pw_stream_set_error(impl->capture, res, "can't start graph: %s", - spa_strerror(res)); + pw_stream_set_error(capture ? impl->capture : impl->playback, + res, "can't start graph: %s", spa_strerror(res)); +} + +static void capture_param_changed(void *data, uint32_t id, const struct spa_pod *param) +{ + param_changed(data, id, param, true); } static const struct pw_stream_events in_stream_events = { @@ -1241,9 +1396,14 @@ .process = capture_process, .io_changed = io_changed, .state_changed = state_changed, - .param_changed = param_changed + .param_changed = capture_param_changed }; +static void playback_param_changed(void *data, uint32_t id, const struct spa_pod *param) +{ + param_changed(data, id, param, false); +} + static void playback_destroy(void *d) { struct impl *impl = d; @@ -1257,7 +1417,7 @@ .process = playback_process, .io_changed = io_changed, .state_changed = state_changed, - .param_changed = param_changed, + .param_changed = playback_param_changed, }; static int setup_streams(struct impl *impl) @@ -1785,6 +1945,91 @@ } /** + * { + * control = name:portname + * min = <float, defaukt 0.0> + * max = <float, default 1.0> + * scale = <string, default "linear", options "linear","cubic"> + * } + */ +static int parse_volume(struct graph *graph, struct spa_json *json, bool capture) +{ + char key256; + char control256 = ""; + char scale64 = "linear"; + float min = 0.0f, max = 1.0f; + const char *val; + struct node *def_control; + struct port *port; + struct volume *vol = capture ? &graph->capture_volume : + &graph->playback_volume; + + if (spa_list_is_empty(&graph->node_list)) { + pw_log_error("can't set volume in graph without nodes"); + return -EINVAL; + } + while (spa_json_get_string(json, key, sizeof(key)) > 0) { + if (spa_streq(key, "control")) { + if (spa_json_get_string(json, control, sizeof(control)) <= 0) { + pw_log_error("control expects a string"); + return -EINVAL; + } + } + else if (spa_streq(key, "min")) { + if (spa_json_get_float(json, &min) <= 0) { + pw_log_error("min expects a float"); + return -EINVAL; + } + } + else if (spa_streq(key, "max")) { + if (spa_json_get_float(json, &max) <= 0) { + pw_log_error("max expects a float"); + return -EINVAL; + } + } + else if (spa_streq(key, "scale")) { + if (spa_json_get_string(json, scale, sizeof(scale)) <= 0) { + pw_log_error("scale expects a string"); + return -EINVAL; + } + } + else if (spa_json_next(json, &val) < 0) + break; + } + if (capture) + def_control = spa_list_first(&graph->node_list, struct node, link); + else + def_control = spa_list_last(&graph->node_list, struct node, link); + + port = find_port(def_control, control, FC_PORT_INPUT | FC_PORT_CONTROL); + if (port == NULL) { + pw_log_error("unknown control port %s", control); + return -ENOENT; + } + if (vol->n_ports >= SPA_AUDIO_MAX_CHANNELS) { + pw_log_error("too many volume controls"); + return -ENOSPC; + } + if (spa_streq(scale, "linear")) { + vol->scalevol->n_ports = SCALE_LINEAR; + } else if (spa_streq(scale, "cubic")) { + vol->scalevol->n_ports = SCALE_CUBIC; + } else { + pw_log_error("Invalid scale value '%s', use one of linear or cubic", scale); + return -EINVAL; + } + pw_log_info("volume %d: \"%s:%s\" min:%f max:%f scale:%s", vol->n_ports, port->node->name, + port->node->desc->desc->portsport->p.name, min, max, scale); + + vol->portsvol->n_ports = port; + vol->minvol->n_ports = min; + vol->maxvol->n_ports = max; + vol->n_ports++; + + return 0; +} + +/** * type = ladspa * name = rev * plugin = g2reverb @@ -1896,7 +2141,7 @@ port->external = SPA_ID_INVALID; port->p = desc->controli; spa_list_init(&port->link_list); - port->control_data = desc->default_controli; + port->control_data0 = desc->default_controli; } for (i = 0; i < desc->n_notify; i++) { struct port *port = &node->notify_porti; @@ -2041,22 +2286,22 @@ } for (j = 0; j < desc->n_control; j++) { port = &node->control_portj; - d->connect_port(node->hndli, port->p, &port->control_data); + d->connect_port(node->hndli, port->p, &port->control_datai); spa_list_for_each(link, &port->link_list, input_link) { struct port *peer = link->output; pw_log_info("connect control port %s%d:%s %p", node->name, i, d->portsport->p.name, - &peer->control_data); - d->connect_port(node->hndli, port->p, &peer->control_data); + &peer->control_datai); + d->connect_port(node->hndli, port->p, &peer->control_datai); } } for (j = 0; j < desc->n_notify; j++) { port = &node->notify_portj; pw_log_info("connect notify port %s%d:%s %p", node->name, i, d->portsport->p.name, - &port->control_data); - d->connect_port(node->hndli, port->p, &port->control_data); + &port->control_datai); + d->connect_port(node->hndli, port->p, &port->control_datai); } if (d->activate) d->activate(node->hndli); @@ -2071,6 +2316,22 @@ return res; } +/* any default values for the controls are set in the first instance + * of the control data. Duplicate this to the other instances now. */ +static void setup_node_controls(struct node *node) +{ + uint32_t i, j; + uint32_t n_hndl = node->n_hndl; + uint32_t n_ports = node->desc->n_control; + struct port *ports = node->control_port; + + for (i = 0; i < n_ports; i++) { + struct port *port = &portsi; + for (j = 1; j < n_hndl; j++) + port->control_dataj = port->control_data0; + } +} + static struct node *find_next_node(struct graph *graph) { struct node *node; @@ -2167,6 +2428,7 @@ desc = node->desc; n_control += desc->n_control; n_nodes++; + setup_node_controls(node); } graph->n_input = 0; graph->input = calloc(n_input * 16 * n_hndl, sizeof(struct graph_port)); @@ -2355,6 +2617,7 @@ { struct spa_json it3; struct spa_json inputs, outputs, *pinputs = NULL, *poutputs = NULL; + struct spa_json cvolumes, pvolumes, *pcvolumes = NULL, *ppvolumes = NULL; struct spa_json nodes, *pnodes = NULL, links, *plinks = NULL; const char *json, *val; char key256; @@ -2402,6 +2665,20 @@ return -EINVAL; } poutputs = &outputs; + } + else if (spa_streq("capture.volumes", key)) { + if (spa_json_enter_array(&it1, &cvolumes) <= 0) { + pw_log_error("capture.volumes expects an array"); + return -EINVAL; + } + pcvolumes = &cvolumes; + } + else if (spa_streq("playback.volumes", key)) { + if (spa_json_enter_array(&it1, &pvolumes) <= 0) { + pw_log_error("playback.volumes expects an array"); + return -EINVAL; + } + ppvolumes = &pvolumes; } else if (spa_json_next(&it1, &val) < 0) break; } @@ -2419,6 +2696,18 @@ return res; } } + if (pcvolumes != NULL) { + while (spa_json_enter_object(pcvolumes, &it2) > 0) { + if ((res = parse_volume(graph, &it2, true)) < 0) + return res; + } + } + if (ppvolumes != NULL) { + while (spa_json_enter_object(ppvolumes, &it2) > 0) { + if ((res = parse_volume(graph, &it2, false)) < 0) + return res; + } + } return setup_graph(graph, pinputs, poutputs); }
View file
pipewire-0.3.80.tar.gz/src/modules/module-filter-chain/dsp-ops-c.c -> pipewire-0.3.81.tar.gz/src/modules/module-filter-chain/dsp-ops-c.c
Changed
@@ -63,9 +63,6 @@ uint32_t i; if (n_src == 0) { dsp_clear_c(ops, dst, n_samples); - } else if (n_src == 1) { - if (dst != src0) - dsp_copy_c(ops, dst, src0, n_samples); } else { if (gain0 == 1.0f) dsp_copy_c(ops, dst, src0, n_samples);
View file
pipewire-0.3.80.tar.gz/src/modules/module-filter-chain/dsp-ops-sse.c -> pipewire-0.3.81.tar.gz/src/modules/module-filter-chain/dsp-ops-sse.c
Changed
@@ -19,7 +19,7 @@ { if (n_src == 0) { memset(dst, 0, n_samples * sizeof(float)); - } else if (n_src == 1) { + } else if (n_src == 1 && gain0 == 1.0f) { if (dst != src0) spa_memcpy(dst, src0, n_samples * sizeof(float)); } else {
View file
pipewire-0.3.80.tar.gz/src/modules/module-pipe-tunnel.c -> pipewire-0.3.81.tar.gz/src/modules/module-pipe-tunnel.c
Changed
@@ -8,6 +8,7 @@ #include <errno.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/uio.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h>
View file
pipewire-0.3.80.tar.gz/src/modules/module-profiler.c -> pipewire-0.3.81.tar.gz/src/modules/module-profiler.c
Changed
@@ -269,7 +269,7 @@ n->count++; } -static struct pw_impl_node_rt_events node_rt_events = { +static const struct pw_impl_node_rt_events node_rt_events = { PW_VERSION_IMPL_NODE_RT_EVENTS, .complete = context_do_profile, .incomplete = context_do_profile,
View file
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/client.c -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/client.c
Changed
@@ -46,7 +46,6 @@ spa_list_init(&client->out_messages); spa_list_init(&client->operations); spa_list_init(&client->pending_samples); - spa_list_init(&client->pending_streams); spa_hook_list_init(&client->listener_list); spa_list_append(&server->clients, &client->link);
View file
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/client.h -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/client.h
Changed
@@ -74,8 +74,6 @@ struct spa_list pending_samples; - struct spa_list pending_streams; - unsigned int disconnect:1; unsigned int new_msg_since_last_flush:1; unsigned int authenticated:1;
View file
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/manager.c -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/manager.c
Changed
@@ -440,6 +440,10 @@ case SPA_PARAM_PropInfo: case SPA_PARAM_Format: case SPA_PARAM_EnumFormat: + /* also emit changed for the Latency param because the stream might + * now be linked. FIXME, we should check if a new link is made for + * a stream and only emit a changed event in that case. */ + case SPA_PARAM_Latency: changed++; break; default:
View file
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c
Changed
@@ -52,6 +52,11 @@ char *args; size_t size; + pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index); + pw_properties_setf(data->source_props, "pulse.module.id", "%u", module->index); + pw_properties_setf(data->sink_props, "pulse.module.id", "%u", module->index); + pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index); + if ((f = open_memstream(&args, &size)) == NULL) return -errno;
View file
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/pulse-server.c
Changed
@@ -832,42 +832,43 @@ } if (spa_streq(o->type, PW_TYPE_INTERFACE_Link)) { - struct stream *s, *t; struct pw_manager_object *peer = NULL; union pw_map_item *item; pw_array_for_each(item, &client->streams.items) { struct stream *s = item->data; const char *peer_name; - if (pw_map_item_is_free(item) || s->pending) + if (pw_map_item_is_free(item)) continue; - if (s->peer_index == SPA_ID_INVALID) + + if (!s->pending && s->peer_index == SPA_ID_INVALID) continue; peer = find_peer_for_link(manager, o, s->id, s->direction); - if (peer == NULL || peer->props == NULL || - peer->index == s->peer_index) + if (peer == NULL) continue; - s->peer_index = peer->index; - - peer_name = pw_properties_get(peer->props, PW_KEY_NODE_NAME); - if (peer_name && s->direction == PW_DIRECTION_INPUT && - pw_manager_object_is_monitor(peer)) { - int len = strlen(peer_name) + 10; - char *tmp = alloca(len); - snprintf(tmp, len, "%s.monitor", peer_name); - peer_name = tmp; - } - if (peer_name != NULL) - stream_send_moved(s, peer->index, peer_name); - } - spa_list_for_each_safe(s, t, &client->pending_streams, link) { - peer = find_peer_for_link(manager, o, s->id, s->direction); - if (peer) { + if (s->pending) { reply_create_stream(s, peer); - spa_list_remove(&s->link); s->pending = false; + } else { + if (s->peer_index == peer->index) + continue; + if (peer->props == NULL) + continue; + + s->peer_index = peer->index; + + peer_name = pw_properties_get(peer->props, PW_KEY_NODE_NAME); + if (peer_name && s->direction == PW_DIRECTION_INPUT && + pw_manager_object_is_monitor(peer)) { + int len = strlen(peer_name) + 10; + char *tmp = alloca(len); + snprintf(tmp, len, "%s.monitor", peer_name); + peer_name = tmp; + } + if (peer_name != NULL) + stream_send_moved(s, peer->index, peer_name); } } } @@ -1244,7 +1245,6 @@ if (peer) { reply_create_stream(stream, peer); } else { - spa_list_append(&stream->client->pending_streams, &stream->link); stream->pending = true; } } @@ -5483,9 +5483,23 @@ const char *str; int res = 0; + debug_messages = pw_log_topic_enabled(SPA_LOG_LEVEL_INFO, pulse_conn); + impl = calloc(1, sizeof(*impl) + user_data_size); if (impl == NULL) - goto error_exit; + goto error_free_props; + + impl->rate_limit.interval = 2 * SPA_NSEC_PER_SEC; + impl->rate_limit.burst = 1; + spa_hook_list_init(&impl->hooks); + spa_list_init(&impl->servers); + pw_map_init(&impl->samples, 16, 16); + pw_map_init(&impl->modules, 16, 16); + spa_list_init(&impl->cleanup_clients); + spa_list_init(&impl->free_messages); + + impl->loop = pw_context_get_main_loop(context); + impl->work_queue = pw_context_get_work_queue(context); if (props == NULL) props = pw_properties_new(NULL, NULL); @@ -5503,25 +5517,6 @@ pw_properties_set(props, "vm.overrides", NULL); } - load_defaults(&impl->defs, props); - - debug_messages = pw_log_topic_enabled(SPA_LOG_LEVEL_INFO, pulse_conn); - - impl->context = context; - impl->loop = pw_context_get_main_loop(context); - impl->props = props; - - impl->work_queue = pw_context_get_work_queue(context); - - spa_hook_list_init(&impl->hooks); - spa_list_init(&impl->servers); - impl->rate_limit.interval = 2 * SPA_NSEC_PER_SEC; - impl->rate_limit.burst = 1; - pw_map_init(&impl->samples, 16, 16); - pw_map_init(&impl->modules, 16, 16); - spa_list_init(&impl->cleanup_clients); - spa_list_init(&impl->free_messages); - str = pw_properties_get(props, "server.address"); if (str == NULL) { pw_properties_setf(props, "server.address", @@ -5544,8 +5539,6 @@ pw_log_warn("%p: can't create pid file: %s", impl, spa_strerror(res)); } - pw_context_add_listener(context, &impl->context_listener, - &context_events, impl); #ifdef HAVE_DBUS str = pw_properties_get(props, "server.dbus-name"); @@ -5554,14 +5547,22 @@ if (strlen(str) > 0) impl->dbus_name = dbus_request_name(context, str); #endif + + load_defaults(&impl->defs, props); + impl->props = spa_steal_ptr(props); + + pw_context_add_listener(context, &impl->context_listener, + &context_events, impl); + impl->context = context; + cmd_run(impl); return (struct pw_protocol_pulse *) impl; error_free: - free(impl); + impl_free(impl); -error_exit: +error_free_props: pw_properties_free(props); if (res < 0)
View file
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/stream.c -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/stream.c
Changed
@@ -103,9 +103,6 @@ pw_log_debug("client %p: stream %p channel:%d", client, stream, stream->channel); - if (stream->pending) - spa_list_remove(&stream->link); - if (stream->drain_tag) reply_error(client, -1, stream->drain_tag, -ENOENT);
View file
pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/stream.h -> pipewire-0.3.81.tar.gz/src/modules/module-protocol-pulse/stream.h
Changed
@@ -34,7 +34,6 @@ }; struct stream { - struct spa_list link; uint32_t create_tag; uint32_t channel; /* index in map */ uint32_t id; /* id of global */
View file
pipewire-0.3.80.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.81.tar.gz/src/modules/module-raop-sink.c
Changed
@@ -44,6 +44,7 @@ #include <pipewire/i18n.h> #include "module-raop/rtsp-client.h" +#include "module-rtp/rtp.h" /** \page page_module_raop_sink PipeWire Module: AirPlay Sink * @@ -137,8 +138,8 @@ #endif #define MD5_HASH_LENGTH (2*MD5_DIGEST_LENGTH) -#define DEFAULT_USER_AGENT "iTunes/11.0.4 (Windows; N)" -#define DEFAULT_USER_NAME "iTunes" +#define DEFAULT_USER_NAME "PipeWire" +#define RAOP_AUTH_USER_NAME "iTunes" #define MAX_PORT_RETRY 128 @@ -147,9 +148,9 @@ #define DEFAULT_CHANNELS 2 #define DEFAULT_POSITION " FL FR " -#define VOLUME_MAX 0.0 -#define VOLUME_DEF -30.0 -#define VOLUME_MIN -144.0 +#define VOLUME_MAX 0.0 +#define VOLUME_MIN -30.0 +#define VOLUME_MUTE -144.0 #define MODULE_USAGE "( raop.ip=<ip address of host> ) " \ "( raop.port=<remote port> ) " \ @@ -228,13 +229,14 @@ unsigned int do_disconnect:1; - uint8_t keyAES_CHUNK_SIZE; /* Key for aes-cbc */ - uint8_t ivAES_CHUNK_SIZE; /* Initialization vector for cbc */ + uint8_t aes_keyAES_CHUNK_SIZE; /* Key for aes-cbc */ + uint8_t aes_ivAES_CHUNK_SIZE; /* Initialization vector for cbc */ EVP_CIPHER_CTX *ctx; uint16_t control_port; int control_fd; struct spa_source *control_source; + struct spa_source *feedback_timer; uint16_t timing_port; int timing_fd; @@ -247,7 +249,7 @@ uint32_t block_size; uint32_t latency; - uint16_t seq; + uint16_t seq, cseq; uint32_t rtptime; uint32_t ssrc; uint32_t sync; @@ -287,7 +289,7 @@ static int aes_encrypt(struct impl *impl, uint8_t *data, int len) { int i = len & ~0xf, clen = i; - EVP_EncryptInit(impl->ctx, EVP_aes_128_cbc(), impl->key, impl->iv); + EVP_EncryptInit(impl->ctx, EVP_aes_128_cbc(), impl->aes_key, impl->aes_iv); EVP_EncryptUpdate(impl->ctx, data, &clen, data, i); return i; } @@ -308,46 +310,104 @@ static int send_udp_sync_packet(struct impl *impl, struct sockaddr *dest_addr, socklen_t addrlen) { - uint32_t pkt5; + uint32_t out3; uint32_t rtptime = impl->rtptime; uint32_t latency = impl->latency; uint64_t transmitted; + struct rtp_header header; + struct iovec iov2; + struct msghdr msg; + int res; - pkt0 = htonl(0x80d40007); + spa_zero(header); + header.v = 2; if (impl->first) - pkt0 |= htonl(0x10000000); - pkt1 = htonl(rtptime - latency); + header.x = 1; + header.m = 1; + header.pt = 84; + header.sequence_number = htons(impl->cseq); + header.timestamp = htonl(rtptime - latency); + + iov0.iov_base = &header; + iov0.iov_len = 8; + transmitted = ntp_now(); - pkt2 = htonl(transmitted >> 32); - pkt3 = htonl(transmitted & 0xffffffff); - pkt4 = htonl(rtptime); + out0 = htonl(transmitted >> 32); + out1 = htonl(transmitted & 0xffffffff); + out2 = htonl(rtptime); + + iov1.iov_base = out; + iov1.iov_len = sizeof(out); + + msg.msg_name = dest_addr; + msg.msg_namelen = addrlen; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + res = sendmsg(impl->control_fd, &msg, MSG_NOSIGNAL); + if (res < 0) { + res = -errno; + pw_log_warn("error sending control packet: %d", res); + } + + impl->cseq = (impl->cseq + 1) & 0xffff; - pw_log_debug("sync: first:%d latency:%u now:%"PRIx64" rtptime:%u", - impl->first, latency, transmitted, rtptime); + pw_log_debug("raop control sync: cseq:%d first:%d latency:%u now:%"PRIx64" rtptime:%u", + impl->cseq, impl->first, latency, transmitted, rtptime); - return sendto(impl->control_fd, pkt, sizeof(pkt), 0, dest_addr, addrlen); + return res; } static int send_udp_timing_packet(struct impl *impl, uint64_t remote, uint64_t received, struct sockaddr *dest_addr, socklen_t addrlen) { - uint32_t pkt8; + uint32_t out6; uint64_t transmitted; + struct rtp_header header; + struct iovec iov2; + struct msghdr msg; + int res; - pkt0 = htonl(0x80d30007); - pkt1 = 0x00000000; - pkt2 = htonl(remote >> 32); - pkt3 = htonl(remote & 0xffffffff); - pkt4 = htonl(received >> 32); - pkt5 = htonl(received & 0xffffffff); - transmitted = ntp_now(); - pkt6 = htonl(transmitted >> 32); - pkt7 = htonl(transmitted & 0xffffffff); + spa_zero(header); + header.v = 2; + header.pt = 83; + header.m = 1; + + iov0.iov_base = &header; + iov0.iov_len = 8; + + out0 = htonl(remote >> 32); + out1 = htonl(remote & 0xffffffff); - pw_log_debug("sync: remote:%"PRIx64" received:%"PRIx64" transmitted:%"PRIx64, + out2 = htonl(received >> 32); + out3 = htonl(received & 0xffffffff); + transmitted = ntp_now(); + out4 = htonl(transmitted >> 32); + out5 = htonl(transmitted & 0xffffffff); + + iov1.iov_base = out; + iov1.iov_len = sizeof(out); + + msg.msg_name = dest_addr; + msg.msg_namelen = addrlen; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + res = sendmsg(impl->timing_fd, &msg, MSG_NOSIGNAL); + if (res < 0) { + res = -errno; + pw_log_warn("error sending timing packet: %d", res); + } + pw_log_debug("raop timing sync: remote:%"PRIx64" received:%"PRIx64" transmitted:%"PRIx64, remote, received, transmitted); - return sendto(impl->timing_fd, pkt, sizeof(pkt), 0, dest_addr, addrlen); + return res; } static int write_codec_pcm(void *dst, void *frames, uint32_t n_frames) @@ -382,8 +442,11 @@ static int flush_to_udp_packet(struct impl *impl) { - const size_t max = 12 + 8 + impl->block_size; - uint32_t pktmax, len, n_frames; + const size_t max = 8 + impl->block_size; + uint32_t outmax, len, n_frames; + struct rtp_header header; + struct iovec iov2; + struct msghdr msg; uint8_t *dst; int res; @@ -394,15 +457,21 @@ impl->sync = 0; send_udp_sync_packet(impl, NULL, 0); } - pkt0 = htonl(0x80600000); + + spa_zero(header); + header.v = 2; + header.pt = 96; if (impl->first) - pkt0 |= htonl((uint32_t)0x80 << 16); - pkt0 |= htonl((uint32_t)impl->seq); - pkt1 = htonl(impl->rtptime); - pkt2 = htonl(impl->ssrc); + header.m = 1; + header.sequence_number = htons(impl->seq); + header.timestamp = htonl(impl->rtptime); + header.ssrc = htonl(impl->ssrc); + + iov0.iov_base = &header; + iov0.iov_len = 12; n_frames = impl->filled / impl->frame_size; - dst = (uint8_t*)&pkt3; + dst = (uint8_t*)&out0; switch (impl->codec) { case CODEC_PCM: @@ -417,11 +486,25 @@ if (impl->encryption == CRYPTO_RSA) aes_encrypt(impl, dst, len); + iov1.iov_base = out; + iov1.iov_len = len; + impl->rtptime += n_frames; impl->seq = (impl->seq + 1) & 0xffff; - pw_log_debug("send %u", len + 12); - res = send(impl->server_fd, pkt, len + 12, 0); + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + res = sendmsg(impl->server_fd, &msg, MSG_NOSIGNAL); + if (res < 0) { + res = -errno; + pw_log_warn("error streaming packet: %d", res); + } impl->first = false; @@ -430,22 +513,34 @@ static int flush_to_tcp_packet(struct impl *impl) { - const size_t max = 16 + 8 + impl->block_size; - uint32_t pktmax, len, n_frames; + const size_t max = 8 + impl->block_size; + uint32_t tcp_pkt1, outmax, len, n_frames; + struct rtp_header header; + struct iovec iov3; + struct msghdr msg; uint8_t *dst; int res; if (!impl->recording) return 0; - pkt0 = htonl(0x24000000); - pkt1 = htonl(0x80e00000); - pkt1 |= htonl((uint32_t)impl->seq); - pkt2 = htonl(impl->rtptime); - pkt3 = htonl(impl->ssrc); + tcp_pkt0 = htonl(0x24000000); + + iov0.iov_base = &tcp_pkt; + iov0.iov_len = 4; + + spa_zero(header); + header.v = 2; + header.pt = 96; + header.sequence_number = htons(impl->seq); + header.timestamp = htonl(impl->rtptime); + header.ssrc = htonl(impl->ssrc); + + iov1.iov_base = &header; + iov1.iov_len = 12; n_frames = impl->filled / impl->frame_size; - dst = (uint8_t*)&pkt4; + dst = (uint8_t*)&out0; switch (impl->codec) { case CODEC_PCM: @@ -460,13 +555,27 @@ if (impl->encryption == CRYPTO_RSA) aes_encrypt(impl, dst, len); - pkt0 |= htonl((uint32_t) len + 12); + out0 |= htonl((uint32_t) len + 12); + + iov2.iov_base = out; + iov2.iov_len = len; impl->rtptime += n_frames; impl->seq = (impl->seq + 1) & 0xffff; - pw_log_debug("send %u", len + 16); - res = send(impl->server_fd, pkt, len + 16, 0); + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + res = sendmsg(impl->server_fd, &msg, MSG_NOSIGNAL); + if (res < 0) { + res = -errno; + pw_log_warn("error streaming packet: %d", res); + } impl->first = false; @@ -783,7 +892,7 @@ return 0; } -static int rtsp_add_auth(struct impl *impl, const char *method) +static int rtsp_add_raop_auth_header(struct impl *impl, const char *method) { char auth1024; @@ -793,7 +902,7 @@ if (spa_streq(impl->auth_method, "Basic")) { char buf256; char enc512; - spa_scnprintf(buf, sizeof(buf), "%s:%s", DEFAULT_USER_NAME, impl->password); + spa_scnprintf(buf, sizeof(buf), "%s:%s", RAOP_AUTH_USER_NAME, impl->password); base64_encode((uint8_t*)buf, strlen(buf), enc, '='); spa_scnprintf(auth, sizeof(auth), "Basic %s", enc); } @@ -805,13 +914,13 @@ url = pw_rtsp_client_get_url(impl->rtsp); - MD5_hash(h1, "%s:%s:%s", DEFAULT_USER_NAME, impl->realm, impl->password); + MD5_hash(h1, "%s:%s:%s", RAOP_AUTH_USER_NAME, impl->realm, impl->password); MD5_hash(h2, "%s:%s", method, url); MD5_hash(resp, "%s:%s:%s", h1, impl->nonce, h2); spa_scnprintf(auth, sizeof(auth), "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"", - DEFAULT_USER_NAME, impl->realm, impl->nonce, url, resp); + RAOP_AUTH_USER_NAME, impl->realm, impl->nonce, url, resp); } else goto error; @@ -821,7 +930,7 @@ return 0; error: - pw_log_error("error adding auth"); + pw_log_error("error adding raop RSA auth"); return -EINVAL; } @@ -831,7 +940,7 @@ { int res; - rtsp_add_auth(impl, method); + rtsp_add_raop_auth_header(impl, method); res = pw_rtsp_client_send(impl->rtsp, method, &impl->headers->dict, content_type, content, reply, impl); @@ -872,10 +981,18 @@ char header128, volstr64; snprintf(header, sizeof(header), "volume: %s\r\n", - spa_dtoa(volstr, sizeof(volstr), impl->volume)); + spa_dtoa(volstr, sizeof(volstr), impl->mute ? VOLUME_MUTE : impl->volume)); return rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, rtsp_log_reply_status); } +static void rtsp_do_post_feedback(void *data, uint64_t expirations) +{ + struct impl *impl = data; + + pw_rtsp_client_url_send(impl->rtsp, "/feedback", "POST", &impl->headers->dict, + NULL, NULL, 0, rtsp_log_reply_status, impl); +} + static int rtsp_record_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content) { struct impl *impl = data; @@ -886,9 +1003,19 @@ struct spa_pod_builder b; struct spa_latency_info latency; char progress128; + struct timespec timeout, interval; pw_log_info("record status: %d", status); + timeout.tv_sec = 2; + timeout.tv_nsec = 0; + interval.tv_sec = 2; + interval.tv_nsec = 0; + + if (!impl->feedback_timer) + impl->feedback_timer = pw_loop_add_timer(impl->loop, rtsp_do_post_feedback, impl); + pw_loop_update_timer(impl->loop, impl->feedback_timer, &timeout, &interval, false); + if ((str = spa_dict_lookup(headers, "Audio-Latency")) != NULL) { uint32_t l; if (spa_atou32(str, &l, 0)) @@ -1241,7 +1368,6 @@ if (!sdp) return -errno; break; - case CRYPTO_AUTH_SETUP: sdp = spa_aprintf("v=0\r\n" "o=iTunes %s 0 IN IP%d %s\r\n" @@ -1260,16 +1386,24 @@ break; case CRYPTO_RSA: - if ((res = pw_getrandom(impl->key, sizeof(impl->key), 0)) < 0 || - (res = pw_getrandom(impl->iv, sizeof(impl->iv), 0)) < 0) + { + uint8_t rac16; + char sac16*4; + + if ((res = pw_getrandom(rac, sizeof(rac), 0)) < 0 || + (res = pw_getrandom(impl->aes_key, sizeof(impl->aes_key), 0)) < 0 || + (res = pw_getrandom(impl->aes_iv, sizeof(impl->aes_iv), 0)) < 0) return res; - rsa_len = rsa_encrypt(impl->key, 16, rsakey); + base64_encode(rac, sizeof(rac), sac, '\0'); + pw_properties_set(impl->headers, "Apple-Challenge", sac); + + rsa_len = rsa_encrypt(impl->aes_key, 16, rsakey); if (rsa_len < 0) return -rsa_len; - base64_encode(rsakey, rsa_len, key, '='); - base64_encode(impl->iv, 16, iv, '='); + base64_encode(rsakey, rsa_len, key, '='); + base64_encode(impl->aes_iv, 16, iv, '='); sdp = spa_aprintf("v=0\r\n" "o=iTunes %s 0 IN IP%d %s\r\n" @@ -1287,6 +1421,7 @@ if (!sdp) return -errno; break; + } default: return -ENOTSUP; } @@ -1294,7 +1429,7 @@ return rtsp_send(impl, "ANNOUNCE", "application/sdp", sdp, rtsp_announce_reply); } -static int rtsp_auth_setup_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content) +static int rtsp_post_auth_setup_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content) { struct impl *impl = data; @@ -1303,7 +1438,7 @@ return rtsp_do_announce(impl); } -static int rtsp_do_auth_setup(struct impl *impl) +static int rtsp_do_post_auth_setup(struct impl *impl) { static const unsigned char content33 = "\x01" @@ -1312,10 +1447,10 @@ return pw_rtsp_client_url_send(impl->rtsp, "/auth-setup", "POST", &impl->headers->dict, "application/octet-stream", content, sizeof(content), - rtsp_auth_setup_reply, impl); + rtsp_post_auth_setup_reply, impl); } -static int rtsp_auth_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content) +static int rtsp_options_auth_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content) { struct impl *impl = data; int res = 0; @@ -1325,7 +1460,7 @@ switch (status) { case 200: if (impl->encryption == CRYPTO_AUTH_SETUP) - res = rtsp_do_auth_setup(impl); + res = rtsp_do_post_auth_setup(impl); else res = rtsp_do_announce(impl); break; @@ -1351,7 +1486,7 @@ return NULL; } -static int rtsp_do_auth(struct impl *impl, const struct spa_dict *headers) +static int rtsp_do_options_auth(struct impl *impl, const struct spa_dict *headers) { const char *str, *realm, *nonce; int n_tokens; @@ -1382,7 +1517,7 @@ impl->nonce = strdup(nonce); } - return rtsp_send(impl, "OPTIONS", NULL, NULL, rtsp_auth_reply); + return rtsp_send(impl, "OPTIONS", NULL, NULL, rtsp_options_auth_reply); } static int rtsp_options_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content) @@ -1394,11 +1529,11 @@ switch (status) { case 401: - res = rtsp_do_auth(impl, headers); + res = rtsp_do_options_auth(impl, headers); break; case 200: if (impl->encryption == CRYPTO_AUTH_SETUP) - res = rtsp_do_auth_setup(impl); + res = rtsp_do_post_auth_setup(impl); else res = rtsp_do_announce(impl); break; @@ -1410,27 +1545,24 @@ { struct impl *impl = data; uint32_t sci2; - uint8_t rac16; - char sac16*4; int res; pw_log_info("connected"); impl->connected = true; - if ((res = pw_getrandom(sci, sizeof(sci), 0)) < 0 || - (res = pw_getrandom(rac, sizeof(rac), 0)) < 0) { + if ((res = pw_getrandom(sci, sizeof(sci), 0)) < 0) { pw_log_error("error generating random data: %s", spa_strerror(res)); return; } pw_properties_setf(impl->headers, "Client-Instance", - "%08x%08x", sci0, sci1); + "%08X%08X", sci0, sci1); - base64_encode(rac, sizeof(rac), sac, '\0'); - pw_properties_set(impl->headers, "Apple-Challenge", sac); + pw_properties_setf(impl->headers, "DACP-ID", + "%08X%08X", sci0, sci1); - pw_properties_set(impl->headers, "User-Agent", DEFAULT_USER_AGENT); + pw_properties_set(impl->headers, "User-Agent", DEFAULT_USER_NAME "/" PACKAGE_VERSION); pw_rtsp_client_send(impl->rtsp, "OPTIONS", &impl->headers->dict, NULL, NULL, rtsp_options_reply, impl); @@ -1463,6 +1595,10 @@ close(impl->timing_fd); impl->timing_fd = -1; } + if (impl->feedback_timer != NULL) { + pw_loop_destroy_source(impl->loop, impl->feedback_timer); + impl->feedback_timer = NULL; + } free(impl->auth_method); impl->auth_method = NULL; free(impl->realm); @@ -1587,10 +1723,13 @@ { bool mute; if (spa_pod_get_bool(&prop->value, &mute) == 0) { - impl->mute = mute; + if (impl->mute != mute) { + impl->mute = mute; + rtsp_send_volume(impl); + } } spa_pod_builder_prop(&b, SPA_PROP_softMute, 0); - spa_pod_builder_bool(&b, impl->mute); + spa_pod_builder_bool(&b, false); spa_pod_builder_raw_padded(&b, prop, SPA_POD_PROP_SIZE(prop)); break; } @@ -1608,12 +1747,11 @@ soft_volsi = 1.0f; } volume /= n_vols; - volume = SPA_CLAMPF(20.0 * log10(volume), VOLUME_MIN, VOLUME_MAX); + volume = SPA_CLAMPF(cbrt(volume) * 30 - 30, VOLUME_MIN, VOLUME_MAX); impl->volume = volume; rtsp_send_volume(impl); } - spa_pod_builder_prop(&b, SPA_PROP_softVolumes, 0); spa_pod_builder_array(&b, sizeof(float), SPA_TYPE_Float, n_vols, soft_vols);
View file
pipewire-0.3.80.tar.gz/src/modules/module-raop/rtsp-client.c -> pipewire-0.3.81.tar.gz/src/modules/module-raop/rtsp-client.c
Changed
@@ -21,12 +21,12 @@ struct message { struct spa_list link; - void *data; size_t len; size_t offset; uint32_t cseq; int (*reply) (void *user_data, int status, const struct spa_dict *headers, const struct pw_array *content); void *user_data; + unsigned char data; }; enum client_recv_state { @@ -598,7 +598,6 @@ fclose(f); - msg->data = SPA_PTROFF(msg, sizeof(*msg), void); msg->len = len - sizeof(*msg); msg->offset = 0; msg->reply = reply;
View file
pipewire-0.3.80.tar.gz/src/modules/module-rt.c -> pipewire-0.3.81.tar.gz/src/modules/module-rt.c
Changed
@@ -35,6 +35,9 @@ #if defined(__FreeBSD__) || defined(__MidnightBSD__) #include <sys/thr.h> #endif +#if defined(__GNU__) +#include <mach.h> +#endif #include <fcntl.h> #include <unistd.h> #include <pthread.h> @@ -221,6 +224,9 @@ long pid; thr_self(&pid); return (pid_t)pid; +#elif defined(__GNU__) + mach_port_t thread = mach_thread_self(); + return (pid_t)thread; #else #error "No gettid impl" #endif
View file
pipewire-0.3.80.tar.gz/src/modules/module-rtp-session.c -> pipewire-0.3.81.tar.gz/src/modules/module-rtp-session.c
Changed
@@ -1335,7 +1335,8 @@ if (spa_streq(service_name, "_pipewire-audio._udp")) { uint32_t mask = 0; for (l = txt; l && compatible; l = l->next) { - char *key, *value, *k = NULL; + const char *k = NULL; + char *key, *value; if (avahi_string_list_get_pair(l, &key, &value, NULL) != 0) break;
View file
pipewire-0.3.80.tar.gz/src/pipewire/conf.c -> pipewire-0.3.81.tar.gz/src/pipewire/conf.c
Changed
@@ -18,7 +18,7 @@ #ifdef HAVE_PWD_H #include <pwd.h> #endif -#if defined(__FreeBSD__) || defined(__MidnightBSD__) +#if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__GNU__) #ifndef O_PATH #define O_PATH 0 #endif @@ -599,10 +599,11 @@ char key256, val1024; const char *str, *value; int match = 0, fail = 0; - int len, skip = 0; + int len; while (spa_json_get_string(&it0, key, sizeof(key)) > 0) { bool success = false; + int skip = 0; if ((len = spa_json_next(&it0, &value)) <= 0) break;
View file
pipewire-0.3.80.tar.gz/src/pipewire/context.c -> pipewire-0.3.81.tar.gz/src/pipewire/context.c
Changed
@@ -1306,9 +1306,10 @@ struct spa_fraction latency = SPA_FRACTION(0, 0); struct spa_fraction max_latency = SPA_FRACTION(0, 0); struct spa_fraction rate = SPA_FRACTION(0, 0); - uint32_t quantum, target_rate, current_rate; + uint32_t target_quantum, target_rate, current_rate, current_quantum; uint64_t quantum_stamp = 0, rate_stamp = 0; - bool force_rate, force_quantum, restore_rate = false; + bool force_rate, force_quantum, restore_rate = false, restore_quantum = false; + bool do_reconfigure = false, was_target_pending; const uint32_t *node_rates; uint32_t node_n_rates, node_def_rate; uint32_t node_max_quantum, node_min_quantum, node_def_quantum, node_rate_quantum; @@ -1364,10 +1365,10 @@ fraction_compare(&s->max_latency, &max_latency) < 0)) max_latency = s->max_latency; - /* largest rate */ + /* largest rate, which is in fact the smallest fraction */ if (rate.denom == 0 || (s->rate.denom > 0 && - fraction_compare(&s->rate, &rate) > 0)) + fraction_compare(&s->rate, &rate) < 0)) rate = s->rate; if (s->active) @@ -1386,6 +1387,12 @@ pw_log_info("(%s-%u) restore rate", n->name, n->info.id); restore_rate = true; } + if (n->forced_quantum && !force_quantum && n->runnable) { + /* A node that was forced to a quantum but is no longer being + * forced can restore its quantum */ + pw_log_info("(%s-%u) restore quantum", n->name, n->info.id); + restore_quantum = true; + } if (force_quantum) lock_quantum = false; @@ -1416,8 +1423,9 @@ rate.denom, target_rate); } + was_target_pending = n->target_pending; + if (target_rate != current_rate) { - bool do_reconfigure = false; /* we doing a rate switch */ pw_log_info("(%s-%u) state:%s new rate:%u/(%u)->%u", n->name, n->info.id, @@ -1427,23 +1435,17 @@ if (force_rate) { if (settings->clock_rate_update_mode == CLOCK_RATE_UPDATE_MODE_HARD) - do_reconfigure = !n->target_pending; + do_reconfigure |= !was_target_pending; } else { if (n->info.state >= PW_NODE_STATE_SUSPENDED) - do_reconfigure = !n->target_pending; + do_reconfigure |= !was_target_pending; } - if (do_reconfigure) - reconfigure_driver(context, n); - /* we're setting the pending rate. This will become the new * current rate in the next iteration of the graph. */ n->target_rate = SPA_FRACTION(1, target_rate); - n->target_pending = true; n->forced_rate = force_rate; + n->target_pending = true; current_rate = target_rate; - /* we might be suspended now and the links need to be prepared again */ - if (do_reconfigure) - goto again; } if (node_rate_quantum != 0 && current_rate != node_rate_quantum) { @@ -1460,26 +1462,42 @@ node_max_quantum = tmp; } - quantum = node_def_quantum; - if (latency.denom != 0) - quantum = (latency.num * current_rate / latency.denom); - quantum = SPA_CLAMP(quantum, node_min_quantum, node_max_quantum); - quantum = SPA_MIN(quantum, lim_quantum); - - if (settings->clock_power_of_two_quantum) - quantum = flp2(quantum); + current_quantum = n->target_quantum; + if (!restore_quantum && + (lock_quantum || n->reconfigure || !running || + (!force_quantum && (n->info.state > PW_NODE_STATE_IDLE)))) + target_quantum = current_quantum; + else { + target_quantum = node_def_quantum; + if (latency.denom != 0) + target_quantum = (latency.num * current_rate / latency.denom); + target_quantum = SPA_CLAMP(target_quantum, node_min_quantum, node_max_quantum); + target_quantum = SPA_MIN(target_quantum, lim_quantum); + + if (settings->clock_power_of_two_quantum) + target_quantum = flp2(target_quantum); + } - if (running && quantum != n->target_quantum && !lock_quantum) { + if (target_quantum != current_quantum) { pw_log_info("(%s-%u) new quantum:%"PRIu64"->%u", n->name, n->info.id, n->target_quantum, - quantum); + target_quantum); /* this is the new pending quantum */ - n->target_quantum = quantum; + n->target_quantum = target_quantum; + n->forced_quantum = force_quantum; n->target_pending = true; + + if (force_quantum) + do_reconfigure |= !was_target_pending; } if (n->target_pending) { + if (do_reconfigure) { + reconfigure_driver(context, n); + /* we might be suspended now and the links need to be prepared again */ + goto again; + } /* we have a pending change. We place the new values in the * pending fields so that they are picked up by the driver in * the next cycle */ @@ -1501,8 +1519,10 @@ n->target_rate = n->rt.position->clock.target_rate; } - pw_log_debug("%p: driver %p running:%d runnable:%d quantum:%u '%s'", - context, n, running, n->runnable, quantum, n->name); + pw_log_debug("%p: driver %p running:%d runnable:%d quantum:%u rate:%u (%"PRIu64"/%u)'%s'", + context, n, running, n->runnable, target_quantum, target_rate, + n->rt.position->clock.target_duration, + n->rt.position->clock.target_rate.denom, n->name); /* first change the node states of the followers to the new target */ spa_list_for_each(s, &n->follower_list, follower_link) {
View file
pipewire-0.3.80.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.81.tar.gz/src/pipewire/impl-link.c
Changed
@@ -1545,6 +1545,7 @@ void pw_impl_link_destroy(struct pw_impl_link *link) { struct impl *impl = SPA_CONTAINER_OF(link, struct impl, this); + bool was_prepared = link->prepared; pw_log_debug("%p: destroy", impl); pw_log_info("(%s) destroy", link->name); @@ -1570,7 +1571,7 @@ pw_global_destroy(link->global); } - if (link->prepared) + if (was_prepared) pw_context_recalc_graph(link->context, "link destroy"); pw_log_debug("%p: free", impl);
View file
pipewire-0.3.80.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.81.tar.gz/src/pipewire/impl-node.c
Changed
@@ -8,6 +8,7 @@ #include <unistd.h> #include <errno.h> #include <time.h> +#include <malloc.h> #include <spa/support/system.h> #include <spa/pod/parser.h> @@ -1321,6 +1322,7 @@ this->target_rate = SPA_FRACTION(1, rate); this->target_quantum = quantum; + this->elapsed = 0; pos->clock.rate = pos->clock.target_rate = this->target_rate; pos->clock.duration = pos->clock.target_duration = this->target_quantum; @@ -1744,8 +1746,10 @@ if (all_ready) a->position.state = SPA_IO_POSITION_STATE_RUNNING; } - if (SPA_LIKELY(a->position.state != SPA_IO_POSITION_STATE_RUNNING)) - a->position.offset += a->position.clock.duration; + if (SPA_LIKELY(a->position.state == SPA_IO_POSITION_STATE_RUNNING)) + node->elapsed += a->position.clock.duration; + + a->position.offset = a->position.clock.position - node->elapsed; } /* Called from the data-loop and it is the starting point for driver nodes. @@ -2103,6 +2107,11 @@ spa_system_close(node->data_system, node->source.fd); free(impl); + +#ifdef HAVE_MALLOC_TRIM + int res = malloc_trim(0); + pw_log_debug("malloc_trim(): %d", res); +#endif } SPA_EXPORT
View file
pipewire-0.3.80.tar.gz/src/pipewire/mem.c -> pipewire-0.3.81.tar.gz/src/pipewire/mem.c
Changed
@@ -25,7 +25,8 @@ PW_LOG_TOPIC_EXTERN(log_mem); #define PW_LOG_TOPIC_DEFAULT log_mem -#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) && !defined(HAVE_MEMFD_CREATE) +#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) && !defined(__GNU__) \ + && !defined(HAVE_MEMFD_CREATE) /* * No glibc wrappers exist for memfd_create(2), so provide our own. * @@ -42,7 +43,7 @@ #define HAVE_MEMFD_CREATE 1 #endif -#if defined(__FreeBSD__) || defined(__MidnightBSD__) +#if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__GNU__) #define MAP_LOCKED 0 #endif
View file
pipewire-0.3.80.tar.gz/src/pipewire/pipewire.c -> pipewire-0.3.81.tar.gz/src/pipewire/pipewire.c
Changed
@@ -7,7 +7,7 @@ #include <unistd.h> #include <limits.h> #include <stdio.h> -#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) +#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) && !defined(__GNU__) #include <sys/prctl.h> #endif #include <pwd.h> @@ -720,7 +720,7 @@ static char namePATH_MAX; spa_memzero(name, sizeof(name)); -#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__MidnightBSD_kernel__) +#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__MidnightBSD_kernel__) || defined(__GNU__) { if (readlink("/proc/self/exe", name, sizeof(name)-1) > 0) { prgname = strrchr(name, '/') + 1; @@ -738,7 +738,7 @@ } } #endif -#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) +#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) && !defined(__GNU__) { if (prctl(PR_GET_NAME, (unsigned long) name, 0, 0, 0) == 0) { prgname = name;
View file
pipewire-0.3.80.tar.gz/src/pipewire/private.h -> pipewire-0.3.81.tar.gz/src/pipewire/private.h
Changed
@@ -22,7 +22,7 @@ #include <spa/utils/result.h> #include <spa/utils/type-info.h> -#if defined(__FreeBSD__) || defined(__MidnightBSD__) +#if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__GNU__) struct ucred { }; #endif @@ -673,6 +673,7 @@ unsigned int suspend_on_idle:1; unsigned int reconfigure:1; unsigned int forced_rate:1; + unsigned int forced_quantum:1; unsigned int trigger:1; /**< has the TRIGGER property and needs an extra * trigger to start processing. */ unsigned int can_suspend:1; @@ -731,6 +732,7 @@ uint64_t target_quantum; uint64_t driver_start; + uint64_t elapsed; /* elapsed time in playing */ void *user_data; /**< extra user data */ };
View file
pipewire-0.3.80.tar.gz/src/pipewire/stream.c -> pipewire-0.3.81.tar.gz/src/pipewire/stream.c
Changed
@@ -157,6 +157,7 @@ unsigned int driving:1; unsigned int using_trigger:1; unsigned int trigger:1; + unsigned int early_process:1; int in_set_param; int in_emit_param_changed; }; @@ -1103,7 +1104,7 @@ * rate matching node (audioconvert) has been scheduled to * update the values. */ ask_more = !impl->process_rt && impl->rate_match == NULL && - queue_is_empty(impl, &impl->queued) && + (impl->early_process || queue_is_empty(impl, &impl->queued)) && !queue_is_empty(impl, &impl->dequeued); pw_log_trace_fp("%p: pop %d %p ask_more:%u %p", stream, b->id, io, ask_more, impl->rate_match); @@ -1121,7 +1122,7 @@ } } else { ask_more = !impl->process_rt && - queue_is_empty(impl, &impl->queued) && + (impl->early_process || queue_is_empty(impl, &impl->queued)) && !queue_is_empty(impl, &impl->dequeued); } @@ -1912,6 +1913,7 @@ impl->node_methods.process = impl_node_process_output; impl->process_rt = SPA_FLAG_IS_SET(flags, PW_STREAM_FLAG_RT_PROCESS); + impl->early_process = SPA_FLAG_IS_SET(flags, PW_STREAM_FLAG_EARLY_PROCESS); impl->impl_node.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_Node,
View file
pipewire-0.3.80.tar.gz/src/pipewire/stream.h -> pipewire-0.3.81.tar.gz/src/pipewire/stream.h
Changed
@@ -394,6 +394,11 @@ * does a trigger_process() that will then * dequeue/queue a buffer from another process() * function. since 0.3.73 */ + PW_STREAM_FLAG_EARLY_PROCESS = (1 << 11), /**< Call process as soon as there is a buffer + * to dequeue. This is only relevant for + * playback and when not using RT_PROCESS. It + * can be used to keep the maximum number of + * buffers queued. Since 0.3.81 */ }; /** Create a new unconneced \ref pw_stream
View file
pipewire-0.3.80.tar.gz/src/pipewire/thread.c -> pipewire-0.3.81.tar.gz/src/pipewire/thread.c
Changed
@@ -53,6 +53,9 @@ } #endif #endif +#if defined(__GNU__) +int pthread_setname_np(pthread_t thread, const char *name) { return 0; } +#endif static struct spa_thread *impl_create(void *object, const struct spa_dict *props,
View file
pipewire-0.3.80.tar.gz/src/pipewire/utils.h -> pipewire-0.3.81.tar.gz/src/pipewire/utils.h
Changed
@@ -15,6 +15,7 @@ #ifndef _POSIX_C_SOURCE # include <sys/mount.h> #endif +#include <errno.h> #ifndef ENODATA #define ENODATA 9919
View file
pipewire-0.3.81.tar.gz/src/tools/dfffile.c
Added
@@ -0,0 +1,313 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#include <errno.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <math.h> + +#include <spa/utils/string.h> +#include <spa/debug/mem.h> + +#include "dfffile.h" + +struct dff_file { + uint8_t *data; + size_t size; + + int mode; + int fd; + + struct dff_file_info info; + + uint8_t *p; + size_t offset; +}; + +struct dff_chunk { + uint32_t id; + uint64_t size; + void *data; +}; + +#define FOURCC(a,b,c,d) (d | (c << 8) | (b << 16) | (a << 24)) + +static inline uint16_t parse_be16(const uint8_t *in) +{ + return (in0 << 8) | in1; +} +static inline uint32_t parse_be32(const uint8_t *in) +{ + return FOURCC(in0, in1, in2, in3); +} + +static inline uint64_t parse_be64(const uint8_t *in) +{ + uint64_t res = in7; + res |= ((uint64_t)in6) << 8; + res |= ((uint64_t)in5) << 16; + res |= ((uint64_t)in4) << 24; + res |= ((uint64_t)in3) << 32; + res |= ((uint64_t)in2) << 40; + res |= ((uint64_t)in1) << 48; + res |= ((uint64_t)in0) << 56; + return res; +} + +static inline int f_avail(struct dff_file *f) +{ + if (f->p < f->data + f->size) + return f->size + f->data - f->p; + return 0; +} + +static int read_chunk(struct dff_file *f, struct dff_chunk *c) +{ + if (f_avail(f) < 12) + return -ENOSPC; + + c->id = parse_be32(f->p); /* id of this chunk */ + c->size = parse_be64(f->p + 4); /* size of this chunk */ + f->p += 12; + c->data = f->p; + return 0; +} + +static int skip_chunk(struct dff_file *f, const struct dff_chunk *c) +{ + f->p = SPA_PTROFF(c->data, c->size, uint8_t); + return 0; +} + +static int read_PROP(struct dff_file *f, struct dff_chunk *prop) +{ + struct dff_chunk c1; + int res; + + if (f_avail(f) < 4 || + memcmp(prop->data, "SND ", 4) != 0) + return -EINVAL; + f->p += 4; + + while (f->p < SPA_PTROFF(prop->data, prop->size, uint8_t)) { + if ((res = read_chunk(f, &c0)) < 0) + return res; + + switch (c0.id) { + case FOURCC('F', 'S', ' ', ' '): + f->info.rate = parse_be32(f->p); + break; + case FOURCC('C', 'H', 'N', 'L'): + f->info.channels = parse_be16(f->p); + switch (f->info.channels) { + case 2: + f->info.channel_type = 2; + break; + case 5: + f->info.channel_type = 6; + break; + case 6: + f->info.channel_type = 7; + break; + } + break; + case FOURCC('C', 'M', 'P', 'R'): + { + uint32_t cmpr = parse_be32(f->p); + if (cmpr != FOURCC('D', 'S', 'D', ' ')) + return -ENOTSUP; + break; + } + case FOURCC('A', 'B', 'S', 'S'): + break; + case FOURCC('L', 'S', 'C', 'O'): + break; + default: + break; + } + skip_chunk(f, &c0); + } + return 0; +} + +static int read_FRM8(struct dff_file *f) +{ + struct dff_chunk c2; + int res; + bool found_dsd = false; + + if ((res = read_chunk(f, &c0)) < 0) + return res; + if (c0.id != FOURCC('F','R','M','8')) + return -EINVAL; + if (f_avail(f) < 4 || + memcmp(c0.data, "DSD ", 4) != 0) + return -EINVAL; + f->p += 4; + + while (true) { + if ((res = read_chunk(f, &c1)) < 0) + return res; + + switch (c1.id) { + case FOURCC('F', 'V', 'E', 'R'): + break; + case FOURCC('P', 'R', 'O', 'P'): + read_PROP(f, &c1); + break; + case FOURCC('D', 'S', 'D', ' '): + { + f->info.length = c1.size; + f->info.samples = c1.size / f->info.channels; + f->info.lsb = 0; + f->info.blocksize = 1; + found_dsd = true; + break; + } + default: + break; + } + if (found_dsd) + break; + + skip_chunk(f, &c1); + } + return 0; +} + +static int open_read(struct dff_file *f, const char *filename, struct dff_file_info *info) +{ + int res; + struct stat st; + + if ((f->fd = open(filename, O_RDONLY)) < 0) { + res = -errno; + goto exit; + } + if (fstat(f->fd, &st) < 0) { + res = -errno; + goto exit_close; + } + f->size = st.st_size; + + f->data = mmap(NULL, f->size, PROT_READ, MAP_SHARED, f->fd, 0); + if (f->data == MAP_FAILED) { + res = -errno; + goto exit_close; + } + + f->p = f->data; + + if ((res = read_FRM8(f)) < 0) + goto exit_unmap; + + f->mode = 1; + *info = f->info; + return 0; + +exit_unmap: + munmap(f->data, f->size); +exit_close: + close(f->fd); +exit: + return res; +} + +struct dff_file * +dff_file_open(const char *filename, const char *mode, struct dff_file_info *info) +{ + int res; + struct dff_file *f; + + f = calloc(1, sizeof(struct dff_file)); + if (f == NULL) + return NULL; + + if (spa_streq(mode, "r")) { + if ((res = open_read(f, filename, info)) < 0) + goto exit_free; + } else { + res = -EINVAL; + goto exit_free; + } + return f; + +exit_free: + free(f); + errno = -res; + return NULL; +} + +static const uint8_t bitrev256 = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +}; + +ssize_t +dff_file_read(struct dff_file *f, void *data, size_t samples, const struct dff_layout *layout) +{ + uint8_t *d = data; + int32_t step = SPA_ABS(layout->interleave); + uint32_t channels = f->info.channels; + bool rev = layout->lsb != f->info.lsb; + size_t total, offset, scale; + + offset = f->offset; + scale = SPA_CLAMP(f->info.rate / (44100u * 64u), 1u, 4u); + + samples *= step; + samples *= scale; + + for (total = 0; total < samples && offset < f->info.length; total++) { + uint32_t i; + int32_t j; + const uint8_t *s = f->p + offset; + + for (i = 0; i < layout->channels; i++) { + if (layout->interleave > 0) { + for (j = 0; j < step; j++) + *d++ = rev ? + bitrevsj * channels + i : + sj * channels + i; + } else { + for (j = step-1; j >= 0; j--) + *d++ = rev ? + bitrevsj * channels + i : + sj * channels + i; + } + } + offset += step * channels; + } + f->offset = offset; + + return total; +} + +int dff_file_close(struct dff_file *f) +{ + if (f->mode == 1) { + munmap(f->data, f->size); + } else + return -EINVAL; + + close(f->fd); + free(f); + return 0; +}
View file
pipewire-0.3.81.tar.gz/src/tools/dfffile.h
Added
@@ -0,0 +1,31 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#include <stdio.h> + +#include <spa/utils/defs.h> + +struct dff_file; + +struct dff_file_info { + uint32_t channel_type; + uint32_t channels; + uint32_t rate; + bool lsb; + uint64_t samples; + uint64_t length; + uint32_t blocksize; +}; + +struct dff_layout { + int32_t interleave; + uint32_t channels; + bool lsb; +}; + +struct dff_file * dff_file_open(const char *filename, const char *mode, struct dff_file_info *info); + +ssize_t dff_file_read(struct dff_file *f, void *data, size_t samples, const struct dff_layout *layout); + +int dff_file_close(struct dff_file *f);
View file
pipewire-0.3.80.tar.gz/src/tools/meson.build -> pipewire-0.3.81.tar.gz/src/tools/meson.build
Changed
@@ -47,6 +47,7 @@ pwcat_sources = 'pw-cat.c', 'midifile.c', + 'dfffile.c', 'dsffile.c',
View file
pipewire-0.3.80.tar.gz/src/tools/pw-cat.c -> pipewire-0.3.81.tar.gz/src/tools/pw-cat.c
Changed
@@ -41,6 +41,7 @@ #endif #include "midifile.h" +#include "dfffile.h" #include "dsffile.h" #define DEFAULT_MEDIA_TYPE "Audio" @@ -143,6 +144,11 @@ struct dsf_file_info info; struct dsf_layout layout; } dsf; + struct { + struct dff_file *file; + struct dff_file_info info; + struct dff_layout layout; + } dff; #ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION struct { @@ -798,6 +804,10 @@ data->dsf.layout.channels = info.info.dsd.channels; data->dsf.layout.lsb = info.info.dsd.bitorder == SPA_PARAM_BITORDER_lsb; + data->dff.layout.interleave = info.info.dsd.interleave, + data->dff.layout.channels = info.info.dsd.channels; + data->dff.layout.lsb = info.info.dsd.bitorder == SPA_PARAM_BITORDER_lsb; + data->stride = data->dsf.layout.channels * SPA_ABS(data->dsf.layout.interleave); if (data->verbose) { @@ -1164,26 +1174,46 @@ return dsf_file_read(d->dsf.file, src, n_frames, &d->dsf.layout); } -static int setup_dsffile(struct data *data) +static int dff_play(struct data *d, void *src, unsigned int n_frames, bool *null_frame) +{ + return dff_file_read(d->dff.file, src, n_frames, &d->dff.layout); +} + +static int setup_dsdfile(struct data *data) { if (data->mode == mode_record) return -ENOTSUP; data->dsf.file = dsf_file_open(data->filename, "r", &data->dsf.info); if (data->dsf.file == NULL) { - fprintf(stderr, "dsffile: can't read dsf file '%s': %m\n", data->filename); - return -errno; + data->dff.file = dff_file_open(data->filename, "r", &data->dff.info); + if (data->dff.file == NULL) { + fprintf(stderr, "dsdfile: can't read dsd file '%s': %m\n", data->filename); + return -errno; + } } - if (data->verbose) - printf("dsffile: opened file \"%s\" channels:%d rate:%d samples:%"PRIu64" bitorder:%s\n", + if (data->dsf.file != NULL) { + if (data->verbose) + printf("dsffile: opened file \"%s\" channels:%d rate:%d " + "samples:%"PRIu64" bitorder:%s\n", data->filename, data->dsf.info.channels, data->dsf.info.rate, data->dsf.info.samples, data->dsf.info.lsb ? "lsb" : "msb"); - data->fill = dsf_play; + data->fill = dsf_play; + } else { + if (data->verbose) + printf("dfffile: opened file \"%s\" channels:%d rate:%d " + "samples:%"PRIu64" bitorder:%s\n", + data->filename, + data->dff.info.channels, data->dff.info.rate, + data->dff.info.samples, + data->dff.info.lsb ? "lsb" : "msb"); + data->fill = dff_play; + } return 0; } @@ -1839,7 +1869,7 @@ ret = setup_midifile(&data); break; case TYPE_DSD: - ret = setup_dsffile(&data); + ret = setup_dsdfile(&data); break; #ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION case TYPE_ENCODED: @@ -1918,13 +1948,21 @@ case TYPE_DSD: { struct spa_audio_info_dsd info; + uint32_t channel_type; spa_zero(info); - info.channels = data.dsf.info.channels; - info.rate = data.dsf.info.rate / 8; + if (data.dsf.file != NULL) { + info.channels = data.dsf.info.channels; + info.rate = data.dsf.info.rate / 8; + channel_type = data.dsf.info.channel_type; + } else { + info.channels = data.dff.info.channels; + info.rate = data.dff.info.rate / 8; + channel_type = data.dff.info.channel_type; + } SPA_FOR_EACH_ELEMENT_VAR(dsd_layouts, i) { - if (i->type != data.dsf.info.channel_type) + if (i->type != channel_type) continue; info.channels = i->info.n_channels; memcpy(info.position, i->info.position, @@ -2020,6 +2058,10 @@ sf_close(data.file); if (data.midi.file) midi_file_close(data.midi.file); + if (data.dsf.file) + dsf_file_close(data.dsf.file); + if (data.dff.file) + dff_file_close(data.dff.file); #ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION if (data.encoded.packet) av_packet_free(&data.encoded.packet);
View file
pipewire-0.3.80.tar.gz/src/tools/pw-cli.c -> pipewire-0.3.81.tar.gz/src/tools/pw-cli.c
Changed
@@ -348,6 +348,10 @@ bool ret; global = calloc(1, sizeof(struct global)); + if (global == NULL) { + fprintf(stderr, "Allocation failed: %m"); + return; + } global->rd = rd; global->id = id; global->permissions = permissions; @@ -615,6 +619,8 @@ struct pw_core_info *info = pd->info; info_global(pd); + if (info == NULL) + return; printf("\tcookie: %u\n", info->cookie); printf("\tuser-name: \"%s\"\n", info->user_name); printf("\thost-name: \"%s\"\n", info->host_name); @@ -629,6 +635,8 @@ struct pw_module_info *info = pd->info; info_global(pd); + if (info == NULL) + return; printf("\tname: \"%s\"\n", info->name); printf("\tfilename: \"%s\"\n", info->filename); printf("\targs: \"%s\"\n", info->args); @@ -641,6 +649,8 @@ struct pw_node_info *info = pd->info; info_global(pd); + if (info == NULL) + return; printf("%c\tinput ports: %u/%u\n", MARK_CHANGE(PW_NODE_CHANGE_MASK_INPUT_PORTS), info->n_input_ports, info->max_input_ports); printf("%c\toutput ports: %u/%u\n", MARK_CHANGE(PW_NODE_CHANGE_MASK_OUTPUT_PORTS), @@ -661,6 +671,8 @@ struct pw_port_info *info = pd->info; info_global(pd); + if (info == NULL) + return; printf("\tdirection: \"%s\"\n", pw_direction_as_string(info->direction)); print_properties(info->props, MARK_CHANGE(PW_PORT_CHANGE_MASK_PROPS), true); print_params(info->params, info->n_params, MARK_CHANGE(PW_PORT_CHANGE_MASK_PARAMS), true); @@ -672,6 +684,8 @@ struct pw_factory_info *info = pd->info; info_global(pd); + if (info == NULL) + return; printf("\tname: \"%s\"\n", info->name); printf("\tobject-type: %s/%d\n", info->type, info->version); print_properties(info->props, MARK_CHANGE(PW_FACTORY_CHANGE_MASK_PROPS), true); @@ -683,6 +697,8 @@ struct pw_client_info *info = pd->info; info_global(pd); + if (info == NULL) + return; print_properties(info->props, MARK_CHANGE(PW_CLIENT_CHANGE_MASK_PROPS), true); info->change_mask = 0; } @@ -692,6 +708,8 @@ struct pw_link_info *info = pd->info; info_global(pd); + if (info == NULL) + return; printf("\toutput-node-id: %u\n", info->output_node_id); printf("\toutput-port-id: %u\n", info->output_port_id); printf("\tinput-node-id: %u\n", info->input_node_id); @@ -717,6 +735,8 @@ struct pw_device_info *info = pd->info; info_global(pd); + if (info == NULL) + return; print_properties(info->props, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PROPS), true); print_params(info->params, info->n_params, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PARAMS), true); info->change_mask = 0; @@ -727,6 +747,8 @@ struct pw_session_info *info = pd->info; info_global(pd); + if (info == NULL) + return; print_properties(info->props, MARK_CHANGE(0), true); print_params(info->params, info->n_params, MARK_CHANGE(1), true); info->change_mask = 0; @@ -738,6 +760,8 @@ const char *direction; info_global(pd); + if (info == NULL) + return; printf("\tname: %s\n", info->name); printf("\tmedia-class: %s\n", info->media_class); switch(info->direction) { @@ -765,6 +789,8 @@ struct pw_endpoint_stream_info *info = pd->info; info_global(pd); + if (info == NULL) + return; printf("\tid: %u\n", info->id); printf("\tendpoint-id: %u\n", info->endpoint_id); printf("\tname: %s\n", info->name); @@ -987,25 +1013,25 @@ struct remote_data *rd = pd->rd; struct pw_session_info *info = pd->info; - if (!info) { + if (info == NULL) info = pd->info = calloc(1, sizeof(*info)); + if (info != NULL) { info->id = update->id; + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { + info->n_params = update->n_params; + free(info->params); + info->params = malloc(info->n_params * sizeof(struct spa_param_info)); + memcpy(info->params, update->params, + info->n_params * sizeof(struct spa_param_info)); + } + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) { + pw_properties_free ((struct pw_properties *)info->props); + info->props = + (struct spa_dict *) pw_properties_new_dict (update->props); + } } - if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { - info->n_params = update->n_params; - free(info->params); - info->params = malloc(info->n_params * sizeof(struct spa_param_info)); - memcpy(info->params, update->params, - info->n_params * sizeof(struct spa_param_info)); - } - if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) { - pw_properties_free ((struct pw_properties *)info->props); - info->props = - (struct spa_dict *) pw_properties_new_dict (update->props); - } - if (pd->global == NULL) - pd->global = pw_map_lookup(&rd->globals, info->id); + pd->global = pw_map_lookup(&rd->globals, update->id); if (pd->global && pd->global->info_pending) { info_session(pd); pd->global->info_pending = false; @@ -1034,33 +1060,34 @@ struct remote_data *rd = pd->rd; struct pw_endpoint_info *info = pd->info; - if (!info) { + if (info == NULL) info = pd->info = calloc(1, sizeof(*info)); + if (info != NULL) { info->id = update->id; info->name = update->name ? strdup(update->name) : NULL; info->media_class = update->media_class ? strdup(update->media_class) : NULL; info->direction = update->direction; info->flags = update->flags; - } - if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) - info->n_streams = update->n_streams; - if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) - info->session_id = update->session_id; - if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { - info->n_params = update->n_params; - free(info->params); - info->params = malloc(info->n_params * sizeof(struct spa_param_info)); - memcpy(info->params, update->params, - info->n_params * sizeof(struct spa_param_info)); - } - if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) { - pw_properties_free ((struct pw_properties *)info->props); - info->props = - (struct spa_dict *) pw_properties_new_dict (update->props); - } + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) + info->n_streams = update->n_streams; + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) + info->session_id = update->session_id; + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { + info->n_params = update->n_params; + free(info->params); + info->params = malloc(info->n_params * sizeof(struct spa_param_info)); + memcpy(info->params, update->params, + info->n_params * sizeof(struct spa_param_info)); + } + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) { + pw_properties_free ((struct pw_properties *)info->props); + info->props = + (struct spa_dict *) pw_properties_new_dict (update->props); + } + } if (pd->global == NULL) - pd->global = pw_map_lookup(&rd->globals, info->id); + pd->global = pw_map_lookup(&rd->globals, update->id); if (pd->global && pd->global->info_pending) { info_endpoint(pd); pd->global->info_pending = false; @@ -1088,27 +1115,28 @@ struct remote_data *rd = pd->rd; struct pw_endpoint_stream_info *info = pd->info; - if (!info) { + if (info == NULL) info = pd->info = calloc(1, sizeof(*info)); + if (info != NULL) { info->id = update->id; info->endpoint_id = update->endpoint_id; info->name = update->name ? strdup(update->name) : NULL; - } - if (update->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS) { - info->n_params = update->n_params; - free(info->params); - info->params = malloc(info->n_params * sizeof(struct spa_param_info)); - memcpy(info->params, update->params, - info->n_params * sizeof(struct spa_param_info)); - } - if (update->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS) { - pw_properties_free ((struct pw_properties *)info->props); - info->props = - (struct spa_dict *) pw_properties_new_dict (update->props); - } + if (update->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS) { + info->n_params = update->n_params; + free(info->params); + info->params = malloc(info->n_params * sizeof(struct spa_param_info)); + memcpy(info->params, update->params, + info->n_params * sizeof(struct spa_param_info)); + } + if (update->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS) { + pw_properties_free ((struct pw_properties *)info->props); + info->props = + (struct spa_dict *) pw_properties_new_dict (update->props); + } + } if (pd->global == NULL) - pd->global = pw_map_lookup(&rd->globals, info->id); + pd->global = pw_map_lookup(&rd->globals, update->id); if (pd->global && pd->global->info_pending) { info_endpoint_stream(pd); pd->global->info_pending = false;
View file
pipewire-0.3.80.tar.gz/src/tools/pw-link.c -> pipewire-0.3.81.tar.gz/src/tools/pw-link.c
Changed
@@ -197,7 +197,9 @@ static void print_port(struct data *data, const char *prefix, struct object *n, struct object *p, bool verbose) { - char buffer1024, id64 = "", *prefix2 = ""; + char buffer1024, id64 = ""; + const char *prefix2 = ""; + if (data->opt_id) { snprintf(id, sizeof(id), "%4d ", p->id); prefix2 = " ";
View file
pipewire-0.3.80.tar.gz/test/pwtest.c -> pipewire-0.3.81.tar.gz/test/pwtest.c
Changed
@@ -1207,7 +1207,7 @@ time_t t = time(NULL); struct tm *tm = localtime(&t); char *dir; - char *tmpdir = getenv("TMPDIR"); + const char *tmpdir = getenv("TMPDIR"); char pathPATH_MAX; FILE *fp;
View file
pipewire-0.3.80.tar.gz/test/test-logger.c -> pipewire-0.3.81.tar.gz/test/test-logger.c
Changed
@@ -232,7 +232,7 @@ enum spa_log_level level = pwtest_get_iteration(current_test); enum spa_log_level default_level = pw_log_level; struct spa_log *default_logger = pw_log_get(); - char *lvl = NULL; + const char *lvl = NULL; char *oldenv = getenv("PIPEWIRE_DEBUG"); if (oldenv) @@ -276,7 +276,7 @@ struct spa_log *default_logger = pw_log_get(); char *oldenv = getenv("PIPEWIRE_DEBUG"); char lvlstr32; - char *lvl = "X"; + const char *lvl = "X"; if (oldenv) oldenv = strdup(oldenv);
View file
pipewire-0.3.80.tar.gz/test/test-spa-json.c -> pipewire-0.3.81.tar.gz/test/test-spa-json.c
Changed
@@ -166,7 +166,7 @@ return PWTEST_PASS; } -static void test_array(char *str, char **vals) +static void test_array(const char *str, const char * const vals) { struct spa_json it2; char val256; @@ -183,12 +183,12 @@ PWTEST(json_array) { - test_array("FL,FR", (char *){ "FL", "FR", NULL }); - test_array(" FL , FR ", (char *){ "FL", "FR", NULL }); - test_array(" FL , FR ", (char *){ "FL", "FR", NULL }); - test_array("FL FR", (char *){ "FL", "FR", NULL }); - test_array("FL FR", (char *){ "FL", "FR", NULL }); - test_array(" FL FR ", (char *){ "FL", "FR", NULL }); + test_array("FL,FR", (const char *){ "FL", "FR", NULL }); + test_array(" FL , FR ", (const char *){ "FL", "FR", NULL }); + test_array(" FL , FR ", (const char *){ "FL", "FR", NULL }); + test_array("FL FR", (const char *){ "FL", "FR", NULL }); + test_array("FL FR", (const char *){ "FL", "FR", NULL }); + test_array(" FL FR ", (const char *){ "FL", "FR", NULL }); return PWTEST_PASS; }
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
.