Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
We truncated the diff of some files because they were too big. If you want to see the full diff for every file,
click here
.
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
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;
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);
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);
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 {
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
.