Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 27
View file
pipewire-aptx.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Sun May 14 10:53:41 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> + +- Update to version 0.3.70 + +------------------------------------------------------------------- Fri Apr 14 13:35:34 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> - Update to version 0.3.69
View file
pipewire-aptx.spec
Changed
@@ -7,7 +7,7 @@ %define soversion 0_2 Name: pipewire-aptx -Version: 0.3.69 +Version: 0.3.70 Release: 0 Summary: PipeWire Bluetooth aptX codec plugin License: MIT
View file
pipewire-0.3.69.tar.gz/.gitlab-ci.yml -> pipewire-0.3.70.tar.gz/.gitlab-ci.yml
Changed
@@ -25,8 +25,8 @@ .fedora: variables: # Update this tag when you want to trigger a rebuild - FDO_DISTRIBUTION_TAG: '2023-03-09.0' - FDO_DISTRIBUTION_VERSION: '35' + FDO_DISTRIBUTION_TAG: '2023-04-18.0' + FDO_DISTRIBUTION_VERSION: '37' FDO_DISTRIBUTION_PACKAGES: >- alsa-lib-devel avahi-devel @@ -50,15 +50,17 @@ libmysofa-devel libsndfile-devel libubsan - libusb-devel + libusb1-devel lilv-devel libv4l-devel libva-devel libX11-devel ModemManager-devel + meson openssl-devel pulseaudio-libs-devel python3-docutils + python3-pip sbc-devel ShellCheck SDL2-devel @@ -69,18 +71,25 @@ valgrind ninja-build pkgconf - python3-pip pulseaudio-utils openal-soft readline-devel - FDO_DISTRIBUTION_EXEC: >- - pip3 install meson +# Uncommenting the following two lines and disabling the meson entry above +# will re-enable use of Meson via pip but please consider using a newer distro +# image first or making the build system compatible instead! This is because +# using pip or another 3rd party repo defeats the point testing the particular +# distro for regressions. +# NOTE: If you do end up using pip3 for meson, be sure to also update the +# build_meson_prerelease and build_meson_exact_release build instructions +# to uninstall the pip3 version again and probably to not call dnf remove +# FDO_DISTRIBUTION_EXEC: >- +# pip3 install meson .ubuntu: variables: # Update this tag when you want to trigger a rebuild - FDO_DISTRIBUTION_TAG: '2023-02-16.0' - FDO_DISTRIBUTION_VERSION: '20.04' + FDO_DISTRIBUTION_TAG: '2023-04-18.0' + FDO_DISTRIBUTION_VERSION: '22.04' FDO_DISTRIBUTION_PACKAGES: >- debhelper-compat findutils @@ -100,19 +109,25 @@ libva-dev libv4l-dev libx11-dev + meson ninja-build pkg-config python3-docutils systemd - python3-pip - FDO_DISTRIBUTION_EXEC: >- - pip3 install meson +# Uncommenting the following three lines and disabling the meson entry above +# will re-enable use of Meson via pip but please consider using a newer distro +# image first or making the build system compatible instead! This is because +# using pip or another 3rd party repo defeats the point testing the particular +# distro for regressions. +# python3-pip +# FDO_DISTRIBUTION_EXEC: >- +# pip3 install meson .alpine: variables: # Update this tag when you want to trigger a rebuild - FDO_DISTRIBUTION_TAG: '2022-09-07.0' - FDO_DISTRIBUTION_VERSION: '3.15' + FDO_DISTRIBUTION_TAG: '2023-04-17.0' + FDO_DISTRIBUTION_VERSION: '3.17' FDO_DISTRIBUTION_PACKAGES: >- alsa-lib-dev avahi-dev @@ -358,6 +373,7 @@ extends: - .build_on_fedora script: + - dnf remove --assumeyes meson - pip3 install --upgrade --pre meson - echo "Building with meson options $MESON_OPTIONS" - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS @@ -374,7 +390,8 @@ - meson_version=$(head -n 5 meson.build | grep 'meson_version' | sed -e 's/.*\(0-9\+\.0-9\+\.0-9\+\).*/\1/') - echo "Requiring meson version $meson_version" - test -n "$meson_version" || (echo "Meson version parser failed" && exit 1) - - pip3 uninstall --yes meson + - dnf remove --assumeyes meson +# - pip3 uninstall --yes meson - pip3 install "meson==$meson_version" - echo "Building with meson options $MESON_OPTIONS" - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
View file
pipewire-0.3.69.tar.gz/NEWS -> pipewire-0.3.70.tar.gz/NEWS
Changed
@@ -1,3 +1,70 @@ +# PipeWire 0.3.70 (2023-04-20) + +This is a quick bugfix release that is API and ABI compatible with previous +0.3.x releases. + +## Highlights + - Fix a regression in the scheduler that could keep some nodes IDLE. + - Fix a regression in the biquad filters in filter-chain. + - Fix a regression and potential crash in the ALSA mixer probing. + - Fix a regression in pipewiresrc with timestamps that could cause cheese + to record video with wrong timestamps. + - Beamforming support was enabled in the echo-canceler. + - pulse-tunnel and raop-sink will now proxy local volume changes to the + remote end. + - More bugfixes and improvements. + + +## PipeWire + - Fix a bug in the graph scheduler where some nodes might stay IDLE in + some cases (like when connecting the source of the echo-canceler to the + sink). + - pw-metadata can now be created from the factory with initial values for + the metadata. (#3076) + - Conditions were added to the pipewire config file to make it possible to + configure the access module and the exec sections. + - Support was added in pw-stream to intercept and override properties for + the adapter. This can be used to implement custom volume control, for + example. + +## Tools + - pw-metadata can now list all available metadata objects with the -l + option. + - A new pw-config tool was added to debug configuration file loading and + parsing. + +## Modules + - The webrtc echo canceler now supports beamforming. You can provide the + coordinates of the microphones and let webrtc perform beamforming on + the captured samples to improve quality and remove noise. + - Fix a regression in the filter-chain with biquad filters. (#3161) and + improve error reporting. + - The pulse-tunnel will now proxy the volume changes to the remote end. + - The RAOP sink will now send volume parameters to control the volume + remotely. (#2061) + +## SPA + - One ALSA commit was not correctly reverted and might cause crashes. + - The ALSA sink and source now calculate the ALSA ringbuffer memory + location more correctly wich might improve compatibility with some + hardware. + - v4l2 now sets the values of the controls in the Props param. + +## Pulse-server + - The echo-canceler aec_args are now parsed like they would be under + pulseaudio. + +## Bluetooth + - More work on synchronizing BAP devices. + +## GStreamer + - The GStreamer source can now renegotiate the format when it changes. + - The GStreamer source now uses the BaseSrc clocking code to implement + the clock and timing code. + + +Older versions: + # PipeWire 0.3.69 (2023-04-13) This is a quick bugfix release that is API and ABI compatible with previous @@ -51,9 +118,6 @@ ## GStreamer - Fix the GStreamer clock to make cheese video recording work again. (#3149) - -Older versions: - # PipeWire 0.3.68 (2023-04-06) This is a bugfix release that is API and ABI compatible with previous
View file
pipewire-0.3.69.tar.gz/doc/pipewire-daemon.dox -> pipewire-0.3.70.tar.gz/doc/pipewire-daemon.dox
Changed
@@ -64,8 +64,8 @@ The first configuration file found is loaded as the base configuration. -Next, configuration sections are collected in the directories in this -order: +Next, configuration sections (from files ending with a .conf extension) are collected +in the directories in this order: - `$datadir/pipewire/pipewire.conf.d/` (usually `/usr/share/pipewire/pipewire.conf.d/`) - `$sysconfdir/pipewire/pipewire.conf.d/` (usually `/etc/pipewire/pipewire.conf.d/`)
View file
pipewire-0.3.69.tar.gz/man/meson.build -> pipewire-0.3.70.tar.gz/man/meson.build
Changed
@@ -12,6 +12,7 @@ 'pipewire.conf.5.rst.in', 'pw-cat.1.rst.in', 'pw-cli.1.rst.in', + 'pw-config.1.rst.in', 'pw-dot.1.rst.in', 'pw-link.1.rst.in', 'pw-metadata.1.rst.in',
View file
pipewire-0.3.70.tar.gz/man/pw-config.1.rst.in
Added
@@ -0,0 +1,110 @@ +pw-config +######### + +----------------------------- +Debug PipeWire Config parsing +----------------------------- + +:Manual section: 1 +:Manual group: General Commands Manual + +SYNOPSIS +======== + +| **pw-config** *options* paths + +| **pw-config** *options* list *SECTION* + +| **pw-config** *options* merge *SECTION* + +DESCRIPTION +=========== + +List config paths and config sections and display the parsed +output. + +This tool can be used to get an overview of the config file that will be +parsed by the PipeWire server and clients. + +COMMON OPTIONS +============== + +-h | --help + Show help. + +--version + Show version information. + +-n | --name=NAME + Config Name (default 'pipewire.conf') + +-p | --prefix=PREFIX + Config Prefix (default '') + +-L | --no-newline + Omit newlines after values + +-r | --recurse + Reformat config sections recursively + +-N | --no-colors + Disable color output + +-C | --color=WHEN + whether to enable color support. WHEN is `never`, `always`, or `auto` + +LISTING PATHS +============= + +Specify the paths command. It will display all the config files that will +be parsed and in what order. + +LISTING CONFIG SECTIONS +======================= + +Specify the list command with an optional *SECTION* to list the configuration +fragments used for *SECTION*. Without a *SECTION*, all sections will be +listed. + +Use the -r options to reformat the sections. + +MERGING A CONFIG SECTION +======================== + +With the merge option and a *SECTION*, pw-config will merge all config files into +a merged config section and dump the results. This will be the section used by +the client or server. + +Use the -r options to reformat the sections. + +EXAMPLES +======== + +**pw-config** + List all config files that will be used + +**pw-config** -n pipewire-pulse.conf + List all config files that will be used by the PipeWire pulseaudio server. + +**pw-config** -n pipewire-pulse.conf list + List all config sections used by the PipeWire pulseaudio server + +**pw-config** -n jack.conf list context.properties + List the context.properties fragments used by the JACK clients + +**pw-config** -n jack.conf merge context.properties + List the merged context.properties used by the JACK clients + +**pw-config** -n pipewire.conf -r merge context.modules + List the merged context.modules used by the PipeWire server and reformat + +AUTHORS +======= + +The PipeWire Developers <@PACKAGE_BUGREPORT@>; PipeWire is available from @PACKAGE_URL@ + +SEE ALSO +======== + +``pipewire(1)``, +``pw-dump(1)``,
View file
pipewire-0.3.69.tar.gz/man/pw-metadata.1.rst.in -> pipewire-0.3.70.tar.gz/man/pw-metadata.1.rst.in
Changed
@@ -44,6 +44,9 @@ --version Show version information. +-l | --list + List available metadata objects + -m | --monitor Keeps running and log the changes to the metadata.
View file
pipewire-0.3.69.tar.gz/meson.build -> pipewire-0.3.70.tar.gz/meson.build
Changed
@@ -1,5 +1,5 @@ project('pipewire', 'c' , - version : '0.3.69', + version : '0.3.70', license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' , meson_version : '>= 0.61.1', default_options : 'warning_level=3',
View file
pipewire-0.3.69.tar.gz/spa/examples/adapter-control.c -> pipewire-0.3.70.tar.gz/spa/examples/adapter-control.c
Changed
@@ -964,8 +964,7 @@ int main(int argc, char *argv) { struct data data = { 0 }; - int res = 0; - char c; + int res = 0, c; /* default values*/ data.volume_ramp_samples = DEFAULT_RAMP_SAMPLES;
View file
pipewire-0.3.69.tar.gz/spa/plugins/aec/aec-webrtc.cpp -> pipewire-0.3.70.tar.gz/spa/plugins/aec/aec-webrtc.cpp
Changed
@@ -10,6 +10,7 @@ #include <spa/support/log.h> #include <spa/utils/string.h> #include <spa/utils/names.h> +#include <spa/utils/json.h> #include <spa/support/plugin.h> #include <webrtc/modules/audio_processing/include/audio_processing.h> @@ -40,6 +41,53 @@ return default_value; } + +/* 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; +} + 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) @@ -61,6 +109,8 @@ 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); + // FIXME: Intelligibility enhancer is not currently supported // This filter will modify playback buffer (when calling ProcessReverseStream), but now // playback buffer modifications are discarded. @@ -71,6 +121,44 @@ 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)); + } + } + webrtc::ProcessingConfig pconfig = {{ webrtc::StreamConfig(rec_info->rate, rec_info->channels, false), /* input stream */ webrtc::StreamConfig(out_info->rate, out_info->channels, false), /* output stream */
View file
pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-util.c -> pipewire-0.3.70.tar.gz/spa/plugins/alsa/acp/alsa-util.c
Changed
@@ -1648,20 +1648,12 @@ } else if (mask & SND_CTL_EVENT_MASK_ADD) { snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem); if (iface == SND_CTL_ELEM_IFACE_CARD || iface == SND_CTL_ELEM_IFACE_PCM) { - snd_mixer_t *mixer = snd_mixer_class_get_mixer(class); - snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem); - const char *name = snd_hctl_elem_get_name(helem); - const int index = snd_hctl_elem_get_index(helem); - const int device = snd_hctl_elem_get_device(helem); snd_mixer_elem_t *new_melem; - new_melem = pa_alsa_mixer_find(mixer, iface, name, index, device); - if (!new_melem) { - /* Put the hctl pointer as our private data - it will be useful for callbacks */ - if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, helem, NULL)) < 0) { - pa_log_warn("snd_mixer_elem_new failed: %s", pa_alsa_strerror(err)); - return 0; - } + /* Put the hctl pointer as our private data - it will be useful for callbacks */ + if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, helem, NULL)) < 0) { + pa_log_warn("snd_mixer_elem_new failed: %s", pa_alsa_strerror(err)); + return 0; } if ((err = snd_mixer_elem_attach(new_melem, helem)) < 0) {
View file
pipewire-0.3.69.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.70.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -2179,7 +2179,7 @@ if (SPA_LIKELY(state->use_mmap)) { for (i = 0; i < b->buf->n_datas; i++) { - spa_memcpy(SPA_PTROFF(my_areasi.addr, off * frame_size, void), + spa_memcpy(channel_area_addr(&my_areasi, off), SPA_PTROFF(di.data, offs, void), n_bytes); } } else { @@ -2290,11 +2290,11 @@ for (i = 0; i < b->buf->n_datas; i++) { spa_memcpy(di.data, - SPA_PTROFF(my_areasi.addr, offset * frame_size, void), + channel_area_addr(&my_areasi, offset), l0); if (SPA_UNLIKELY(l1 > 0)) spa_memcpy(SPA_PTROFF(di.data, l0, void), - my_areasi.addr, + channel_area_addr(&my_areasi, 0), l1); di.chunk->offset = 0; di.chunk->size = n_bytes;
View file
pipewire-0.3.69.tar.gz/spa/plugins/alsa/alsa-pcm.h -> pipewire-0.3.70.tar.gz/spa/plugins/alsa/alsa-pcm.h
Changed
@@ -13,6 +13,7 @@ #include <math.h> #include <alsa/asoundlib.h> +#include <alsa/version.h> #include <alsa/use-case.h> #include <spa/support/plugin.h> @@ -353,6 +354,12 @@ return missed; } +/* This function is also as snd_pcm_channel_area_addr() since 1.2.6 which is not yet + * in ubuntu and I can't figure out how to do the ALSA version check. */ +static inline void *channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset) +{ + return (char *)area->addr + (area->first + area->step * offset) / 8; +} #ifdef __cplusplus } /* extern "C" */
View file
pipewire-0.3.69.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.70.tar.gz/spa/plugins/audioconvert/audioadapter.c
Changed
@@ -51,6 +51,7 @@ uint64_t follower_flags; struct spa_audio_info follower_current_format; struct spa_audio_info default_format; + int in_set_param; struct spa_handle *hnd_convert; struct spa_node *convert; @@ -675,13 +676,16 @@ } case SPA_PARAM_Props: - if (this->target != this->follower) - res = spa_node_set_param(this->target, id, flags, param); - res2 = spa_node_set_param(this->follower, id, flags, param); + { + int in_set_param = ++this->in_set_param; + res = spa_node_set_param(this->follower, id, flags, param); + if (this->target != this->follower && this->in_set_param == in_set_param) + res2 = spa_node_set_param(this->target, id, flags, param); if (res < 0 && res2 < 0) return res; res = 0; break; + } case SPA_PARAM_ProcessLatency: res = spa_node_set_param(this->follower, id, flags, param); break;
View file
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/bluez5-dbus.c
Changed
@@ -2986,13 +2986,8 @@ else if (spa_streq(key, "State")) { enum spa_bt_transport_state state = spa_bt_transport_state_from_string(value); - /* Emit transition to active only for transports with - * acquired fd. If the acquire completes after prop - * update, we set the state in acquire completion. BlueZ - * currently sends events in the order where this never - * happens, but let's not rely on that. - */ - if (state != SPA_BT_TRANSPORT_STATE_ACTIVE || transport->fd >= 0) + /* Transition to active emitted only from acquire callback. */ + if (state != SPA_BT_TRANSPORT_STATE_ACTIVE) spa_bt_transport_set_state(transport, state); } else if (spa_streq(key, "Device")) { @@ -3287,7 +3282,6 @@ { struct spa_bt_monitor *monitor = transport->monitor; struct spa_bt_transport *t; - bool sink = (transport->profile & SPA_BT_PROFILE_BAP_SINK) != 0; if (!(transport->profile & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE))) return 0; @@ -3311,21 +3305,28 @@ if (t->iso_io) { spa_log_debug(monitor->log, "transport %p: attach ISO IO to %p", transport, t); - transport->iso_io = spa_bt_iso_io_attach(t->iso_io, transport->fd, sink); + transport->iso_io = spa_bt_iso_io_attach(t->iso_io, transport); + if (transport->iso_io == NULL) + return -errno; return 0; } } spa_log_debug(monitor->log, "transport %p: new ISO IO", transport); - transport->iso_io = spa_bt_iso_io_create(transport->fd, sink, - transport->bap_cig, transport->bap_interval, - monitor->log, monitor->data_loop, monitor->data_system); + transport->iso_io = spa_bt_iso_io_create(transport, monitor->log, monitor->data_loop, monitor->data_system); if (transport->iso_io == NULL) return -errno; return 0; } +static bool transport_in_same_cig(struct spa_bt_transport *transport, struct spa_bt_transport *other) +{ + return (other->profile & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE)) && + other->bap_cig == transport->bap_cig && + other->bap_initiator; +} + static void transport_acquire_reply(DBusPendingCall *pending, void *user_data) { struct spa_bt_transport *transport = user_data; @@ -3334,7 +3335,7 @@ int ret = 0; DBusError err; DBusMessage *r; - struct spa_bt_transport *t_linked; + struct spa_bt_transport *t, *t_linked; r = dbus_pending_call_steal_reply(pending); @@ -3389,7 +3390,8 @@ spa_log_error(monitor->log, "transport %p: transport_create_iso_io failed", transport); - spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ACTIVE); + if (!transport->bap_initiator) + spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ACTIVE); } /* For LE Audio, multiple transport from the same device may share the same @@ -3413,7 +3415,27 @@ spa_log_error(monitor->log, "transport %p: transport_create_iso_io failed", t_linked); - spa_bt_transport_set_state(t_linked, SPA_BT_TRANSPORT_STATE_ACTIVE); + if (!transport->bap_initiator) + spa_bt_transport_set_state(t_linked, SPA_BT_TRANSPORT_STATE_ACTIVE); + } + + /* + * Transports in same CIG emit state change events at the same time, + * after all pending acquires complete. + */ + if (transport->bap_initiator) { + spa_list_for_each(t, &monitor->transport_list, link) { + if (!transport_in_same_cig(transport, t)) + continue; + if (t->acquire_call) + return; + } + spa_list_for_each(t, &monitor->transport_list, link) { + if (!transport_in_same_cig(transport, t)) + continue; + if (t->fd >= 0) + spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_ACTIVE); + } } } @@ -3462,13 +3484,6 @@ return 0; } -static bool transport_in_same_cig(struct spa_bt_transport *transport, struct spa_bt_transport *other) -{ - return (other->profile & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE)) && - other->bap_cig == transport->bap_cig && - other->bap_initiator; -} - static bool another_cig_transport_active(struct spa_bt_transport *transport) { struct spa_bt_monitor *monitor = transport->monitor;
View file
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/dbus-monitor.c -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/dbus-monitor.c
Changed
@@ -75,7 +75,7 @@ monitor, g_dbus_object_get_object_path(object), name ? name : "<null>"); if (g_object_get_data(G_OBJECT(iface), "dbus-monitor-signals-connected")) { - g_object_disconnect(G_OBJECT(iface), "g-properties-changed", + g_object_disconnect(G_OBJECT(iface), "any_signal", G_CALLBACK(on_g_properties_changed), monitor, NULL); g_object_set_data(G_OBJECT(iface), "dbus-monitor-signals-connected", NULL);
View file
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/iso-io.c -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/iso-io.c
Changed
@@ -19,11 +19,15 @@ #include "config.h" #include "iso-io.h" +#include "media-codecs.h" +#include "defs.h" + static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.iso"); #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic -#define IDLE_TIME (200 * SPA_NSEC_PER_MSEC) +#define IDLE_TIME (500 * SPA_NSEC_PER_MSEC) +#define EMPTY_BUF_SIZE 65536 struct group { struct spa_log *log; @@ -37,6 +41,7 @@ uint64_t next; uint64_t duration; uint32_t paused; + bool started; }; struct stream { @@ -48,6 +53,9 @@ bool idle; spa_bt_iso_io_pull_t pull; + + const struct media_codec *codec; + uint32_t block_size; }; struct modify_info @@ -86,6 +94,33 @@ spa_assert_se(res == 0); } +static int stream_silence(struct stream *stream) +{ + static uint8_t emptyEMPTY_BUF_SIZE = {0}; + const size_t max_size = sizeof(stream->this.buf); + int res, used, need_flush; + size_t encoded; + + stream->idle = true; + + res = used = stream->codec->start_encode(stream->this.codec_data, stream->this.buf, max_size, 0, 0); + if (res < 0) + return res; + + res = stream->codec->encode(stream->this.codec_data, empty, stream->block_size, + SPA_PTROFF(stream->this.buf, used, void), max_size - used, &encoded, &need_flush); + if (res < 0) + return res; + + used += encoded; + + if (!need_flush) + return -EINVAL; + + stream->this.size = used; + return 0; +} + static int set_timeout(struct group *group, uint64_t time) { struct itimerspec ts; @@ -112,9 +147,10 @@ { struct group *group = source->data; struct stream *stream; + bool resync = false; + bool fail = false; uint64_t exp; int res; - bool active = false; if ((res = spa_system_timerfd_read(group->data_system, group->timerfd, &exp)) < 0) { if (res != -EAGAIN) @@ -124,26 +160,20 @@ } /* - * If an idle stream activates when another stream is already active, - * pause output of all streams for a while to avoid desynchronization. + * If a stream failed, pause output of all streams for a while to avoid + * desynchronization. */ spa_list_for_each(stream, &group->streams, link) { if (!stream->sink) continue; - if (!stream->idle) { - active = true; - break; - } - } - spa_list_for_each(stream, &group->streams, link) { - if (!stream->sink) - continue; - - if (stream->idle && stream->this.size > 0 && active && !group->paused) - group->paused = 1u + IDLE_TIME / group->duration; + if (stream->this.need_resync) { + resync = true; + stream->this.need_resync = false; + } - stream->idle = (stream->this.size == 0); + if (!group->started && !stream->idle && stream->this.size > 0) + group->started = true; } if (group->paused) { @@ -157,23 +187,36 @@ if (!stream->sink) continue; - if (stream->idle || group->paused) { + if (group->paused || !group->started) { stream->this.resync = true; stream->this.size = 0; continue; } + if (stream->this.size == 0) { + spa_log_debug(group->log, "%p: ISO group:%u miss fd:%d", + group, group->cig, stream->fd); + if (stream_silence(stream) < 0) { + fail = true; + continue; + } + } res = send(stream->fd, stream->this.buf, stream->this.size, MSG_DONTWAIT | MSG_NOSIGNAL); - if (res < 0) + if (res < 0) { res = -errno; + fail = true; + } - spa_log_trace(group->log, "%p: ISO group:%u sent fd:%d size:%u ts:%u res:%d", + spa_log_trace(group->log, "%p: ISO group:%u sent fd:%d size:%u ts:%u idle:%d res:%d", group, group->cig, stream->fd, (unsigned)stream->this.size, - (unsigned)stream->this.timestamp, res); + (unsigned)stream->this.timestamp, stream->idle, res); stream->this.size = 0; } + if (fail) + group->paused = 1u + IDLE_TIME / group->duration; + /* Pull data for the next interval */ group->next += exp * group->duration; @@ -181,23 +224,27 @@ if (!stream->sink) continue; + if (resync) + stream->this.resync = true; + if (stream->pull) { + stream->idle = false; stream->this.now = group->next; stream->pull(&stream->this); } else { - stream->this.size = 0; + stream_silence(stream); } } set_timeout(group, group->next); } -static struct group *group_create(uint8_t cig, uint32_t interval, +static struct group *group_create(struct spa_bt_transport *t, struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system) { struct group *group; - if (interval <= 5000) { + if (t->bap_interval <= 5000) { errno = EINVAL; return NULL; } @@ -208,11 +255,11 @@ spa_log_topic_init(log, &log_topic); - group->cig = cig; + group->cig = t->bap_cig; group->log = log; group->data_loop = data_loop; group->data_system = data_system; - group->duration = interval * SPA_NSEC_PER_USEC; + group->duration = t->bap_interval * SPA_NSEC_PER_USEC; spa_list_init(&group->streams); @@ -261,36 +308,80 @@ free(group); } -struct stream *stream_create(int fd, bool sink, struct group *group) +struct stream *stream_create(struct spa_bt_transport *t, struct group *group) { struct stream *stream; + void *codec_data = NULL; + int block_size = 0; + struct spa_audio_info format = { 0 }; + int res; + bool sink = (t->profile & SPA_BT_PROFILE_BAP_SINK) != 0; + + if (!t->media_codec->bap) { + res = -EINVAL; + goto fail; + } + + if (sink) { + res = t->media_codec->validate_config(t->media_codec, 0, t->configuration, t->configuration_len, &format); + if (res < 0) + goto fail; + + codec_data = t->media_codec->init(t->media_codec, 0, t->configuration, t->configuration_len, + &format, NULL, t->write_mtu); + if (!codec_data) { + res = -EINVAL; + goto fail; + } + + block_size = t->media_codec->get_block_size(codec_data); + if (block_size < 0 || block_size > EMPTY_BUF_SIZE) { + res = -EINVAL; + goto fail; + } + } stream = calloc(1, sizeof(struct stream)); if (stream == NULL) - return NULL; + goto fail_errno; - stream->fd = fd; + stream->fd = t->fd; stream->sink = sink; stream->group = group; - stream->idle = true; stream->this.duration = group->duration; + stream->codec = t->media_codec; + stream->this.codec_data = codec_data; + stream->this.format = format; + stream->block_size = block_size; + + if (sink) + stream_silence(stream); + stream_link(group, stream); return stream; + +fail_errno: + res = -errno; +fail: + if (codec_data) + t->media_codec->deinit(codec_data); + errno = -res; + return NULL; } -struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, uint8_t cig, uint32_t interval, +struct spa_bt_iso_io *spa_bt_iso_io_create(struct spa_bt_transport *t, struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system) { struct stream *stream; struct group *group; - group = group_create(cig, interval, log, data_loop, data_system); + group = group_create(t, log, data_loop, data_system); if (group == NULL) return NULL; - stream = stream_create(fd, sink, group); + stream = stream_create(t, group); if (stream == NULL) { int err = errno; group_destroy(group); @@ -301,11 +392,11 @@ return &stream->this; } -struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *this, int fd, bool sink) +struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *this, struct spa_bt_transport *t) { struct stream *stream = SPA_CONTAINER_OF(this, struct stream, this); - stream = stream_create(fd, sink, stream->group); + stream = stream_create(t, stream->group); if (stream == NULL) return NULL; @@ -321,6 +412,10 @@ if (spa_list_is_empty(&stream->group->streams)) group_destroy(stream->group); + if (stream->this.codec_data) + stream->codec->deinit(stream->this.codec_data); + stream->this.codec_data = NULL; + free(stream); } @@ -348,10 +443,12 @@ enabled = group_is_enabled(stream->group); - if (!enabled && was_enabled) + if (!enabled && was_enabled) { + stream->group->started = false; set_timeout(stream->group, 0); - else if (enabled && !was_enabled) + } else if (enabled && !was_enabled) { set_timers(stream->group); + } stream->idle = true; stream->this.resync = true;
View file
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/iso-io.h -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/iso-io.h
Changed
@@ -9,6 +9,9 @@ #include <spa/support/loop.h> #include <spa/support/log.h> #include <spa/node/io.h> +#include <spa/param/audio/format.h> + +struct spa_bt_transport; /** * ISO I/O. @@ -26,15 +29,19 @@ uint32_t timestamp; /**< Packet timestamp (set by pull callback) */ uint8_t buf4096; /**< Packet data (set by pull callback) */ size_t size; /**< Packet size (set by pull callback) */ + bool need_resync; /**< Resync requested (set by pull callback) */ + + struct spa_audio_info format; /**< Audio format */ + void *codec_data; /**< Codec data */ void *user_data; }; typedef void (*spa_bt_iso_io_pull_t)(struct spa_bt_iso_io *io); -struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, uint8_t cig, uint32_t interval, +struct spa_bt_iso_io *spa_bt_iso_io_create(struct spa_bt_transport *t, struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system); -struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *io, int fd, bool sink); +struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *io, struct spa_bt_transport *t); void spa_bt_iso_io_destroy(struct spa_bt_iso_io *io); void spa_bt_iso_io_set_cb(struct spa_bt_iso_io *io, spa_bt_iso_io_pull_t pull, void *user_data);
View file
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/media-sink.c -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/media-sink.c
Changed
@@ -134,6 +134,7 @@ unsigned int is_output:1; unsigned int flush_pending:1; unsigned int iso_pending:1; + unsigned int own_codec_data:1; unsigned int is_duplex:1; unsigned int is_internal:1; @@ -990,16 +991,15 @@ err = value - target; max_err = iso_io->duration; - if (err > max_err || (iso_io->resync && err > 0)) { + if (iso_io->resync && err >= 0) { unsigned int req = err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC; if (req > 0) { spa_bt_rate_control_init(&port->ratectl, 0); drop_frames(this, req); - spa_log_debug(this->log, "%p: ISO sync skip frames:%u resync:%d", - this, req, iso_io->resync); } - } else if (-err > max_err || (iso_io->resync && -err > 0)) { + spa_log_debug(this->log, "%p: ISO sync skip frames:%u", this, req); + } else if (iso_io->resync && -err >= 0) { unsigned int req = -err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC; static const uint8_t empty8192 = {0}; @@ -1007,9 +1007,12 @@ spa_bt_rate_control_init(&port->ratectl, 0); req = SPA_MIN(req, sizeof(empty) / port->frame_size); add_data(this, empty, req * port->frame_size); - spa_log_debug(this->log, "%p: ISO sync pad frames:%u resync:%d", - this, req, iso_io->resync); } + spa_log_debug(this->log, "%p: ISO sync pad frames:%u", this, req); + } else if (err > max_err || -err > max_err) { + iso_io->need_resync = true; + spa_log_debug(this->log, "%p: ISO sync need resync err:%+.3f", + this, err / SPA_NSEC_PER_MSEC); } else { spa_bt_rate_control_update(&port->ratectl, err, 0, iso_io->duration, period, RATE_CTL_DIFF_MAX); @@ -1172,15 +1175,22 @@ flags = this->is_duplex ? MEDIA_CODEC_FLAG_SINK : 0; - this->codec_data = this->codec->init(this->codec, - flags, - this->transport->configuration, - this->transport->configuration_len, - &port->current_format, - this->codec_props, - this->transport->write_mtu); - if (this->codec_data == NULL) - return -EIO; + if (!this->transport->iso_io) { + this->own_codec_data = true; + this->codec_data = this->codec->init(this->codec, + flags, + this->transport->configuration, + this->transport->configuration_len, + &port->current_format, + this->codec_props, + this->transport->write_mtu); + if (this->codec_data == NULL) + return -EIO; + } else { + this->own_codec_data = false; + this->codec_data = this->transport->iso_io->codec_data; + this->codec_props_changed = true; + } spa_log_info(this->log, "%p: using %s codec %s, delay:%"PRIi64" ms", this, this->codec->bap ? "BAP" : "A2DP", this->codec->description, @@ -1333,7 +1343,7 @@ spa_loop_invoke(this->data_loop, do_remove_transport_source, 0, NULL, 0, true, this); - if (this->codec_data) + if (this->codec_data && this->own_codec_data) this->codec->deinit(this->codec_data); this->codec_data = NULL; } @@ -1652,6 +1662,15 @@ info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) return -EINVAL; + if (this->transport && this->transport->iso_io) { + if (memcmp(&info.info.raw, &this->transport->iso_io->format.info.raw, + sizeof(info.info.raw))) { + spa_log_error(this->log, "unexpected incompatible " + "BAP audio format"); + return -EINVAL; + } + } + port->frame_size = info.info.raw.channels; switch (info.info.raw.format) { case SPA_AUDIO_FORMAT_S16: @@ -1930,7 +1949,7 @@ else transport_stop(this); - if (state < SPA_BT_TRANSPORT_STATE_ACTIVE && was_started) { + if (state < SPA_BT_TRANSPORT_STATE_ACTIVE && was_started && !this->is_duplex && this->is_output) { /* * If establishing connection fails due to remote end not activating * the transport, we won't get a write error, but instead see a transport
View file
pipewire-0.3.69.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.70.tar.gz/spa/plugins/bluez5/media-source.c
Changed
@@ -1468,7 +1468,7 @@ io->buffer_id = SPA_ID_INVALID; } - if (!this->source.loop) { + if (this->transport_started && !this->source.loop) { io->status = -EIO; return SPA_STATUS_STOPPED; }
View file
pipewire-0.3.69.tar.gz/spa/plugins/support/logger.c -> pipewire-0.3.70.tar.gz/spa/plugins/support/logger.c
Changed
@@ -293,7 +293,8 @@ { struct impl *this; struct spa_loop *loop = NULL; - const char *str; + const char *str, *dest = ""; + bool linebuf = false; spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL); @@ -337,25 +338,37 @@ if ((str = spa_dict_lookup(info, SPA_KEY_LOG_LEVEL)) != NULL) this->log.level = atoi(str); if ((str = spa_dict_lookup(info, SPA_KEY_LOG_FILE)) != NULL) { - this->file = fopen(str, "we"); - if (this->file == NULL) - fprintf(stderr, "Warning: failed to open file %s: (%m)", str); - else - this->close_file = true; + dest = str; + if (spa_streq(str, "stderr")) + this->file = stderr; + else if (spa_streq(str, "stdout")) + this->file = stdout; + else { + this->file = fopen(str, "we"); + if (this->file == NULL) + fprintf(stderr, "Warning: failed to open file %s: (%m)", str); + else + this->close_file = true; + } } if ((str = spa_dict_lookup(info, SPA_KEY_LOG_PATTERNS)) != NULL) support_log_parse_patterns(&this->patterns, str); } - if (this->file == NULL) + if (this->file == NULL) { this->file = stderr; + dest = "stderr"; + } else { + linebuf = true; + } + if (linebuf) + setlinebuf(this->file); + if (!isatty(fileno(this->file))) this->colors = false; spa_ringbuffer_init(&this->trace_rb); - spa_log_debug(&this->log, NAME " %p: initialized", this); - - setlinebuf(this->file); + spa_log_debug(&this->log, NAME " %p: initialized to %s linebuf:%u", this, dest, linebuf); return 0; }
View file
pipewire-0.3.69.tar.gz/spa/plugins/v4l2/v4l2-source.c -> pipewire-0.3.70.tar.gz/spa/plugins/v4l2/v4l2-source.c
Changed
@@ -63,7 +63,8 @@ struct control { uint32_t id; uint32_t ctrl_id; - double value; + uint32_t type; + int32_t value; }; struct port { @@ -148,6 +149,37 @@ #include "v4l2-utils.c" +static const struct spa_dict_item info_items = { + { SPA_KEY_DEVICE_API, "v4l2" }, + { SPA_KEY_MEDIA_CLASS, "Video/Source" }, + { SPA_KEY_MEDIA_ROLE, "Camera" }, + { SPA_KEY_NODE_DRIVER, "true" }, +}; + +static void emit_node_info(struct impl *this, bool full) +{ + uint64_t old = full ? this->info.change_mask : 0; + if (full) + this->info.change_mask = this->info_all; + if (this->info.change_mask) { + this->info.props = &SPA_DICT_INIT_ARRAY(info_items); + spa_node_emit_info(&this->hooks, &this->info); + this->info.change_mask = old; + } +} + +static void emit_port_info(struct impl *this, struct port *port, bool full) +{ + uint64_t old = full ? port->info.change_mask : 0; + if (full) + port->info.change_mask = port->info_all; + if (port->info.change_mask) { + spa_node_emit_port_info(&this->hooks, + SPA_DIRECTION_OUTPUT, 0, &port->info); + port->info.change_mask = old; + } +} + static int port_get_format(struct port *port, uint32_t index, const struct spa_pod *filter, @@ -255,14 +287,40 @@ case SPA_PARAM_Props: { struct props *p = &this->props; + struct spa_pod_frame f; + struct port *port = &this->out_ports0; + uint32_t i; + + if ((res = spa_v4l2_update_controls(this)) < 0) { + spa_log_error(this->log, "error: %s", spa_strerror(res)); + return res; + } switch (result.index) { case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Props, id, + spa_pod_builder_push_object(&b, &f, SPA_TYPE_OBJECT_Props, id); + spa_pod_builder_add(&b, SPA_PROP_device, SPA_POD_String(p->device), SPA_PROP_deviceName, SPA_POD_String(p->device_name), - SPA_PROP_deviceFd, SPA_POD_Int(p->device_fd)); + SPA_PROP_deviceFd, SPA_POD_Int(p->device_fd), + 0); + for (i = 0; i < port->n_controls; i++) { + struct control *c = &port->controlsi; + + spa_pod_builder_prop(&b, c->id, 0); + switch (c->type) { + case SPA_TYPE_Int: + spa_pod_builder_int(&b, c->value); + break; + case SPA_TYPE_Bool: + spa_pod_builder_bool(&b, c->value); + break; + default: + spa_pod_builder_int(&b, c->value); + break; + } + } + param = spa_pod_builder_pop(&b, &f); break; default: return 0; @@ -322,7 +380,9 @@ break; } } - + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; + this->paramsNODE_Props.flags ^= SPA_PARAM_INFO_SERIAL; + emit_node_info(this, true); break; } default: @@ -399,37 +459,6 @@ return 0; } -static const struct spa_dict_item info_items = { - { SPA_KEY_DEVICE_API, "v4l2" }, - { SPA_KEY_MEDIA_CLASS, "Video/Source" }, - { SPA_KEY_MEDIA_ROLE, "Camera" }, - { SPA_KEY_NODE_DRIVER, "true" }, -}; - -static void emit_node_info(struct impl *this, bool full) -{ - uint64_t old = full ? this->info.change_mask : 0; - if (full) - this->info.change_mask = this->info_all; - if (this->info.change_mask) { - this->info.props = &SPA_DICT_INIT_ARRAY(info_items); - spa_node_emit_info(&this->hooks, &this->info); - this->info.change_mask = old; - } -} - -static void emit_port_info(struct impl *this, struct port *port, bool full) -{ - uint64_t old = full ? port->info.change_mask : 0; - if (full) - port->info.change_mask = port->info_all; - if (port->info.change_mask) { - spa_node_emit_port_info(&this->hooks, - SPA_DIRECTION_OUTPUT, 0, &port->info); - port->info.change_mask = old; - } -} - static int impl_node_add_listener(void *object, struct spa_hook *listener,
View file
pipewire-0.3.69.tar.gz/spa/plugins/v4l2/v4l2-utils.c -> pipewire-0.3.70.tar.gz/spa/plugins/v4l2/v4l2-utils.c
Changed
@@ -1139,10 +1139,9 @@ spa_log_debug(this->log, "Control '%s' %d %d", queryctrl.name, prop_id, ctrl_id); - port->n_controls++; - switch (queryctrl.type) { case V4L2_CTRL_TYPE_INTEGER: + port->controlsport->n_controls.type = SPA_TYPE_Int; param = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_id, SPA_POD_Id(prop_id), @@ -1154,6 +1153,7 @@ SPA_PROP_INFO_description, SPA_POD_String(queryctrl.name)); break; case V4L2_CTRL_TYPE_BOOLEAN: + port->controlsport->n_controls.type = SPA_TYPE_Bool; param = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_id, SPA_POD_Id(prop_id), @@ -1165,6 +1165,7 @@ struct v4l2_querymenu querymenu; struct spa_pod_builder_state state; + port->controlsport->n_controls.type = SPA_TYPE_Int; spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo); spa_pod_builder_add(&b, SPA_PROP_INFO_id, SPA_POD_Id(prop_id), @@ -1205,6 +1206,9 @@ goto next; } + + port->n_controls++; + if (spa_pod_filter(&b, &result.param, param, filter) < 0) goto next; @@ -1220,6 +1224,35 @@ } static int +spa_v4l2_update_controls(struct impl *this) +{ + struct port *port = &this->out_ports0; + struct spa_v4l2_device *dev = &port->dev; + int res; + uint32_t i; + + if ((res = spa_v4l2_open(dev, this->props.device)) < 0) + return res; + + for (i = 0; i < port->n_controls; i++) { + struct control *c = &port->controlsi; + struct v4l2_control control; + + spa_zero(control); + control.id = c->ctrl_id; + if (xioctl(dev->fd, VIDIOC_G_CTRL, &control) < 0) { + res = -errno; + goto done; + } + c->value = control.value; + } + res = 0; +done: + spa_v4l2_close(dev); + return res; +} + +static int spa_v4l2_set_control(struct impl *this, uint32_t id, const struct spa_pod_prop *prop) {
View file
pipewire-0.3.69.tar.gz/src/daemon/minimal.conf.in -> pipewire-0.3.70.tar.gz/src/daemon/minimal.conf.in
Changed
@@ -152,7 +152,15 @@ #{ factory = spa-node-factory args = { factory.name = api.vulkan.compute.source node.name = my-compute-source } } # Make a default metadata store - { factory = metadata args = { metadata.name = default } } + { factory = metadata + args = { + metadata.name = default + # metadata.values = + # { key = default.audio.sink value = { name = somesink } } + # { key = default.audio.source value = { name = somesource } } + # + } + } # A default dummy driver. This handles nodes marked with the "node.always-driver" # property when no other driver is currently active. JACK clients need this.
View file
pipewire-0.3.69.tar.gz/src/daemon/pipewire.conf.in -> pipewire-0.3.70.tar.gz/src/daemon/pipewire.conf.in
Changed
@@ -47,6 +47,9 @@ # keys checked below to disable module loading module.x11.bell = true + # enables autoloading of access module, when disabled an alternative + # access module needs to be loaded. + module.access = true } context.spa-libs = { @@ -152,6 +155,7 @@ # access.force permission. #access.force = flatpak } + condition = { module.access = true } } # Makes a factory for wrapping nodes in an adapter with a @@ -263,6 +267,17 @@ # audio.position = "FL,FR" # } #} + + # Use the metadata factory to create metadata and some default values. + #{ factory = metadata + # args = { + # metadata.name = my-metadata + # metadata.values = + # { key = default.audio.sink value = { name = somesink } } + # { key = default.audio.source value = { name = somesource } } + # + # } + #} context.exec = @@ -279,12 +294,14 @@ # but it is better to start it as a systemd service. # Run the session manager with -h for options. # - @sm_comment@{ path = "@session_manager_path@" args = "@session_manager_args@" } + @sm_comment@{ path = "@session_manager_path@" args = "@session_manager_args@" + @sm_comment@ condition = { exec.session-manager = null } { exec.session-manager = true } } # # You can optionally start the pulseaudio-server here as well # but it is better to start it as a systemd service. # It can be interesting to start another daemon here that listens # on another address with the -a option (eg. -a tcp:4713). # - @pulse_comment@{ path = "@pipewire_path@" args = "-c pipewire-pulse.conf" } + @pulse_comment@{ path = "@pipewire_path@" args = "-c pipewire-pulse.conf" + @pulse_comment@ condition = { exec.pipewire-pulse = null } { exec.pipewire-pulse = true } }
View file
pipewire-0.3.69.tar.gz/src/gst/gstpipewiresrc.c -> pipewire-0.3.70.tar.gz/src/gst/gstpipewiresrc.c
Changed
@@ -91,6 +91,8 @@ static gboolean gst_pipewire_src_stop (GstBaseSrc * basesrc); static gboolean gst_pipewire_src_event (GstBaseSrc * src, GstEvent * event); static gboolean gst_pipewire_src_query (GstBaseSrc * src, GstQuery * query); +static void gst_pipewire_src_get_times (GstBaseSrc * basesrc, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end); static void gst_pipewire_src_set_property (GObject * object, guint prop_id, @@ -413,6 +415,7 @@ gstbasesrc_class->stop = gst_pipewire_src_stop; gstbasesrc_class->event = gst_pipewire_src_event; gstbasesrc_class->query = gst_pipewire_src_query; + gstbasesrc_class->get_times = gst_pipewire_src_get_times; gstpushsrc_class->create = gst_pipewire_src_create; GST_DEBUG_CATEGORY_INIT (pipewire_src_debug, "pipewiresrc", 0, @@ -578,7 +581,7 @@ GST_LOG_OBJECT (pwsrc, "pts %" G_GUINT64_FORMAT ", dts_offset %" G_GUINT64_FORMAT, h->pts, h->dts_offset); if (GST_CLOCK_TIME_IS_VALID (h->pts)) { - GST_BUFFER_PTS (buf) = h->pts + GST_PIPEWIRE_CLOCK (pwsrc->clock)->time_offset; + GST_BUFFER_PTS (buf) = h->pts; if (GST_BUFFER_PTS (buf) + h->dts_offset > 0) GST_BUFFER_DTS (buf) = GST_BUFFER_PTS (buf) + h->dts_offset; } @@ -1126,14 +1129,43 @@ return res; } +static void +gst_pipewire_src_get_times (GstBaseSrc * basesrc, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end) +{ + GstPipeWireSrc *pwsrc = GST_PIPEWIRE_SRC (basesrc); + + /* for live sources, sync on the timestamp of the buffer */ + if (gst_base_src_is_live (basesrc)) { + GstClockTime timestamp = GST_BUFFER_PTS (buffer); + + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + /* get duration to calculate end time */ + GstClockTime duration = GST_BUFFER_DURATION (buffer); + + if (GST_CLOCK_TIME_IS_VALID (duration)) { + *end = timestamp + duration; + } + *start = timestamp; + } + } else { + *start = GST_CLOCK_TIME_NONE; + *end = GST_CLOCK_TIME_NONE; + } + + GST_LOG_OBJECT (pwsrc, "start %" GST_TIME_FORMAT " (%" G_GUINT64_FORMAT + "), end %" GST_TIME_FORMAT " (%" G_GUINT64_FORMAT ")", + GST_TIME_ARGS (*start), *start, GST_TIME_ARGS (*end), *end); +} + static GstFlowReturn gst_pipewire_src_create (GstPushSrc * psrc, GstBuffer ** buffer) { GstPipeWireSrc *pwsrc; - GstClockTime pts, dts, base_time; const char *error = NULL; GstBuffer *buf; gboolean update_time = FALSE, timeout = FALSE; + GstCaps *caps = NULL; pwsrc = GST_PIPEWIRE_SRC (psrc); @@ -1157,6 +1189,18 @@ if (state != PW_STREAM_STATE_STREAMING) goto streaming_stopped; + if ((caps = pwsrc->caps) != NULL) { + pwsrc->caps = NULL; + pw_thread_loop_unlock (pwsrc->core->loop); + + GST_DEBUG_OBJECT (pwsrc, "set format %" GST_PTR_FORMAT, caps); + gst_base_src_set_caps (GST_BASE_SRC (pwsrc), caps); + gst_caps_unref (caps); + + pw_thread_loop_lock (pwsrc->core->loop); + continue; + } + if (pwsrc->eos) { if (pwsrc->last_buffer == NULL) goto streaming_eos; @@ -1196,37 +1240,24 @@ *buffer = buf; - if (pwsrc->is_live) - base_time = GST_ELEMENT_CAST (psrc)->base_time; - else - base_time = 0; - if (update_time) { - GstClock *clock = gst_element_get_clock (GST_ELEMENT_CAST (pwsrc)); + GstClock *clock; + GstClockTime pts, dts; + + clock = gst_element_get_clock (GST_ELEMENT_CAST (pwsrc)); if (clock != NULL) { pts = dts = gst_clock_get_time (clock); gst_object_unref (clock); } else { pts = dts = GST_CLOCK_TIME_NONE; } - } else { - pts = GST_BUFFER_PTS (*buffer); - dts = GST_BUFFER_DTS (*buffer); - } - - if (GST_CLOCK_TIME_IS_VALID (pts)) - pts = (pts >= base_time ? pts - base_time : 0); - if (GST_CLOCK_TIME_IS_VALID (dts)) - dts = (dts >= base_time ? dts - base_time : 0); - GST_LOG_OBJECT (pwsrc, - "pts %" G_GUINT64_FORMAT ", dts %" G_GUINT64_FORMAT - ", base-time %" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT ", %" GST_TIME_FORMAT, - GST_BUFFER_PTS (*buffer), GST_BUFFER_DTS (*buffer), GST_TIME_ARGS (base_time), - GST_TIME_ARGS (pts), GST_TIME_ARGS (dts)); + GST_BUFFER_PTS (*buffer) = pts; + GST_BUFFER_DTS (*buffer) = dts; - GST_BUFFER_PTS (*buffer) = pts; - GST_BUFFER_DTS (*buffer) = dts; + GST_LOG_OBJECT (pwsrc, "Sending keepalive buffer pts/dts: %" GST_TIME_FORMAT + " (%" G_GUINT64_FORMAT ")", GST_TIME_ARGS (pts), pts); + } return GST_FLOW_OK;
View file
pipewire-0.3.69.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.70.tar.gz/src/modules/module-filter-chain/builtin_plugin.c
Changed
@@ -287,13 +287,19 @@ impl->rate = SampleRate; impl->b0 = impl->a0 = 1.0f; impl->type = bq_type_from_name(Descriptor->name); + if (impl->type != BQ_NONE) + return impl; - if (config == NULL) + if (config == NULL) { + pw_log_error("biquads:bq_raw requires a config section"); goto error; + } spa_json_init(&it0, config, strlen(config)); - if (spa_json_enter_object(&it0, &it1) <= 0) + if (spa_json_enter_object(&it0, &it1) <= 0) { + pw_log_error("biquads:config section must be an object"); goto error; + } while (spa_json_get_string(&it1, key, sizeof(key)) > 0) { if (spa_streq(key, "coefficients")) { @@ -349,8 +355,11 @@ goto error; } } - else if (spa_json_next(&it1, &val) < 0) - break; + else { + pw_log_warn("biquads: ignoring coefficients key: '%s'", key); + if (spa_json_next(&it3, &val) < 0) + break; + } } if (labs((long)rate - (long)SampleRate) < labs((long)best_rate - (long)SampleRate)) { @@ -359,8 +368,11 @@ } } } - else if (spa_json_next(&it1, &val) < 0) - break; + else { + pw_log_warn("biquads: ignoring config key: '%s'", key); + if (spa_json_next(&it1, &val) < 0) + break; + } } return impl; @@ -842,12 +854,16 @@ unsigned long rate; errno = EINVAL; - if (config == NULL) + if (config == NULL) { + pw_log_error("convolver: requires a config section"); return NULL; + } spa_json_init(&it0, config, strlen(config)); - if (spa_json_enter_object(&it0, &it1) <= 0) + if (spa_json_enter_object(&it0, &it1) <= 0) { + pw_log_error("convolver:config must be an object"); return NULL; + } while (spa_json_get_string(&it1, key, sizeof(key)) > 0) { if (spa_streq(key, "blocksize")) { @@ -918,8 +934,11 @@ return NULL; } } - else if (spa_json_next(&it1, &val) < 0) - break; + else { + pw_log_warn("convolver: ignoring config key: '%s'", key); + if (spa_json_next(&it1, &val) < 0) + break; + } } if (filenames0 == NULL) { pw_log_error("convolver:filename was not given"); @@ -1061,13 +1080,16 @@ float max_delay = 1.0f; if (config == NULL) { + pw_log_error("delay: requires a config section"); errno = EINVAL; return NULL; } spa_json_init(&it0, config, strlen(config)); - if (spa_json_enter_object(&it0, &it1) <= 0) + if (spa_json_enter_object(&it0, &it1) <= 0) { + pw_log_error("delay:config must be an object"); return NULL; + } while (spa_json_get_string(&it1, key, sizeof(key)) > 0) { if (spa_streq(key, "max-delay")) { @@ -1075,9 +1097,11 @@ pw_log_error("delay:max-delay requires a number"); return NULL; } + } else { + pw_log_warn("delay: ignoring config key: '%s'", key); + if (spa_json_next(&it1, &val) < 0) + break; } - else if (spa_json_next(&it1, &val) < 0) - break; } if (max_delay <= 0.0f) max_delay = 1.0f;
View file
pipewire-0.3.69.tar.gz/src/modules/module-metadata.c -> pipewire-0.3.70.tar.gz/src/modules/module-metadata.c
Changed
@@ -10,6 +10,7 @@ #include "config.h" #include <spa/utils/result.h> +#include <spa/utils/json.h> #include <pipewire/impl.h> #include <pipewire/extensions/metadata.h> @@ -19,6 +20,12 @@ #define NAME "metadata" +#define FACTORY_USAGE "("PW_KEY_METADATA_NAME" = <name> ) " \ + "("PW_KEY_METADATA_VALUES" = " \ + " { ( id = <int> ) key = <string> ( type = <string> ) value = <json> } " \ + " ..." \ + " )" + PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic @@ -29,7 +36,7 @@ }; -void * pw_metadata_new(struct pw_context *context, struct pw_resource *resource, +struct pw_metadata *pw_metadata_new(struct pw_context *context, struct pw_resource *resource, struct pw_properties *properties); struct pw_proxy *pw_core_metadata_export(struct pw_core *core, @@ -47,6 +54,56 @@ struct pw_export_type export_metadata; }; +/* + * + * { ( "id" = <int>, ) "key" = <string> ("type" = <string>) "value" = <json> } + * .... + * + */ +static int fill_metadata(struct pw_metadata *metadata, const char *str) +{ + struct spa_json it3; + + spa_json_init(&it0, str, strlen(str)); + if (spa_json_enter_array(&it0, &it1) <= 0) + return -EINVAL; + + while (spa_json_enter_object(&it1, &it2) > 0) { + char key256, *k = NULL, *v = NULL, *t = NULL; + int id = 0; + + while (spa_json_get_string(&it2, key, sizeof(key)) > 0) { + int len; + const char *val; + + if ((len = spa_json_next(&it2, &val)) <= 0) + return -EINVAL; + + if (spa_streq(key, "id")) { + if (spa_json_parse_int(val, len, &id) <= 0) + return -EINVAL; + } else if (spa_streq(key, "key")) { + if ((k = malloc(len+1)) != NULL) + spa_json_parse_stringn(val, len, k, len+1); + } else if (spa_streq(key, "type")) { + if ((t = malloc(len+1)) != NULL) + spa_json_parse_stringn(val, len, t, len+1); + } else if (spa_streq(key, "value")) { + if (spa_json_is_container(val, len)) + len = spa_json_container_len(&it2, val, len); + if ((v = malloc(len+1)) != NULL) + spa_json_parse_stringn(val, len, v, len+1); + } + } + if (k != NULL && v != NULL) + pw_metadata_set_property(metadata, id, k, t, v); + free(k); + free(v); + free(t); + } + return 0; +} + static void *create_object(void *_data, struct pw_resource *resource, const char *type, @@ -56,9 +113,10 @@ { struct factory_data *data = _data; struct pw_context *context = pw_impl_module_get_context(data->module); - void *result; + struct pw_metadata *result; struct pw_resource *metadata_resource = NULL; struct pw_impl_client *client = resource ? pw_resource_get_client(resource) : NULL; + const char *str; int res; if (properties == NULL) @@ -91,14 +149,20 @@ goto error_node; } } else { - result = pw_context_create_metadata(context, NULL, properties, 0); - if (result == NULL) { + struct pw_impl_metadata *impl; + + impl = pw_context_create_metadata(context, NULL, properties, 0); + if (impl == NULL) { properties = NULL; res = -errno; goto error_node; } - pw_impl_metadata_register(result, NULL); + pw_impl_metadata_register(impl, NULL); + result = pw_impl_metadata_get_implementation(impl); } + if ((str = pw_properties_get(properties, PW_KEY_METADATA_VALUES)) != NULL) + fill_metadata(result, str); + return result; error_resource: @@ -189,7 +253,9 @@ "metadata", PW_TYPE_INTERFACE_Metadata, PW_VERSION_METADATA, - NULL, + pw_properties_new( + PW_KEY_FACTORY_USAGE, FACTORY_USAGE, + NULL), sizeof(*data)); if (factory == NULL) return -errno;
View file
pipewire-0.3.69.tar.gz/src/modules/module-metadata/metadata.c -> pipewire-0.3.70.tar.gz/src/modules/module-metadata/metadata.c
Changed
@@ -234,7 +234,7 @@ .destroy = global_resource_destroy, }; -void * +struct pw_metadata * pw_metadata_new(struct pw_context *context, struct pw_resource *resource, struct pw_properties *properties) { @@ -292,5 +292,5 @@ &impl->resource_listener, &global_resource_events, impl); - return impl; + return impl->metadata; }
View file
pipewire-0.3.69.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c -> pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c
Changed
@@ -46,9 +46,10 @@ static int module_echo_cancel_load(struct module *module) { + struct pw_properties * const props = module->props; struct module_echo_cancel_data *data = module->user_data; + const char *method; FILE *f; - const char *str; char *args; size_t size; uint32_t i; @@ -57,12 +58,14 @@ return -errno; fprintf(f, "{"); - /* Can't just serialise this dict because the "null" method gets - * interpreted as a JSON null */ - if ((str = pw_properties_get(data->props, "aec.method"))) - fprintf(f, " aec.method = \"%s\"", str); - if ((str = pw_properties_get(data->props, "aec.args"))) - fprintf(f, " aec.args = \"%s\"", str); + if ((method = pw_properties_get(props, "aec_method")) == NULL) + method = "webrtc"; + + fprintf(f, " library.name = \"aec/libspa-aec-%s\"", method); + + fprintf(f, " aec.args = {"); + pw_properties_serialize_dict(f, &data->props->dict, 0); + fprintf(f, " }"); if (data->info.rate != 0) fprintf(f, " audio.rate = %u", data->info.rate); if (data->info.channels != 0) { @@ -149,13 +152,86 @@ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; +static void rename_bool_prop(struct pw_properties *props, const char *pa_key, const char *pw_key) +{ + const char *str; + if ((str = pw_properties_get(props, pa_key)) != NULL) { + pw_properties_set(props, pw_key, module_args_parse_bool(str) ? "true" : "false"); + pw_properties_set(props, pa_key, NULL); + } +} +static int parse_point(const char **point, float f3) +{ + int length; + if (sscanf(*point, "%g,%g,%g%n", &f0, &f1, &f2, &length) != 3) + return -EINVAL; + return length; +} + +static int rename_geometry(struct pw_properties *props, const char *pa_key, const char *pw_key) +{ + const char *str; + int len; + char *args; + size_t size; + FILE *f; + + if ((str = pw_properties_get(props, pa_key)) == NULL) + return 0; + + pw_log_info("geometry: %s", str); + + if ((f = open_memstream(&args, &size)) == NULL) + return -errno; + + fprintf(f, " "); + while (true) { + float p3; + if ((len = parse_point(&str, p)) < 0) + break; + + fprintf(f, " %f %f %f ", p0, p1, p2); + str += len; + if (*str != ',') + break; + str++; + } + fprintf(f, ""); + fclose(f); + + pw_properties_set(props, pw_key, args); + free(args); + + pw_properties_set(props, pa_key, NULL); + return 0; +} + +static int rename_direction(struct pw_properties *props, const char *pa_key, const char *pw_key) +{ + const char *str; + int res; + float f3; + + if ((str = pw_properties_get(props, pa_key)) == NULL) + return 0; + + pw_log_info("direction: %s", str); + + if ((res = parse_point(&str, f)) < 0) + return res; + + pw_properties_setf(props, pw_key, " %f %f %f ", f0, f1, f2); + pw_properties_set(props, pa_key, NULL); + return 0; +} + static int module_echo_cancel_prepare(struct module * const module) { struct module_echo_cancel_data * const d = module->user_data; struct pw_properties * const props = module->props; struct pw_properties *aec_props = NULL, *sink_props = NULL, *source_props = NULL; struct pw_properties *playback_props = NULL, *capture_props = NULL; - const char *str; + const char *str, *method; struct spa_audio_info_raw info = { 0 }; int res; @@ -217,13 +293,23 @@ pw_properties_set(props, "sink_properties", NULL); } - if ((str = pw_properties_get(props, "aec_method")) != NULL) { - pw_properties_set(aec_props, "aec.method", str); - pw_properties_set(props, "aec_method", NULL); - } + if ((method = pw_properties_get(props, "aec_method")) == NULL) + method = "webrtc"; if ((str = pw_properties_get(props, "aec_args")) != NULL) { - pw_properties_set(aec_props, "aec.args", str); + module_args_add_props(aec_props, str); + if (spa_streq(method, "webrtc")) { + rename_bool_prop(aec_props, "high_pass_filter", "webrtc.high_pass_filter"); + rename_bool_prop(aec_props, "noise_suppression", "webrtc.noise_suppression"); + rename_bool_prop(aec_props, "analog_gain_control", "webrtc.gain_control"); + rename_bool_prop(aec_props, "digital_gain_control", "webrtc.gain_control"); + rename_bool_prop(aec_props, "voice_detection", "webrtc.voice_detection"); + rename_bool_prop(aec_props, "extended_filter", "webrtc.extended_filter"); + rename_bool_prop(aec_props, "experimental_agc", "webrtc.experimental_agc"); + rename_bool_prop(aec_props, "beamforming", "webrtc.beamforming"); + rename_geometry(aec_props, "mic_geometry", "webrtc.mic-geometry"); + rename_direction(aec_props, "target_direction", "webrtc.target-direction"); + } pw_properties_set(props, "aec_args", NULL); }
View file
pipewire-0.3.69.tar.gz/src/modules/module-pulse-tunnel.c -> pipewire-0.3.70.tar.gz/src/modules/module-pulse-tunnel.c
Changed
@@ -162,9 +162,13 @@ void *buffer; uint8_t empty8192; + bool mute; + pa_cvolume volume; + pa_threaded_mainloop *pa_mainloop; pa_context *pa_context; pa_stream *pa_stream; + uint32_t pa_index; struct ratelimit rate_limit; @@ -233,6 +237,79 @@ } } +static void stream_param_changed(void *d, uint32_t id, const struct spa_pod *param) +{ + struct impl *impl = d; + char buf1024; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + struct spa_pod_frame f1; + struct spa_pod_object *obj = (struct spa_pod_object *) param; + struct spa_pod_prop *prop; + + if (param == NULL || id != SPA_PARAM_Props) + return; + + spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); + + SPA_POD_OBJECT_FOREACH(obj, prop) { + switch (prop->key) { + case SPA_PROP_mute: + { + bool mute; + if (spa_pod_get_bool(&prop->value, &mute) == 0) { + pa_threaded_mainloop_lock(impl->pa_mainloop); + if (impl->mode == MODE_SOURCE) { + pa_context_set_source_output_mute(impl->pa_context, + impl->pa_index, mute, + NULL, impl); + } else { + pa_context_set_sink_input_mute(impl->pa_context, + impl->pa_index, mute, + NULL, impl); + } + pa_threaded_mainloop_unlock(impl->pa_mainloop); + } + break; + } + case SPA_PROP_channelVolumes: + { + struct pa_cvolume volume; + uint32_t n; + float volsSPA_AUDIO_MAX_CHANNELS; + + if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, + vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + volume.channels = n; + for (n = 0; n < volume.channels; n++) + volume.valuesn = pa_sw_volume_from_linear(volsn); + + pa_threaded_mainloop_lock(impl->pa_mainloop); + if (impl->mode == MODE_SOURCE) { + pa_context_set_source_output_volume(impl->pa_context, + impl->pa_index, &volume, + NULL, impl); + } else { + pa_context_set_sink_input_volume(impl->pa_context, + impl->pa_index, &volume, + NULL, impl); + } + pa_threaded_mainloop_unlock(impl->pa_mainloop); + } + break; + } + case SPA_PROP_softVolumes: + case SPA_PROP_softMute: + break; + default: + spa_pod_builder_raw_padded(&b, prop, SPA_POD_PROP_SIZE(prop)); + break; + } + } + param = spa_pod_builder_pop(&b, &f0); + + pw_stream_set_param(impl->stream, id, param); +} + static void update_rate(struct impl *impl, uint32_t filled) { float error, corr; @@ -356,6 +433,7 @@ .destroy = stream_destroy, .state_changed = stream_state_changed, .io_changed = stream_io_changed, + .param_changed = stream_param_changed, .process = playback_stream_process }; @@ -364,6 +442,7 @@ .destroy = stream_destroy, .state_changed = stream_state_changed, .io_changed = stream_io_changed, + .param_changed = stream_param_changed, .process = capture_stream_process }; @@ -464,6 +543,7 @@ do_destroy = true; SPA_FALLTHROUGH; case PA_STREAM_READY: + impl->pa_index = pa_stream_get_index(impl->pa_stream); pa_threaded_mainloop_signal(impl->pa_mainloop, 0); break; case PA_STREAM_UNCONNECTED: @@ -615,6 +695,78 @@ pa_threaded_mainloop_signal(impl->pa_mainloop, 0); } +static int +do_stream_sync_volumes(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *impl = user_data; + char buf1024; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + struct spa_pod_frame f1; + struct spa_pod *param; + uint32_t i; + float volsSPA_AUDIO_MAX_CHANNELS; + float soft_volsSPA_AUDIO_MAX_CHANNELS; + + for (i = 0; i < impl->volume.channels; i++) { + volsi = pa_sw_volume_to_linear(impl->volume.valuesi); + soft_volsi = 1.0f; + } + + spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); + spa_pod_builder_prop(&b, SPA_PROP_softMute, 0); + spa_pod_builder_bool(&b, impl->mute); + spa_pod_builder_prop(&b, SPA_PROP_mute, 0); + spa_pod_builder_bool(&b, impl->mute); + + spa_pod_builder_prop(&b, SPA_PROP_channelVolumes, 0); + spa_pod_builder_array(&b, sizeof(float), SPA_TYPE_Float, + impl->volume.channels, vols); + spa_pod_builder_prop(&b, SPA_PROP_softVolumes, 0); + spa_pod_builder_array(&b, sizeof(float), SPA_TYPE_Float, + impl->volume.channels, soft_vols); + param = spa_pod_builder_pop(&b, &f0); + + pw_stream_set_param(impl->stream, SPA_PARAM_Props, param); + return 0; +} + +static void stream_sync_volumes(struct impl *impl, const struct pa_cvolume *volume, bool mute) +{ + impl->mute = mute; + impl->volume = *volume; + pw_loop_invoke(impl->main_loop, do_stream_sync_volumes, 1, NULL, 0, false, impl); +} + +static void source_output_info_cb(pa_context *c, const pa_source_output_info *i, int eol, void *userdata) +{ + struct impl *impl = userdata; + if (i != NULL) + stream_sync_volumes(impl, &i->volume, i->mute); +} + +static void sink_input_info_cb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) +{ + struct impl *impl = userdata; + if (i != NULL) + stream_sync_volumes(impl, &i->volume, i->mute); +} + +static void context_subscribe_cb(pa_context *c, pa_subscription_event_type_t t, + uint32_t idx, void *userdata) +{ + struct impl *impl = userdata; + if (idx != impl->pa_index) + return; + + if (impl->mode == MODE_SOURCE) + pa_context_get_source_output_info(impl->pa_context, + idx, source_output_info_cb, impl); + else + pa_context_get_sink_input_info(impl->pa_context, + idx, sink_input_info_cb, impl); +} + static pa_proplist* tunnel_new_proplist(struct impl *impl) { pa_proplist *proplist = pa_proplist_new(); @@ -659,6 +811,8 @@ pa_threaded_mainloop_lock(impl->pa_mainloop); + pa_context_set_subscribe_callback(impl->pa_context, context_subscribe_cb, impl); + if (pa_threaded_mainloop_start(impl->pa_mainloop) < 0) goto error_unlock; @@ -717,6 +871,9 @@ if (impl->mode == MODE_SOURCE) { bufferattr.fragsize = latency_bytes / 2; + pa_context_subscribe(impl->pa_context, + PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, NULL, impl); + res = pa_stream_connect_record(impl->pa_stream, remote_node_target, &bufferattr, PA_STREAM_DONT_MOVE | @@ -728,6 +885,9 @@ bufferattr.minreq = bufferattr.tlength / 4; bufferattr.prebuf = bufferattr.tlength; + pa_context_subscribe(impl->pa_context, + PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, impl); + res = pa_stream_connect_playback(impl->pa_stream, remote_node_target, &bufferattr, PA_STREAM_DONT_MOVE |
View file
pipewire-0.3.69.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.70.tar.gz/src/modules/module-raop-sink.c
Changed
@@ -145,6 +145,10 @@ #define DEFAULT_LATENCY 22050 +#define VOLUME_MAX 0.0 +#define VOLUME_DEF -30.0 +#define VOLUME_MIN -144.0 + #define MODULE_USAGE "( raop.ip=<ip address of host> ) " \ "( raop.port=<remote port> ) " \ "( raop.name=<name of host> ) " \ @@ -252,6 +256,9 @@ unsigned int ready:1; unsigned int recording:1; + bool mute; + float volume; + uint8_t bufferFRAMES_PER_TCP_PACKET * 4; uint32_t filled; }; @@ -1547,6 +1554,70 @@ return rtsp_send(impl, "TEARDOWN", NULL, NULL, rtsp_teardown_reply); } +static void stream_props_changed(struct impl *impl, uint32_t id, const struct spa_pod *param) +{ + char buf1024; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + struct spa_pod_frame f1; + struct spa_pod_object *obj = (struct spa_pod_object *) param; + struct spa_pod_prop *prop; + + spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); + + SPA_POD_OBJECT_FOREACH(obj, prop) { + switch (prop->key) { + case SPA_PROP_mute: + { + bool mute; + if (spa_pod_get_bool(&prop->value, &mute) == 0) { + impl->mute = mute; + } + spa_pod_builder_prop(&b, SPA_PROP_softMute, 0); + spa_pod_builder_bool(&b, impl->mute); + spa_pod_builder_raw_padded(&b, prop, SPA_POD_PROP_SIZE(prop)); + break; + } + case SPA_PROP_channelVolumes: + { + uint32_t i, n_vols; + float volsSPA_AUDIO_MAX_CHANNELS, volume; + float soft_volsSPA_AUDIO_MAX_CHANNELS; + char header128, volstr64; + + if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, + vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + volume = 0.0f; + for (i = 0; i < n_vols; i++) { + volume += volsi; + soft_volsi = 1.0f; + } + volume /= n_vols; + volume = SPA_CLAMPF(20.0 * log10(volume), VOLUME_MIN, VOLUME_MAX); + impl->volume = volume; + + snprintf(header, sizeof(header), "volume: %s\r\n", + spa_dtoa(volstr, sizeof(volstr), volume)); + rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, NULL); + } + spa_pod_builder_prop(&b, SPA_PROP_softVolumes, 0); + spa_pod_builder_array(&b, sizeof(float), SPA_TYPE_Float, + n_vols, soft_vols); + spa_pod_builder_raw_padded(&b, prop, SPA_POD_PROP_SIZE(prop)); + break; + } + case SPA_PROP_softVolumes: + case SPA_PROP_softMute: + break; + default: + spa_pod_builder_raw_padded(&b, prop, SPA_POD_PROP_SIZE(prop)); + break; + } + } + param = spa_pod_builder_pop(&b, &f0); + + pw_stream_set_param(impl->stream, id, param); +} + static void stream_param_changed(void *data, uint32_t id, const struct spa_pod *param) { struct impl *impl = data; @@ -1558,6 +1629,9 @@ else rtsp_do_connect(impl); break; + case SPA_PARAM_Props: + if (param != NULL) + stream_props_changed(impl, id, param); default: break; }
View file
pipewire-0.3.69.tar.gz/src/modules/module-rtp-session.c -> pipewire-0.3.70.tar.gz/src/modules/module-rtp-session.c
Changed
@@ -264,17 +264,8 @@ { ssize_t n; n = sendmsg(fd, msg, MSG_NOSIGNAL); - if (n < 0) { - switch (errno) { - case ECONNREFUSED: - case ECONNRESET: - pw_log_debug("remote end not listening"); - break; - default: - pw_log_debug("sendmsg() failed: %m"); - break; - } - } + if (n < 0) + pw_log_debug("sendmsg() failed: %m"); return n; } @@ -889,7 +880,7 @@ latency = t3 - t1; offset = ((t3 + t1) / 2) - t2; - pw_log_info("latency:%f offset:%f", latency / 1e5, offset / 1e5); + pw_log_debug("latency:%f offset:%f", latency / 1e5, offset / 1e5); if (hdr->count >= 2) return; } @@ -1654,7 +1645,7 @@ struct session *sess; uint64_t current_time = impl->next_time; - pw_log_info("timeout"); + pw_log_debug("timeout"); spa_list_for_each(sess, &impl->sessions, link) { if (sess->state != SESSION_STATE_ESTABLISHED) continue;
View file
pipewire-0.3.69.tar.gz/src/modules/module-rtp-sink.c -> pipewire-0.3.70.tar.gz/src/modules/module-rtp-sink.c
Changed
@@ -197,17 +197,8 @@ msg.msg_flags = 0; n = sendmsg(impl->rtp_fd, &msg, MSG_NOSIGNAL); - if (n < 0) { - switch (errno) { - case ECONNREFUSED: - case ECONNRESET: - pw_log_debug("remote end not listening"); - break; - default: - pw_log_warn("sendmsg() failed: %m"); - break; - } - } + if (n < 0) + pw_log_debug("sendmsg() failed: %m"); } static void stream_state_changed(void *data, bool started, const char *error)
View file
pipewire-0.3.69.tar.gz/src/modules/module-x11-bell.c -> pipewire-0.3.70.tar.gz/src/modules/module-x11-bell.c
Changed
@@ -159,8 +159,8 @@ unsigned int auto_ctrls, auto_values; if (!(impl->display = XOpenDisplay(name))) { - pw_log_error("XOpenDisplay() failed"); - return -EIO; + pw_log_info("XOpenDisplay() failed. Uninstall or disable the module-x11-bell module"); + return -EHOSTDOWN; } impl->source = pw_loop_add_io(impl->loop,
View file
pipewire-0.3.69.tar.gz/src/pipewire/conf.c -> pipewire-0.3.70.tar.gz/src/pipewire/conf.c
Changed
@@ -634,8 +634,10 @@ match++; pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value); } - else + else { fail++; + break; + } } if (match > 0 && fail == 0) return true; @@ -931,17 +933,16 @@ SPA_EXPORT -int pw_context_conf_section_for_each(struct pw_context *context, const char *section, +int pw_conf_section_for_each(const struct spa_dict *conf, const char *section, int (*callback) (void *data, const char *location, const char *section, const char *str, size_t len), void *data) { - struct pw_properties *conf = context->conf; const char *path = NULL; const struct spa_dict_item *it; int res = 0; - spa_dict_for_each(it, &conf->dict) { + spa_dict_for_each(it, conf) { if (spa_strendswith(it->key, "config.path")) { path = it->value; continue; @@ -961,31 +962,6 @@ return res; } -SPA_EXPORT -int pw_context_parse_conf_section(struct pw_context *context, - struct pw_properties *conf, const char *section) -{ - struct data data = { .context = context }; - int res; - - if (spa_streq(section, "context.spa-libs")) - res = pw_context_conf_section_for_each(context, section, - parse_spa_libs, &data); - else if (spa_streq(section, "context.modules")) - res = pw_context_conf_section_for_each(context, section, - parse_modules, &data); - else if (spa_streq(section, "context.objects")) - res = pw_context_conf_section_for_each(context, section, - parse_objects, &data); - else if (spa_streq(section, "context.exec")) - res = pw_context_conf_section_for_each(context, section, - parse_exec, &data); - else - res = -EINVAL; - - return res == 0 ? data.count : res; -} - static int update_props(void *user_data, const char *location, const char *key, const char *val, size_t len) { @@ -994,6 +970,27 @@ return 0; } +SPA_EXPORT +int pw_conf_section_update_props(const struct spa_dict *conf, + const char *section, struct pw_properties *props) +{ + struct data data = { .props = props }; + int res; + const char *str; + + res = pw_conf_section_for_each(conf, section, + update_props, &data); + + str = pw_properties_get(props, "config.ext"); + if (res == 0 && str != NULL) { + char key128; + snprintf(key, sizeof(key), "%s.%s", section, str); + res = pw_conf_section_for_each(conf, key, + update_props, &data); + } + return res == 0 ? data.count : res; +} + static int try_load_conf(const char *conf_prefix, const char *conf_name, struct pw_properties *conf) { @@ -1026,13 +1023,12 @@ conf_name = getenv("PIPEWIRE_CONFIG_NAME"); if ((res = try_load_conf(conf_prefix, conf_name, conf)) < 0) { conf_name = pw_properties_get(props, PW_KEY_CONFIG_NAME); - if ((res = try_load_conf(conf_prefix, conf_name, conf)) < 0) { + if (conf_name == NULL) conf_name = "client.conf"; - if ((res = try_load_conf(conf_prefix, conf_name, conf)) < 0) { - pw_log_error("can't load default config %s: %s", - conf_name, spa_strerror(res)); - return res; - } + if ((res = try_load_conf(conf_prefix, conf_name, conf)) < 0) { + pw_log_error("can't load config %s: %s", + conf_name, spa_strerror(res)); + return res; } } @@ -1063,26 +1059,6 @@ return res; } -SPA_EXPORT -int pw_context_conf_update_props(struct pw_context *context, - const char *section, struct pw_properties *props) -{ - struct data data = { .context = context, .props = props }; - int res; - const char *str = pw_properties_get(props, "config.ext"); - - res = pw_context_conf_section_for_each(context, section, - update_props, &data); - if (res == 0 && str != NULL) { - char key128; - snprintf(key, sizeof(key), "%s.%s", section, str); - res = pw_context_conf_section_for_each(context, key, - update_props, &data); - } - return res == 0 ? data.count : res; -} - - /** * * { @@ -1171,7 +1147,7 @@ } SPA_EXPORT -int pw_context_conf_section_match_rules(struct pw_context *context, const char *section, +int pw_conf_section_match_rules(const struct spa_dict *conf, const char *section, const struct spa_dict *props, int (*callback) (void *data, const char *location, const char *action, const char *str, size_t len), @@ -1182,15 +1158,71 @@ .matched = callback, .data = data }; int res; - const char *str = spa_dict_lookup(props, "config.ext"); + const char *str; - res = pw_context_conf_section_for_each(context, section, + res = pw_conf_section_for_each(conf, section, match_rules, &match); + + str = spa_dict_lookup(props, "config.ext"); if (res == 0 && str != NULL) { char key128; snprintf(key, sizeof(key), "%s.%s", section, str); - res = pw_context_conf_section_for_each(context, key, + res = pw_conf_section_for_each(conf, key, match_rules, &match); } return res; } + +SPA_EXPORT +int pw_context_conf_update_props(struct pw_context *context, + const char *section, struct pw_properties *props) +{ + return pw_conf_section_update_props(&context->conf->dict, + section, props); +} + +SPA_EXPORT +int pw_context_conf_section_for_each(struct pw_context *context, const char *section, + int (*callback) (void *data, const char *location, const char *section, + const char *str, size_t len), + void *data) +{ + return pw_conf_section_for_each(&context->conf->dict, section, callback, data); +} + + +SPA_EXPORT +int pw_context_parse_conf_section(struct pw_context *context, + struct pw_properties *conf, const char *section) +{ + struct data data = { .context = context }; + int res; + + if (spa_streq(section, "context.spa-libs")) + res = pw_context_conf_section_for_each(context, section, + parse_spa_libs, &data); + else if (spa_streq(section, "context.modules")) + res = pw_context_conf_section_for_each(context, section, + parse_modules, &data); + else if (spa_streq(section, "context.objects")) + res = pw_context_conf_section_for_each(context, section, + parse_objects, &data); + else if (spa_streq(section, "context.exec")) + res = pw_context_conf_section_for_each(context, section, + parse_exec, &data); + else + res = -EINVAL; + + return res == 0 ? data.count : res; +} + +SPA_EXPORT +int pw_context_conf_section_match_rules(struct pw_context *context, const char *section, + const struct spa_dict *props, + int (*callback) (void *data, const char *location, const char *action, + const char *str, size_t len), + void *data) +{ + return pw_conf_section_match_rules(&context->conf->dict, section, + props, callback, data); +}
View file
pipewire-0.3.69.tar.gz/src/pipewire/conf.h -> pipewire-0.3.70.tar.gz/src/pipewire/conf.h
Changed
@@ -18,12 +18,25 @@ int pw_conf_load_state(const char *prefix, const char *name, struct pw_properties *conf); int pw_conf_save_state(const char *prefix, const char *name, const struct pw_properties *conf); +int pw_conf_section_update_props(const struct spa_dict *conf, + const char *section, struct pw_properties *props); + +int pw_conf_section_for_each(const struct spa_dict *conf, const char *section, + int (*callback) (void *data, const char *location, const char *section, + const char *str, size_t len), + void *data); + int pw_conf_match_rules(const char *str, size_t len, const char *location, const struct spa_dict *props, int (*callback) (void *data, const char *location, const char *action, const char *str, size_t len), void *data); +int pw_conf_section_match_rules(const struct spa_dict *conf, const char *section, + const struct spa_dict *props, + int (*callback) (void *data, const char *location, const char *action, + const char *str, size_t len), + void *data); /** * \} */
View file
pipewire-0.3.69.tar.gz/src/pipewire/context.c -> pipewire-0.3.70.tar.gz/src/pipewire/context.c
Changed
@@ -788,9 +788,6 @@ struct pw_impl_port *p; struct pw_impl_link *l; - if (!node->runnable) - return 0; - pw_log_debug("node %p: '%s'", node, node->name); spa_list_for_each(p, &node->input_ports, link) { @@ -885,7 +882,7 @@ pw_impl_link_prepare(l); - if (!l->prepared || (t != n && t->visited)) + if (!l->prepared) continue; if (!l->passive) @@ -906,7 +903,7 @@ pw_impl_link_prepare(l); - if (!l->prepared || (t != n && t->visited)) + if (!l->prepared) continue; if (!l->passive) @@ -936,7 +933,8 @@ pw_log_debug(" next node %p: '%s' runnable:%u", n, n->name, n->runnable); } spa_list_for_each(n, collect, sort_link) - run_nodes(context, n, collect); + if (!n->driver && n->runnable) + run_nodes(context, n, collect); return 0; }
View file
pipewire-0.3.69.tar.gz/src/pipewire/extensions/metadata.h -> pipewire-0.3.70.tar.gz/src/pipewire/extensions/metadata.h
Changed
@@ -80,6 +80,7 @@ #define pw_metadata_clear(c) pw_metadata_method(c,clear,0) #define PW_KEY_METADATA_NAME "metadata.name" +#define PW_KEY_METADATA_VALUES "metadata.values" /** * \}
View file
pipewire-0.3.69.tar.gz/src/pipewire/properties.c -> pipewire-0.3.70.tar.gz/src/pipewire/properties.c
Changed
@@ -4,6 +4,8 @@ #include <stdio.h> #include <stdarg.h> + +#include <spa/utils/ansi.h> #include <spa/utils/json.h> #include <spa/utils/string.h> @@ -640,12 +642,30 @@ return pw_array_get_unchecked(&impl->items, index, struct spa_dict_item)->key; } -static int encode_string(FILE *f, const char *val) +#define NORMAL(c) ((c)->colors ? SPA_ANSI_RESET : "") +#define LITERAL(c) ((c)->colors ? SPA_ANSI_BRIGHT_MAGENTA : "") +#define NUMBER(c) ((c)->colors ? SPA_ANSI_BRIGHT_CYAN : "") +#define STRING(c) ((c)->colors ? SPA_ANSI_BRIGHT_GREEN : "") +#define KEY(c) ((c)->colors ? SPA_ANSI_BRIGHT_BLUE : "") +#define CONTAINER(c) ((c)->colors ? SPA_ANSI_BRIGHT_YELLOW : "") + +struct dump_config { + FILE *file; + int indent; + const char *sep; + bool colors; + bool recurse; +}; + +static int encode_string(struct dump_config *c, const char *before, + const char *val, int size, const char *after) { - int len = 0; - len += fprintf(f, "\""); - while (*val) { - switch (*val) { + FILE *f = c->file; + int i, len = 0; + len += fprintf(f, "%s\"", before); + for (i = 0; i < size; i++) { + char v = vali; + switch (v) { case '\n': len += fprintf(f, "\\n"); break; @@ -661,53 +681,117 @@ case '\f': len += fprintf(f, "\\f"); break; - case '\\': - case '"': - len += fprintf(f, "\\%c", *val); - break; + case '\\': case '"': + len += fprintf(f, "\\%c", v); + break; default: - if (*val > 0 && *val < 0x20) - len += fprintf(f, "\\u%04x", *val); + if (v > 0 && v < 0x20) + len += fprintf(f, "\\u%04x", v); else - len += fprintf(f, "%c", *val); + len += fprintf(f, "%c", v); break; } - val++; } - len += fprintf(f, "\""); + len += fprintf(f, "\"%s", after); return len-1; } +static int dump(struct dump_config *c, int indent, struct spa_json *it, const char *value, int len) +{ + FILE *file = c->file; + struct spa_json sub; + int count = 0; + char key1024; + + if (value == NULL || len == 0) { + fprintf(file, "%snull%s", LITERAL(c), NORMAL(c)); + } else if (spa_json_is_container(value, len) && !c->recurse) { + len = spa_json_container_len(it, value, len); + fprintf(file, "%s%.*s%s", CONTAINER(c), len, value, NORMAL(c)); + } else if (spa_json_is_array(value, len)) { + fprintf(file, ""); + spa_json_enter(it, &sub); + indent += c->indent; + while ((len = spa_json_next(&sub, &value)) > 0) { + fprintf(file, "%s%s%*s", count++ > 0 ? "," : "", + c->sep, indent, ""); + dump(c, indent, &sub, value, len); + } + indent -= c->indent; + fprintf(file, "%s%*s", count > 0 ? c->sep : "", + count > 0 ? indent : 0, ""); + } else if (spa_json_is_object(value, len)) { + fprintf(file, "{"); + spa_json_enter(it, &sub); + indent += c->indent; + while (spa_json_get_string(&sub, key, sizeof(key)) > 0) { + fprintf(file, "%s%s%*s", + count++ > 0 ? "," : "", + c->sep, indent, ""); + encode_string(c, KEY(c), key, strlen(key), NORMAL(c)); + fprintf(file, ": "); + if ((len = spa_json_next(&sub, &value)) <= 0) + break; + dump(c, indent, &sub, value, len); + } + indent -= c->indent; + fprintf(file, "%s%*s}", count > 0 ? c->sep : "", + count > 0 ? indent : 0, ""); + } else if (spa_json_is_null(value, len) || + spa_json_is_bool(value, len)) { + fprintf(file, "%s%.*s%s", LITERAL(c), len, value, NORMAL(c)); + } else if (spa_json_is_int(value, len) || + spa_json_is_float(value, len)) { + fprintf(file, "%s%.*s%s", NUMBER(c), len, value, NORMAL(c)); + } else if (spa_json_is_string(value, len)) { + fprintf(file, "%s%.*s%s", STRING(c), len, value, NORMAL(c)); + } else { + encode_string(c, STRING(c), value, len, NORMAL(c)); + } + return 0; +} + SPA_EXPORT int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags) { const struct spa_dict_item *it; int count = 0; - char key1024; + struct dump_config cfg = { + .file = f, + .indent = flags & PW_PROPERTIES_FLAG_NL ? 2 : 0, + .sep = flags & PW_PROPERTIES_FLAG_NL ? "\n" : " ", + .colors = SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_COLORS), + .recurse = SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_RECURSE), + }, *c = &cfg; + const char *enc = flags & PW_PROPERTIES_FLAG_ARRAY ? "" : "{}"; + + if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_ENCLOSE)) + fprintf(f, "%c", enc0); spa_dict_for_each(it, dict) { - size_t len = it->value ? strlen(it->value) : 0; + char key1024; + int len; + const char *value; + struct spa_json sub; - if (spa_json_encode_string(key, sizeof(key)-1, it->key) >= (int)sizeof(key)-1) - continue; + fprintf(f, "%s%s%*s", count == 0 ? "" : ",", c->sep, c->indent, ""); - fprintf(f, "%s%s %s: ", - count == 0 ? "" : ",", - flags & PW_PROPERTIES_FLAG_NL ? "\n" : "", - key); - - if (it->value == NULL) { - fprintf(f, "null"); - } else if (spa_json_is_null(it->value, len) || - spa_json_is_float(it->value, len) || - spa_json_is_bool(it->value, len) || - spa_json_is_container(it->value, len) || - spa_json_is_string(it->value, len)) { - fprintf(f, "%s", it->value); - } else { - encode_string(f, it->value); + if (!(flags & PW_PROPERTIES_FLAG_ARRAY)) { + if (spa_json_encode_string(key, sizeof(key)-1, it->key) >= (int)sizeof(key)-1) + continue; + fprintf(f, "%s%s%s: ", KEY(c), key, NORMAL(c)); } + value = it->value; + + len = value ? strlen(value) : 0; + spa_json_init(&sub, value, len); + if ((len = spa_json_next(&sub, &value)) < 0) + break; + + dump(c, c->indent, &sub, value, len); count++; } + if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_ENCLOSE)) + fprintf(f, "%s%c", c->sep, enc1); return count; }
View file
pipewire-0.3.69.tar.gz/src/pipewire/properties.h -> pipewire-0.3.70.tar.gz/src/pipewire/properties.h
Changed
@@ -136,7 +136,11 @@ const char * pw_properties_iterate(const struct pw_properties *properties, void **state); -#define PW_PROPERTIES_FLAG_NL (1<<0) +#define PW_PROPERTIES_FLAG_NL (1<<0) +#define PW_PROPERTIES_FLAG_RECURSE (1<<1) +#define PW_PROPERTIES_FLAG_ENCLOSE (1<<2) +#define PW_PROPERTIES_FLAG_ARRAY (1<<3) +#define PW_PROPERTIES_FLAG_COLORS (1<<4) int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags); static inline bool pw_properties_parse_bool(const char *value) {
View file
pipewire-0.3.69.tar.gz/src/pipewire/stream.c -> pipewire-0.3.70.tar.gz/src/pipewire/stream.c
Changed
@@ -156,7 +156,7 @@ unsigned int driving:1; unsigned int using_trigger:1; unsigned int trigger:1; - int in_set_control; + int in_set_param; }; static int get_param_index(uint32_t id) @@ -566,7 +566,7 @@ if (id != SPA_PARAM_Props) return -ENOTSUP; - if (impl->in_set_control == 0) + if (impl->in_set_param == 0) pw_stream_emit_param_changed(stream, id, param); return 0; @@ -2138,6 +2138,27 @@ return res; } +static inline int stream_set_param(struct stream *impl, uint32_t id, const struct spa_pod *param) +{ + int res = 0; + impl->in_set_param++; + res = pw_impl_node_set_param(impl->node, id, 0, param); + impl->in_set_param--; + return res; +} + +SPA_EXPORT +int pw_stream_set_param(struct pw_stream *stream, uint32_t id, const struct spa_pod *param) +{ + struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); + ensure_loop(impl->context->main_loop, return -EIO); + + if (impl->node == NULL) + return -EIO; + + return stream_set_param(impl, id, param); +} + SPA_EXPORT int pw_stream_set_control(struct pw_stream *stream, uint32_t id, uint32_t n_values, float *values, ...) { @@ -2194,9 +2215,7 @@ va_end(varargs); - impl->in_set_control++; - pw_impl_node_set_param(impl->node, SPA_PARAM_Props, 0, pod); - impl->in_set_control--; + stream_set_param(impl, SPA_PARAM_Props, pod); return 0; }
View file
pipewire-0.3.69.tar.gz/src/pipewire/stream.h -> pipewire-0.3.70.tar.gz/src/pipewire/stream.h
Changed
@@ -447,12 +447,7 @@ const char *error, /**< an error message */ ...) SPA_PRINTF_FUNC(3, 4); -/** Complete the negotiation process with result code \a res - * - * This function should be called after notification of the format. - - * When \a res indicates success, \a params contain the parameters for the - * allocation state. */ +/** Update the param exposed on the stream. */ int pw_stream_update_params(struct pw_stream *stream, /**< a \ref pw_stream */ const struct spa_pod **params, /**< an array of params. The params should @@ -460,6 +455,14 @@ * buffer allocation. */ uint32_t n_params /**< number of elements in \a params */); +/** + * Set a parameter on the stream. This is like pw_stream_set_control() but with + * a complete spa_pod param. It can also be called from the param_changed event handler + * to intercept and modify the param for the adapter. Since 0.3.70 */ +int pw_stream_set_param(struct pw_stream *stream, /**< a \ref pw_stream */ + uint32_t id, /**< the id of the param */ + const struct spa_pod *param /**< the params to set */); + /** Get control values */ const struct pw_stream_control *pw_stream_get_control(struct pw_stream *stream, uint32_t id);
View file
pipewire-0.3.69.tar.gz/src/tools/meson.build -> pipewire-0.3.70.tar.gz/src/tools/meson.build
Changed
@@ -1,5 +1,6 @@ tools_sources = 'pw-mon', 'pw-mon.c' , + 'pw-config', 'pw-config.c' , 'pw-dot', 'pw-dot.c' , 'pw-dump', 'pw-dump.c' , 'pw-profiler', 'pw-profiler.c' ,
View file
pipewire-0.3.70.tar.gz/src/tools/pw-config.c
Added
@@ -0,0 +1,254 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#include "config.h" + +#include <getopt.h> +#include <signal.h> +#include <locale.h> +#include <unistd.h> + +#include <spa/utils/result.h> +#include <spa/utils/json.h> + +#include "pipewire/pipewire.h" +#include "pipewire/log.h" + +#define DEFAULT_NAME "pipewire.conf" +#define DEFAULT_PREFIX "" + +struct data { + const char *opt_name; + const char *opt_prefix; + const char *opt_cmd; + bool opt_recurse; + bool opt_newline; + bool opt_colors; + struct pw_properties *conf; + struct pw_properties *assemble; + int count; + bool array; +}; + +static void print_all_properties(struct data *d, struct pw_properties *props) +{ + pw_properties_serialize_dict(stdout, + &props->dict, + (d->opt_newline ? PW_PROPERTIES_FLAG_NL : 0) | + (d->opt_recurse ? PW_PROPERTIES_FLAG_RECURSE : 0) | + (d->opt_colors ? PW_PROPERTIES_FLAG_COLORS : 0) | + (d->array ? PW_PROPERTIES_FLAG_ARRAY : 0) | + PW_PROPERTIES_FLAG_ENCLOSE); + fprintf(stdout, "\n"); +} + +static void list_paths(struct data *d) +{ + const struct spa_dict_item *it; + + spa_dict_for_each(it, &d->conf->dict) { + if (spa_strstartswith(it->key, "config.path")) { + pw_properties_set(d->assemble, it->key, it->value); + } + if (spa_strstartswith(it->key, "override.") && + spa_strendswith(it->key, ".config.path")) { + pw_properties_set(d->assemble, it->key, it->value); + } + } +} + +static int do_merge_section(void *data, const char *location, const char *section, + const char *str, size_t len) +{ + struct data *d = data; + struct spa_json it2; + int l; + const char *value; + + spa_json_init(&it0, str, len); + if ((l = spa_json_next(&it0, &value)) <= 0) + return 0; + + if (spa_json_is_array(value, l)) { + char key128; + + spa_json_enter(&it0, &it1); + while ((l = spa_json_next(&it1, &value)) > 0) { + if (spa_json_is_container(value, l)) + l = spa_json_container_len(&it1, value, l); + + snprintf(key, sizeof(key), "%d-%s", d->count++, location); + pw_properties_setf(d->assemble, key, "%.*s", l, value); + } + d->array = true; + } + else if (spa_json_is_object(value, l)) { + pw_properties_update_string(d->assemble, str, len); + } + return 0; +} + +static int do_list_section(void *data, const char *location, const char *section, + const char *str, size_t len) +{ + struct data *d = data; + char key128; + snprintf(key, sizeof(key), "%d-%s", d->count++, location); + pw_properties_setf(d->assemble, key, "%.*s", (int)len, str); + return 0; +} + +static void section_for_each(struct data *d, const char *section, + int (*callback) (void *data, const char *location, const char *section, + const char *str, size_t len)) +{ + const char *str; + char key128; + + pw_conf_section_for_each(&d->conf->dict, section, callback, d); + str = pw_properties_get(d->assemble, "config.ext"); + if (str != NULL) { + snprintf(key, sizeof(key), "%s.%s", section, str); + pw_conf_section_for_each(&d->conf->dict, key, callback, d); + } +} + +static void show_help(const char *name, bool error) +{ + fprintf(error ? stderr : stdout, "%1$s : PipeWire config manager.\n" + "Usage:\n" + " %1$s options paths List config paths (default action)\n" + " %1$s options list SECTION List config section\n" + " %1$s options merge SECTION Merge a config section\n\n" + "Options:\n" + " -h, --help Show this help\n" + " --version Show version\n" + " -n, --name Config Name (default '%2$s')\n" + " -p, --prefix Config Prefix (default '%3$s')\n" + " -L, --no-newline Omit newlines after values\n" + " -r, --recurse Reformat config sections recursively\n" + " -N, --no-colors disable color output\n" + " -C, --color=WHEN whether to enable color support. WHEN is `never`, `always`, or `auto`\n", + name, DEFAULT_NAME, DEFAULT_PREFIX); +} + +int main(int argc, char *argv) +{ + struct data d = { 0, }; + struct pw_properties *props = NULL; + int res = 0, c; + static const struct option long_options = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "name", required_argument, NULL, 'n' }, + { "prefix", required_argument, NULL, 'p' }, + { "no-newline", no_argument, NULL, 'L' }, + { "recurse", no_argument, NULL, 'r' }, + { "no-colors", no_argument, NULL, 'N' }, + { "color", optional_argument, NULL, 'C' }, + { NULL, 0, NULL, 0} + }; + + d.opt_name = DEFAULT_NAME; + d.opt_prefix = NULL; + d.opt_recurse = false; + d.opt_newline = true; + if (isatty(fileno(stdout)) && getenv("NO_COLOR") == NULL) + d.opt_colors = true; + d.opt_cmd = "paths"; + + pw_init(&argc, &argv); + + while ((c = getopt_long(argc, argv, "hVn:p:LrNC", long_options, NULL)) != -1) { + switch (c) { + case 'h': + show_help(argv0, false); + return 0; + case 'V': + printf("%s\n" + "Compiled with libpipewire %s\n" + "Linked with libpipewire %s\n", + argv0, + pw_get_headers_version(), + pw_get_library_version()); + return 0; + case 'n': + d.opt_name = optarg; + break; + case 'p': + d.opt_prefix = optarg; + break; + case 'L': + d.opt_newline = false; + break; + case 'r': + d.opt_recurse = true; + break; + case 'N' : + d.opt_colors = false; + break; + case 'C' : + if (optarg == NULL || !strcmp(optarg, "auto")) + break; /* nothing to do, tty detection was done + before parsing options */ + else if (!strcmp(optarg, "never")) + d.opt_colors = false; + else if (!strcmp(optarg, "always")) + d.opt_colors = true; + else { + fprintf(stderr, "Unknown color: %s\n", optarg); + show_help(argv0, true); + return -1; + } + break; + default: + show_help(argv0, true); + return -1; + } + } + + if (optind < argc) + d.opt_cmd = argvoptind++; + + props = pw_properties_new( + PW_KEY_CONFIG_NAME, d.opt_name, + PW_KEY_CONFIG_PREFIX, d.opt_prefix, + NULL); + + d.conf = pw_properties_new(NULL, NULL); + if ((res = pw_conf_load_conf_for_context (props, d.conf)) < 0) { + fprintf(stderr, "error loading config: %s\n", spa_strerror(res)); + goto done; + } + + d.assemble = pw_properties_new(NULL, NULL); + + if (spa_streq(d.opt_cmd, "paths")) { + list_paths(&d); + } + else if (spa_streq(d.opt_cmd, "list")) { + if (optind == argc) { + pw_properties_update(d.assemble, &d.conf->dict); + } else { + section_for_each(&d, argvoptind++, do_list_section); + } + } + else if (spa_streq(d.opt_cmd, "merge")) { + if (optind == argc) { + fprintf(stderr, "%s requires a section\n", d.opt_cmd); + res = -EINVAL; + goto done; + } + section_for_each(&d, argvoptind++, do_merge_section); + } + print_all_properties(&d, d.assemble); + +done: + pw_properties_free(d.conf); + pw_properties_free(d.assemble); + pw_properties_free(props); + + pw_deinit(); + return res; +}
View file
pipewire-0.3.69.tar.gz/src/tools/pw-metadata.c -> pipewire-0.3.70.tar.gz/src/tools/pw-metadata.c
Changed
@@ -21,6 +21,7 @@ const char *opt_remote; const char *opt_name; + bool opt_list; bool opt_monitor; bool opt_delete; uint32_t opt_id; @@ -48,6 +49,9 @@ { struct data *d = data; + if (d->opt_list) + return 0; + if ((d->opt_id == SPA_ID_INVALID || d->opt_id == id) && (d->opt_key == NULL || spa_streq(d->opt_key, key))) { if (key == NULL) { @@ -72,22 +76,30 @@ const struct spa_dict *props) { struct data *d = data; - const char *str; + const char *name; if (!spa_streq(type, PW_TYPE_INTERFACE_Metadata)) return; - if (props != NULL && - (str = spa_dict_lookup(props, PW_KEY_METADATA_NAME)) != NULL && - !spa_streq(str, d->opt_name)) + if (props == NULL) + return; + + name = spa_dict_lookup(props, PW_KEY_METADATA_NAME); + if (name == NULL) + return; + + if (d->opt_name && !spa_streq(name, d->opt_name)) return; - if (d->metadata != NULL) { + if (!d->opt_list && d->metadata != NULL) { pw_log_warn("Multiple metadata: ignoring metadata %d", id); return; } - printf("Found \"%s\" metadata %d\n", d->opt_name, id); + printf("Found \"%s\" metadata %d\n", name, id); + if (d->opt_list) + return; + d->metadata = pw_registry_bind(d->registry, id, type, PW_VERSION_METADATA, 0); @@ -157,6 +169,7 @@ " -h, --help Show this help\n" " --version Show version\n" " -r, --remote Remote daemon name\n" + " -l, --list List available metadata\n" " -m, --monitor Monitor metadata\n" " -d, --delete Delete metadata\n" " -n, --name Metadata name (default: \"%s\")\n", @@ -171,6 +184,7 @@ { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { "remote", required_argument, NULL, 'r' }, + { "list", no_argument, NULL, 'l' }, { "monitor", no_argument, NULL, 'm' }, { "delete", no_argument, NULL, 'd' }, { "name", required_argument, NULL, 'n' }, @@ -184,7 +198,7 @@ data.opt_name = "default"; - while ((c = getopt_long(argc, argv, "hVr:mdn:", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "hVr:lmdn:", long_options, NULL)) != -1) { switch (c) { case 'h': show_help(&data, argv0, false); @@ -200,6 +214,10 @@ case 'r': data.opt_remote = optarg; break; + case 'l': + data.opt_name = NULL; + data.opt_list = true; + break; case 'm': data.opt_monitor = true; break;
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
.