Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 6
View file
pipewire-aptx.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Mon Apr 4 15:18:29 UTC 2022 - Bjørn Lie <zaitor@opensuse.org> + +- Update to version 0.3.49 + +------------------------------------------------------------------- Thu Mar 3 11:50:58 UTC 2022 - Bjørn Lie <zaitor@opensuse.org> - Update to version 0.3.48
View file
pipewire-aptx.spec
Changed
@@ -7,7 +7,7 @@ %define soversion 0_2 Name: pipewire-aptx -Version: 0.3.48 +Version: 0.3.49 Release: 0 Summary: PipeWire Bluetooth aptX codec plugin License: MIT
View file
pipewire-0.3.48.tar.gz/.gitignore -> pipewire-0.3.49.tar.gz/.gitignore
Changed
@@ -1,3 +1,4 @@ +.* .tarball-version .version .*.swp @@ -6,9 +7,6 @@ *.tar.gz *.tar.xz *.o -build/ -builddir/ -config.h.meson cscope.out cscope.in.out cscope.po.out
View file
pipewire-0.3.48.tar.gz/.gitlab-ci.yml -> pipewire-0.3.49.tar.gz/.gitlab-ci.yml
Changed
@@ -25,7 +25,7 @@ .fedora: variables: # Update this tag when you want to trigger a rebuild - FDO_DISTRIBUTION_TAG: '2022-02-16.0' + FDO_DISTRIBUTION_TAG: '2022-03-05.0' FDO_DISTRIBUTION_VERSION: '35' FDO_DISTRIBUTION_PACKAGES: >- alsa-lib-devel @@ -66,6 +66,8 @@ ninja-build pkgconf python3-pip + pulseaudio-utils + openal-soft FDO_DISTRIBUTION_EXEC: >- pip3 install meson
View file
pipewire-0.3.48.tar.gz/Makefile.in -> pipewire-0.3.49.tar.gz/Makefile.in
Changed
@@ -17,6 +17,7 @@ run: all SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \ + SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \ PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules \ PATH=$(BUILD_ROOT)/src/examples:$(PATH) \ PIPEWIRE_CONFIG_DIR=$(BUILD_ROOT)/src/daemon \ @@ -26,6 +27,7 @@ run-pulse: all SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \ + SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \ PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules \ PIPEWIRE_CONFIG_DIR=$(BUILD_ROOT)/src/daemon \ ACP_PATHS_DIR=$(SOURCE_ROOT)/spa/plugins/alsa/mixer/paths \ @@ -46,11 +48,13 @@ monitor: all SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \ + SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \ PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules/ \ $(BUILD_ROOT)/src/tools/pw-mon cli: all SPA_PLUGIN_DIR=$(BUILD_ROOT)/spa/plugins \ + SPA_DATA_DIR=$(SOURCE_ROOT)/spa/plugins \ PIPEWIRE_MODULE_DIR=$(BUILD_ROOT)/src/modules/ \ $(BUILD_ROOT)/src/tools/pw-cli
View file
pipewire-0.3.48.tar.gz/NEWS -> pipewire-0.3.49.tar.gz/NEWS
Changed
@@ -1,3 +1,77 @@ +# PipeWire 0.3.49 (2022-03-31) + +This is a bugfix release that is API and ABI compatible with previous +0.3.x releases. + +## Highlights + - Sample rate switching should work again. + - pw-dot can now use the output of pw-dump to render a graph. + - Bluetooth A2DP streaming was improved that would reduce stuttering on + some devices. + - A JACK bug was fixed that would sometimes make it impossible to add more + tracks in Ardour. (#1714) + - Many bugfixes and improvements. + +## PipeWire + - Fix a potential crash when NULL params were configured. + - Add some simple functional tests to avoid some recent regressions. Improve + the test framework for this as well. + - Improvements to the poll loop to avoid some use-after-free scenarios. + - Fix samplerate switching again. + - setlocale is not called anymore from the pipewire library. This should be + called by the application. (#2223) + - pw_init() and pw_deinit() can now be nested and called multiple times. + - pw_stream will now report the resampler delay in the pw_time.queued field. + +## modules + - module-filter-chain now supports arbitrary many properties and will use + property hints to assign them the right type. + - The ROC modules now accept a sink/source_properties parameter. + - The module-rt can now also be built without RT-Kit support. + - module-echo-cancel can now use a fraction to specify the delay for more + precise control. + +## SPA + - The channelmixer will now do upmixing by default and will not use + normalization. It will also use a simple upmixing algorithm that duplicates + channels by default. A more interesting upmix method is also available (PSD) + but needs to be enabled manually. (#861) + - Add SSE optimized (de)interleave functions for 32 bits samples with and + without byteswap. + - JSON parsing of empty strings will now give an invalid number instead of + 0. + - JSON numbers are now parsed and serialized in a locale independent way so + that , and . are not mixed up. + - The resampler will now report the resample delay and queued samples as the + extra delay. + +## tools + - pw-cat will read more dsf files correctly and will not crash at the end. + - pw-top now has a man page. + - pw-dot can now use the output of pw-dump to render a graph. + +## bluetooth + - Improve interactions with oFono. + - Fix recovery with slow connections. + - Improve frame size of AptX-ll. + - A2DP can now use any quantum and will flush packets in smaller chunks + when needed to adapt. This improves stuttering in some cases. + +## pulse-server + - The server configuration can now be placed in pulse.properties section, + which also makes it possible to have custom overrides. + - Implement FIX_ flags for capture as well. + - Small fixes and improvements in module loading. + +## JACK + - Clear the last error before executing a new action or else we could end up + with error from a previous action. This causes some problems in Ardour where + adding a track would fail after some time. (#1714) + + +Older versions: + + # PipeWire 0.3.48 (2022-03-03) This is a bugfix release that is API and ABI compatible with previous @@ -73,9 +147,6 @@ - Don't try to connect HSP/HFP when no backend is available. -Older versions: - - # PipeWire 0.3.47 (2022-02-18) This is a bugfix release that is API and ABI compatible with previous
View file
pipewire-0.3.48.tar.gz/README.md -> pipewire-0.3.49.tar.gz/README.md
Changed
@@ -189,7 +189,7 @@ ## Documentation -Find tutorials and design documentation [here](doc/index.md). +Find tutorials and design documentation [here](doc/index.dox). The (incomplete) autogenerated API docs are [here](https://docs.pipewire.org).
View file
pipewire-0.3.48.tar.gz/doc/dma-buf.dox -> pipewire-0.3.49.tar.gz/doc/dma-buf.dox
Changed
@@ -138,6 +138,15 @@ using a proper graphics API (such as EGL, Vulkan or VA-API) to process the DMA-BUFs. +# Size of DMA-BUFs + +When importing a DMA-BUF with a proper graphics API the size of a single buffer plane +is no relevant property since it will be derived by the driver from the other properties. +Therefore consumers should ignore the field `maxsize` of a `spa_data` and the field +`size` of a `spa_chunk` struct. Producers are allowed to set both to 0. +In cases where mapping a single plane is required the size should be obtained locally +via the filedescriptor. + # v4l2 Another use case for streaming via DMA-BUFs are exporting a camera feed from v4l2
View file
pipewire-0.3.48.tar.gz/man/meson.build -> pipewire-0.3.49.tar.gz/man/meson.build
Changed
@@ -17,6 +17,7 @@ 'pw-mididump.1.rst.in', 'pw-mon.1.rst.in', 'pw-profiler.1.rst.in', + 'pw-top.1.rst.in', ] if get_option('pipewire-jack').allowed()
View file
pipewire-0.3.49.tar.gz/man/pw-top.1.rst.in
Added
@@ -0,0 +1,82 @@ +pw-top +###### + +--------------------------- +The PipeWire process viewer +--------------------------- + +:Manual section: 1 +:Manual group: General Commands Manual + +SYNOPSIS +======== + +| **pw-top** [*options*] + +DESCRIPTION +=========== + +The *pw-top* program provides a dynamic real-time view of the pipewire +node and device statistics. + +The columns presented are as follows: + +S + Measurement status. + ! representing inactive - no connections + + Blank representing active + +ID + The ID of the pipewire node/device, as found in *pw-dump* + +QUANT + Current quantum at which the device/node is polled. + See https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained + +RATE + Sample rate used for communicating with this device/node. + +WAIT + +BUSY + +W/Q + Ratio of WAIT / QUANT. + +B/Q + Ratio of BUSY / QUANT + +ERR + Total of Xruns and Errors + +NAME + Name assigned to the device/node, as found in *pw-dump* node.name + + Names are prefixed by *+* when they are linked to a driver (entry above with no +) + + +OPTIONS +======= + +-h | --help + Show help. + +-r | --remote=NAME + The name the *remote* instance to monitor. If left unspecified, + a connection is made to the default PipeWire instance. + +--version + Show version information. + + +AUTHORS +======= + +The PipeWire Developers <@PACKAGE_BUGREPORT@>; PipeWire is available from @PACKAGE_URL@ + +SEE ALSO +======== + +``pipewire(1)``, +
View file
pipewire-0.3.48.tar.gz/meson.build -> pipewire-0.3.49.tar.gz/meson.build
Changed
@@ -1,5 +1,5 @@ project('pipewire', ['c' ], - version : '0.3.48', + version : '0.3.49', license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ], meson_version : '>= 0.59.0', default_options : [ 'warning_level=3',
View file
pipewire-0.3.48.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.49.tar.gz/pipewire-jack/src/pipewire-jack.c
Changed
@@ -298,7 +298,6 @@ int pending_sync; int last_sync; int last_res; - bool error; struct spa_node_info info; @@ -845,7 +844,6 @@ id, seq, res, spa_strerror(res), message); if (id == PW_ID_CORE) { - client->error = true; client->last_res = res; if (!client->destroyed) do_callback(client, shutdown_callback, client->shutdown_arg); @@ -867,9 +865,8 @@ pw_log_warn("sync requested from callback"); return 0; } - if (client->error) - return client->last_res; + client->last_res = 0; client->pending_sync = pw_proxy_sync((struct pw_proxy*)client->core, client->pending_sync); while (true) { @@ -881,7 +878,7 @@ if (in_data_thread && client->rt_locked) pthread_mutex_lock(&client->rt_lock); - if (client->error) + if (client->last_res < 0) return client->last_res; if (client->pending_sync == client->last_sync) @@ -3314,7 +3311,7 @@ while (true) { pw_thread_loop_wait(client->context.loop); - if (client->error) + if (client->last_res < 0) goto init_failed; if (client->has_transport)
View file
pipewire-0.3.48.tar.gz/po/pl.po -> pipewire-0.3.49.tar.gz/po/pl.po
Changed
@@ -1,15 +1,15 @@ # Polish translation for pipewire. -# Copyright © 2008-2021 the pipewire authors. +# Copyright © 2008-2022 the pipewire authors. # This file is distributed under the same license as the pipewire package. -# Piotr Drąg <piotrdrag@gmail.com>, 2008, 2012-2021. +# Piotr Drąg <piotrdrag@gmail.com>, 2008, 2012-2022. # msgid "" msgstr "" "Project-Id-Version: pipewire\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" "issues\n" -"POT-Creation-Date: 2021-09-21 15:31+0000\n" -"PO-Revision-Date: 2021-10-09 15:35+0200\n" +"POT-Creation-Date: 2022-02-13 15:33+0000\n" +"PO-Revision-Date: 2022-03-13 12:05+0100\n" "Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n" "Language-Team: Polish <community-poland@mozilla.org>\n" "Language: pl\n" @@ -41,43 +41,36 @@ msgid "Start the PipeWire Media System" msgstr "Uruchomienie systemu multimediów PipeWire" -#: src/examples/media-session/alsa-monitor.c:656 -#: spa/plugins/alsa/acp/compat.c:189 -msgid "Built-in Audio" -msgstr "Wbudowany dźwięk" - -#: src/examples/media-session/alsa-monitor.c:660 -#: spa/plugins/alsa/acp/compat.c:194 -msgid "Modem" -msgstr "Modem" - -#: src/examples/media-session/alsa-monitor.c:669 -#: src/modules/module-zeroconf-discover.c:296 -msgid "Unknown device" -msgstr "Nieznane urządzenie" - -#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:173 -#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:173 +#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:188 +#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:188 #, c-format msgid "Tunnel to %s/%s" msgstr "Tunel do %s/%s" -#: src/modules/module-pulse-tunnel.c:534 +#: src/modules/module-fallback-sink.c:51 +msgid "Dummy Output" +msgstr "Głuche wyjście" + +#: src/modules/module-pulse-tunnel.c:536 #, c-format msgid "Tunnel for %s@%s" msgstr "Tunel dla %s@%s" -#: src/modules/module-zeroconf-discover.c:308 +#: src/modules/module-zeroconf-discover.c:332 +msgid "Unknown device" +msgstr "Nieznane urządzenie" + +#: src/modules/module-zeroconf-discover.c:344 #, c-format msgid "%s on %s@%s" msgstr "%s na %s@%s" -#: src/modules/module-zeroconf-discover.c:312 +#: src/modules/module-zeroconf-discover.c:348 #, c-format msgid "%s on %s" msgstr "%s na %s" -#: src/tools/pw-cat.c:1055 +#: src/tools/pw-cat.c:1075 #, c-format msgid "" "%s [options] <file>\n" @@ -92,7 +85,7 @@ " -v, --verbose Wyświetla więcej komunikatów\n" "\n" -#: src/tools/pw-cat.c:1062 +#: src/tools/pw-cat.c:1082 #, c-format msgid "" " -R, --remote Remote daemon name\n" @@ -130,7 +123,7 @@ "docelowych dla --target\n" "\n" -#: src/tools/pw-cat.c:1080 +#: src/tools/pw-cat.c:1100 #, c-format msgid "" " --rate Sample rate (req. for rec) (default " @@ -166,7 +159,7 @@ "(domyślnie %d)\n" "\n" -#: src/tools/pw-cat.c:1097 +#: src/tools/pw-cat.c:1117 msgid "" " -p, --playback Playback mode\n" " -r, --record Recording mode\n" @@ -180,7 +173,7 @@ " -d, --dsd Tryb DSD\n" "\n" -#: src/tools/pw-cli.c:2954 +#: src/tools/pw-cli.c:3050 #, c-format msgid "" "%s [options] [command]\n" @@ -198,12 +191,12 @@ " -r, --remote Nazwa zdalnej usługi\n" "\n" -#: spa/plugins/alsa/acp/acp.c:310 +#: spa/plugins/alsa/acp/acp.c:321 msgid "Pro Audio" msgstr "Dźwięk w zastosowaniach profesjonalnych" -#: spa/plugins/alsa/acp/acp.c:433 spa/plugins/alsa/acp/alsa-mixer.c:4648 -#: spa/plugins/bluez5/bluez5-device.c:1135 +#: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648 +#: spa/plugins/bluez5/bluez5-device.c:1159 msgid "Off" msgstr "Wyłączone" @@ -230,7 +223,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:2657 #: spa/plugins/alsa/acp/alsa-mixer.c:2741 -#: spa/plugins/bluez5/bluez5-device.c:1292 +#: spa/plugins/bluez5/bluez5-device.c:1328 msgid "Microphone" msgstr "Mikrofon" @@ -296,7 +289,7 @@ msgstr "Brak podbicia basów" #: spa/plugins/alsa/acp/alsa-mixer.c:2672 -#: spa/plugins/bluez5/bluez5-device.c:1297 +#: spa/plugins/bluez5/bluez5-device.c:1333 msgid "Speaker" msgstr "Głośnik" @@ -411,7 +404,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:4484 #: spa/plugins/alsa/acp/alsa-mixer.c:4642 -#: spa/plugins/bluez5/bluez5-device.c:1282 +#: spa/plugins/bluez5/bluez5-device.c:1318 msgid "Headset" msgstr "Słuchawki z mikrofonem" @@ -623,65 +616,73 @@ "Prawdopodobnie jest to błąd sterownika ALSA „%s”. Proszę zgłosić ten problem " "programistom usługi ALSA." -#: spa/plugins/alsa/acp/channelmap.h:466 +#: spa/plugins/alsa/acp/channelmap.h:464 msgid "(invalid)" msgstr "(nieprawidłowe)" -#: spa/plugins/bluez5/bluez5-device.c:1145 +#: spa/plugins/alsa/acp/compat.c:189 +msgid "Built-in Audio" +msgstr "Wbudowany dźwięk" + +#: spa/plugins/alsa/acp/compat.c:194 +msgid "Modem" +msgstr "Modem" + +#: spa/plugins/bluez5/bluez5-device.c:1170 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgstr "Bramka dźwięku (źródło A2DP i AG HSP/HFP)" -#: spa/plugins/bluez5/bluez5-device.c:1168 +#: spa/plugins/bluez5/bluez5-device.c:1195 #, c-format msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgstr "Odtwarzanie o wysokiej dokładności (odpływ A2DP, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1171 +#: spa/plugins/bluez5/bluez5-device.c:1198 #, c-format msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgstr "Dupleks o wysokiej dokładności (źródło/odpływ A2DP, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1178 +#: spa/plugins/bluez5/bluez5-device.c:1206 msgid "High Fidelity Playback (A2DP Sink)" msgstr "Odtwarzanie o wysokiej dokładności (odpływ A2DP)" -#: spa/plugins/bluez5/bluez5-device.c:1180 +#: spa/plugins/bluez5/bluez5-device.c:1208 msgid "High Fidelity Duplex (A2DP Source/Sink)" msgstr "Dupleks o wysokiej dokładności (źródło/odpływ A2DP)" -#: spa/plugins/bluez5/bluez5-device.c:1207 +#: spa/plugins/bluez5/bluez5-device.c:1236 #, c-format msgid "Headset Head Unit (HSP/HFP, codec %s)" msgstr "Jednostka główna słuchawek z mikrofonem (HSP/HFP, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1211 +#: spa/plugins/bluez5/bluez5-device.c:1241 msgid "Headset Head Unit (HSP/HFP)" msgstr "Jednostka główna słuchawek z mikrofonem (HSP/HFP)" -#: spa/plugins/bluez5/bluez5-device.c:1287 +#: spa/plugins/bluez5/bluez5-device.c:1323 msgid "Handsfree" msgstr "Zestaw głośnomówiący" -#: spa/plugins/bluez5/bluez5-device.c:1302 +#: spa/plugins/bluez5/bluez5-device.c:1338 msgid "Headphone" msgstr "Słuchawki" -#: spa/plugins/bluez5/bluez5-device.c:1307 +#: spa/plugins/bluez5/bluez5-device.c:1343 msgid "Portable" msgstr "Przenośne" -#: spa/plugins/bluez5/bluez5-device.c:1312 +#: spa/plugins/bluez5/bluez5-device.c:1348 msgid "Car" msgstr "Samochód" -#: spa/plugins/bluez5/bluez5-device.c:1317 +#: spa/plugins/bluez5/bluez5-device.c:1353 msgid "HiFi" msgstr "HiFi" -#: spa/plugins/bluez5/bluez5-device.c:1322 +#: spa/plugins/bluez5/bluez5-device.c:1358 msgid "Phone" msgstr "Telefon" -#: spa/plugins/bluez5/bluez5-device.c:1328 +#: spa/plugins/bluez5/bluez5-device.c:1364 msgid "Bluetooth" msgstr "Bluetooth"
View file
pipewire-0.3.48.tar.gz/spa/include/spa/debug/format.h -> pipewire-0.3.49.tar.gz/spa/include/spa/debug/format.h
Changed
@@ -69,7 +69,7 @@ spa_debugn("%f", *(float *) body); break; case SPA_TYPE_Double: - spa_debugn("%g", *(double *) body); + spa_debugn("%f", *(double *) body); break; case SPA_TYPE_String: spa_debugn("%s", (char *) body);
View file
pipewire-0.3.48.tar.gz/spa/include/spa/utils/hook.h -> pipewire-0.3.49.tar.gz/spa/include/spa/utils/hook.h
Changed
@@ -261,8 +261,10 @@ * The below (pseudo)code is a minimal example outlining the use of hooks: * \code{.c} * // the public interface + * #define VERSION_BAR_EVENTS 0 // version of the vtable * struct bar_events { - * uint32_t version; + * uint32_t version; // NOTE: an integral member named `version` + * // must be present in the vtable * void (*boom)(void *data, const char *msg); * }; * @@ -272,17 +274,23 @@ * }; * * void party_add_event_listener(struct party *p, struct spa_hook *listener, - * struct bar_events *events, void *data) + * const struct bar_events *events, void *data) * { * spa_hook_list_append(&p->bar_list, listener, events, data); * } * * static void party_on(struct party *p) * { - * spa_hook_list_call(&p->list, struct bar_events, - * boom, // function name - * 0 // hardcoded version, - * "party on, wayne"); + * // NOTE: this is a macro, it evaluates to an integer, + * // which is the number of hooks called + * spa_hook_list_call(&p->list, + * struct bar_events, // vtable type + * boom, // function name + * 0, // hardcoded version, + * // usually the version in which `boom` + * // has been added to the vtable + * "party on, wayne" // function argument(s) + * ); * } * \endcode * @@ -293,7 +301,8 @@ * printf("%s", msg); * } * - * static const struct bar_events { + * static const struct bar_events events = { + * .version = VERSION_BAR_EVENTS, // version of the implemented interface * .boom = boom_cb, * }; * @@ -302,7 +311,7 @@ * struct spa_hook hook; * struct party *p = start_the_party(); * - * party_add_event_listener(p, &hook, boom_cb, userdata); + * party_add_event_listener(p, &hook, &events, userdata); * * mainloop(); * return 0;
View file
pipewire-0.3.48.tar.gz/spa/include/spa/utils/json.h -> pipewire-0.3.49.tar.gz/spa/include/spa/utils/json.h
Changed
@@ -34,8 +34,11 @@ #include <stdlib.h> #include <stdint.h> #include <string.h> +#include <math.h> +#include <float.h> #include <spa/utils/defs.h> +#include <spa/utils/string.h> /** \defgroup spa_json JSON * Relaxed JSON variant parsing @@ -237,9 +240,10 @@ static inline int spa_json_parse_float(const char *val, int len, float *result) { char *end; - *result = strtof(val, &end); - return end == val + len; + *result = spa_strtof(val, &end); + return len > 0 && end == val + len; } + static inline bool spa_json_is_float(const char *val, int len) { float dummy; @@ -254,12 +258,25 @@ return spa_json_parse_float(value, len, res); } +static inline char *spa_json_format_float(char *str, int size, float val) +{ + if (SPA_UNLIKELY(!isnormal(val))) { + if (val == INFINITY) + val = FLT_MAX; + else if (val == -INFINITY) + val = FLT_MIN; + else + val = 0.0f; + } + return spa_dtoa(str, size, val); +} + /* int */ static inline int spa_json_parse_int(const char *val, int len, int *result) { char *end; *result = strtol(val, &end, 0); - return end == val + len; + return len > 0 && end == val + len; } static inline bool spa_json_is_int(const char *val, int len) {
View file
pipewire-0.3.48.tar.gz/spa/include/spa/utils/string.h -> pipewire-0.3.49.tar.gz/spa/include/spa/utils/string.h
Changed
@@ -32,6 +32,8 @@ #include <stdarg.h> #include <stdbool.h> #include <errno.h> +#include <stdlib.h> +#include <locale.h> #include <spa/utils/defs.h> @@ -264,6 +266,27 @@ } /** + * Convert \a str to a float in the C locale. + * + * If \a endptr is not NULL, a pointer to the character after the last character + * used in the conversion is stored in the location referenced by endptr. + * + * \return the result float. + */ +static inline float spa_strtof(const char *str, char **endptr) +{ + static locale_t locale = NULL; + float v; + if (SPA_UNLIKELY(locale == NULL)) + locale = newlocale(LC_ALL_MASK, "C", NULL); + if (locale != NULL) + v = strtof_l(str, endptr, locale); + else + v = strtof(str, endptr); + return v; +} + +/** * Convert \a str to a float and store the result in \a val. * * On failure, the value of \a val is unmodified. @@ -277,9 +300,8 @@ if (!str || *str =='\0') return false; - errno = 0; - v = strtof(str, &endptr); + v = spa_strtof(str, &endptr); if (errno != 0 || *endptr != '\0') return false; @@ -288,6 +310,27 @@ } /** + * Convert \a str to a double in the C locale. + * + * If \a endptr is not NULL, a pointer to the character after the last character + * used in the conversion is stored in the location referenced by endptr. + * + * \return the result float. + */ +static inline double spa_strtod(const char *str, char **endptr) +{ + static locale_t locale = NULL; + double v; + if (SPA_UNLIKELY(locale == NULL)) + locale = newlocale(LC_ALL_MASK, "C", NULL); + if (locale != NULL) + v = strtod_l(str, endptr, locale); + else + v = strtod(str, endptr); + return v; +} + +/** * Convert \a str to a double and store the result in \a val. * * On failure, the value of \a val is unmodified. @@ -303,7 +346,7 @@ return false; errno = 0; - v = strtod(str, &endptr); + v = spa_strtod(str, &endptr); if (errno != 0 || *endptr != '\0') return false; @@ -311,6 +354,16 @@ return true; } +static inline char *spa_dtoa(char *str, size_t size, double val) +{ + int i, l; + l = spa_scnprintf(str, size, "%f", val); + for (i = 0; i < l; i++) + if (str[i] == ',') + str[i] = '.'; + return str; +} + /** * \} */
View file
pipewire-0.3.48.tar.gz/spa/plugins/aec/aec-null.c -> pipewire-0.3.49.tar.gz/spa/plugins/aec/aec-null.c
Changed
@@ -151,7 +151,7 @@ return 1; } -const struct spa_handle_factory spa_aec_exaudio_factory = { +const struct spa_handle_factory spa_aec_null_factory = { SPA_VERSION_HANDLE_FACTORY, SPA_NAME_AEC, NULL, @@ -169,7 +169,7 @@ switch (*index) { case 0: - *factory = &spa_aec_exaudio_factory; + *factory = &spa_aec_null_factory; break; default: return 0;
View file
pipewire-0.3.48.tar.gz/spa/plugins/aec/aec-webrtc.cpp -> pipewire-0.3.49.tar.gz/spa/plugins/aec/aec-webrtc.cpp
Changed
@@ -250,7 +250,7 @@ return 1; } -const struct spa_handle_factory spa_aec_exaudio_factory = { +const struct spa_handle_factory spa_aec_webrtc_factory = { SPA_VERSION_HANDLE_FACTORY, SPA_NAME_AEC, NULL, @@ -268,7 +268,7 @@ switch (*index) { case 0: - *factory = &spa_aec_exaudio_factory; + *factory = &spa_aec_webrtc_factory; break; default: return 0;
View file
pipewire-0.3.48.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c -> pipewire-0.3.49.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c
Changed
@@ -160,7 +160,7 @@ SPA_TYPE_OBJECT_PropInfo, id, SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_latencyOffsetNsec), SPA_PROP_INFO_description, SPA_POD_String("Latency offset (ns)"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, 0LL, INT64_MAX)); + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, 0LL, 2 * SPA_NSEC_PER_SEC)); break; case 4: if (!this->is_iec958 && !this->is_hdmi) @@ -310,20 +310,18 @@ case SPA_PARAM_Props: { struct props *p = &this->props; - struct spa_process_latency_info info; struct spa_pod *iec958_codecs = NULL, *params = NULL; + int64_t lat_ns = -1; if (param == NULL) { reset_props(p); return 0; } - info = this->process_latency; - spa_pod_parse_object(param, SPA_TYPE_OBJECT_Props, NULL, SPA_PROP_device, SPA_POD_OPT_Stringn(p->device, sizeof(p->device)), - SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&info.ns), + SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&lat_ns), SPA_PROP_iec958Codecs, SPA_POD_OPT_Pod(&iec958_codecs), SPA_PROP_params, SPA_POD_OPT_Pod(¶ms)); @@ -342,8 +340,12 @@ this->port_params[PORT_EnumFormat].user++; } spa_alsa_parse_prop_params(this, params); - handle_process_latency(this, &info); - + if (lat_ns != -1) { + struct spa_process_latency_info info; + info = this->process_latency; + info.ns = lat_ns; + handle_process_latency(this, &info); + } emit_node_info(this, false); emit_port_info(this, false); break;
View file
pipewire-0.3.48.tar.gz/spa/plugins/alsa/alsa-pcm-source.c -> pipewire-0.3.49.tar.gz/spa/plugins/alsa/alsa-pcm-source.c
Changed
@@ -160,7 +160,7 @@ SPA_TYPE_OBJECT_PropInfo, id, SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_latencyOffsetNsec), SPA_PROP_INFO_description, SPA_POD_String("Latency offset (ns)"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, 0LL, INT64_MAX)); + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, 0LL, 2 * SPA_NSEC_PER_SEC)); break; default: param = spa_alsa_enum_propinfo(this, result.index - 4, &b); @@ -290,24 +290,27 @@ case SPA_PARAM_Props: { struct props *p = &this->props; - struct spa_process_latency_info info; struct spa_pod *params = NULL; + int64_t lat_ns = -1; if (param == NULL) { reset_props(p); return 0; } - info = this->process_latency; - spa_pod_parse_object(param, SPA_TYPE_OBJECT_Props, NULL, SPA_PROP_device, SPA_POD_OPT_Stringn(p->device, sizeof(p->device)), - SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&info.ns), + SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&lat_ns), SPA_PROP_params, SPA_POD_OPT_Pod(¶ms)); spa_alsa_parse_prop_params(this, params); - handle_process_latency(this, &info); + if (lat_ns != -1) { + struct spa_process_latency_info info; + info = this->process_latency; + info.ns = lat_ns; + handle_process_latency(this, &info); + } emit_node_info(this, false); emit_port_info(this, false);
View file
pipewire-0.3.48.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.49.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -237,7 +237,7 @@ SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("api.alsa.period-size"), SPA_PROP_INFO_description, SPA_POD_String("Period Size"), - SPA_PROP_INFO_type, SPA_POD_Int(state->default_period_size), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->default_period_size, 0, 8192), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 6: @@ -245,7 +245,7 @@ SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("api.alsa.period-num"), SPA_PROP_INFO_description, SPA_POD_String("Number of Periods"), - SPA_PROP_INFO_type, SPA_POD_Int(state->default_period_num), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->default_period_num, 0, 1024), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 7: @@ -253,7 +253,7 @@ SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("api.alsa.headroom"), SPA_PROP_INFO_description, SPA_POD_String("Headroom"), - SPA_PROP_INFO_type, SPA_POD_Int(state->default_headroom), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->default_headroom, 0, 8192), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 8: @@ -261,7 +261,7 @@ SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("api.alsa.start-delay"), SPA_PROP_INFO_description, SPA_POD_String("Start Delay"), - SPA_PROP_INFO_type, SPA_POD_Int(state->default_start_delay), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->default_start_delay, 0, 8192), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 9: @@ -269,7 +269,7 @@ SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("api.alsa.disable-mmap"), SPA_PROP_INFO_description, SPA_POD_String("Disable MMAP"), - SPA_PROP_INFO_type, SPA_POD_Bool(state->disable_mmap), + SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->disable_mmap), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 10: @@ -277,7 +277,7 @@ SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("api.alsa.disable-batch"), SPA_PROP_INFO_description, SPA_POD_String("Disable Batch"), - SPA_PROP_INFO_type, SPA_POD_Bool(state->disable_batch), + SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->disable_batch), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 11: @@ -285,7 +285,7 @@ SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("api.alsa.use-chmap"), SPA_PROP_INFO_description, SPA_POD_String("Use the driver channelmap"), - SPA_PROP_INFO_type, SPA_POD_Bool(state->props.use_chmap), + SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->props.use_chmap), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 12: @@ -293,7 +293,7 @@ SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("api.alsa.multi-rate"), SPA_PROP_INFO_description, SPA_POD_String("Support multiple rates"), - SPA_PROP_INFO_type, SPA_POD_Bool(state->multi_rate), + SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->multi_rate), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 13: @@ -301,7 +301,8 @@ SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("latency.internal.rate"), SPA_PROP_INFO_description, SPA_POD_String("Internal latency in samples"), - SPA_PROP_INFO_type, SPA_POD_Int(state->process_latency.rate), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->process_latency.rate, + 0, 65536), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 14: @@ -309,7 +310,8 @@ SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("latency.internal.ns"), SPA_PROP_INFO_description, SPA_POD_String("Internal latency in nanoseconds"), - SPA_PROP_INFO_type, SPA_POD_Long(state->process_latency.ns), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(state->process_latency.ns, + 0, 2 * SPA_NSEC_PER_SEC), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 15: @@ -429,7 +431,7 @@ } else continue; - spa_log_debug(state->log, "key:'%s' val:'%s'", name, value); + spa_log_info(state->log, "key:'%s' val:'%s'", name, value); alsa_set_param(state, name, value); changed++; }
View file
pipewire-0.3.48.tar.gz/spa/plugins/alsa/test-timer.c -> pipewire-0.3.49.tar.gz/spa/plugins/alsa/test-timer.c
Changed
@@ -25,6 +25,7 @@ #include <stdio.h> #include <stdbool.h> #include <limits.h> +#include <getopt.h> #include <math.h> #include <sys/timerfd.h> @@ -42,6 +43,8 @@ #define BW_PERIOD (NSEC_PER_SEC * 3) struct state { + const char *device; + unsigned int format; unsigned int rate; unsigned int channels; snd_pcm_uframes_t period; @@ -77,27 +80,39 @@ } \ } +#define LOOP(type,areas,scale) { \ + uint32_t i, j; \ + type *samples, v; \ + samples = (type*)((uint8_t*)areas[0].addr + (areas[0].first + offset*areas[0].step) / 8); \ + for (i = 0; i < frames; i++) { \ + state->accumulator += M_PI_M2 * 440 / state->rate; \ + if (state->accumulator >= M_PI_M2) \ + state->accumulator -= M_PI_M2; \ + v = sin(state->accumulator) * scale; \ + for (j = 0; j < state->channels; j++) \ + *samples++ = v; \ + } \ +} + static int write_period(struct state *state) { snd_pcm_uframes_t frames = state->period; snd_pcm_uframes_t offset; const snd_pcm_channel_area_t* areas; - uint32_t i, j; - int32_t *samples, v; snd_pcm_mmap_begin(state->hndl, &areas, &offset, &frames); - samples = (int32_t*)((uint8_t*)areas[0].addr + (areas[0].first + offset*areas[0].step) / 8); - - for (i = 0; i < frames; i++) { - state->accumulator += M_PI_M2 * 440 / state->rate; - if (state->accumulator >= M_PI_M2) - state->accumulator -= M_PI_M2; - v = sin(state->accumulator) * 0x7fffffff; - - for (j = 0; j < state->channels; j++) - *samples++ = v; + switch (state->format) { + case SND_PCM_FORMAT_S32_LE: + LOOP(int32_t, areas, 0x7fffffff); + break; + case SND_PCM_FORMAT_S16_LE: + LOOP(int16_t, areas, 0x7fff); + break; + default: + break; } + snd_pcm_mmap_commit(state->hndl, offset, frames) ; return 0; @@ -156,27 +171,89 @@ return 0; } +static unsigned int format_from_string(const char *str) +{ + if (strcmp(str, "S32_LE") == 0) + return SND_PCM_FORMAT_S32_LE; + else if (strcmp(str, "S32_BE") == 0) + return SND_PCM_FORMAT_S32_BE; + else if (strcmp(str, "S24_LE") == 0) + return SND_PCM_FORMAT_S24_LE; + else if (strcmp(str, "S24_BE") == 0) + return SND_PCM_FORMAT_S24_BE; + else if (strcmp(str, "S24_3LE") == 0) + return SND_PCM_FORMAT_S24_3LE; + else if (strcmp(str, "S24_3_BE") == 0) + return SND_PCM_FORMAT_S24_3BE; + else if (strcmp(str, "S16_LE") == 0) + return SND_PCM_FORMAT_S16_LE; + else if (strcmp(str, "S16_BE") == 0) + return SND_PCM_FORMAT_S16_BE; + return 0; +} + +static void show_help(const char *name, bool error) +{ + fprintf(error ? stderr : stdout, "%s [options]\n" + " -h, --help Show this help\n" + " -D, --device device name (default %s)\n", + name, DEFAULT_DEVICE); +} + int main(int argc, char *argv[]) { struct state state = { 0, }; - const char *device = DEFAULT_DEVICE; snd_pcm_hw_params_t *hparams; snd_pcm_sw_params_t *sparams; struct timespec now; - - CHECK(snd_pcm_open(&state.hndl, device, SND_PCM_STREAM_PLAYBACK, 0), "open %s failed", device); - + char c; + static const struct option long_options[] = { + { "help", no_argument, NULL, 'h' }, + { "device", required_argument, NULL, 'D' }, + { "format", required_argument, NULL, 'f' }, + { "rate", required_argument, NULL, 'r' }, + { "channels", required_argument, NULL, 'c' }, + { NULL, 0, NULL, 0} + }; + state.device = DEFAULT_DEVICE; + state.format = SND_PCM_FORMAT_S16_LE; state.rate = 44100; state.channels = 2; state.period = 1024; + while ((c = getopt_long(argc, argv, "hD:f:r:c:", long_options, NULL)) != -1) { + switch (c) { + case 'h': + show_help(argv[0], false); + return 0; + case 'D': + state.device = optarg; + break; + case 'f': + state.format = format_from_string(optarg); + break; + case 'r': + state.rate = atoi(optarg); + break; + case 'c': + state.channels = atoi(optarg); + break; + default: + show_help(argv[0], true); + return -1; + } + } + + CHECK(snd_pcm_open(&state.hndl, state.device, SND_PCM_STREAM_PLAYBACK, 0), + "open %s failed", state.device); + /* hw params */ snd_pcm_hw_params_alloca(&hparams); snd_pcm_hw_params_any(state.hndl, hparams); CHECK(snd_pcm_hw_params_set_access(state.hndl, hparams, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set interleaved"); CHECK(snd_pcm_hw_params_set_format(state.hndl, hparams, - SND_PCM_FORMAT_S32_LE), "set format"); + state.format), "set format"); CHECK(snd_pcm_hw_params_set_channels_near(state.hndl, hparams, &state.channels), "set channels"); CHECK(snd_pcm_hw_params_set_rate_near(state.hndl, hparams, @@ -186,7 +263,7 @@ CHECK(snd_pcm_hw_params_get_buffer_size(hparams, &state.buffer_frames), "get_buffer_size_max"); fprintf(stdout, "opened format:%s rate:%u channels:%u\n", - snd_pcm_format_name(SND_PCM_FORMAT_S32_LE), + snd_pcm_format_name(state.format), state.rate, state.channels); snd_pcm_sw_params_alloca(&sparams);
View file
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/audioadapter.c
Changed
@@ -542,7 +542,6 @@ res = spa_pod_parse_object(format, SPA_TYPE_OBJECT_Format, NULL, SPA_FORMAT_AUDIO_format, SPA_POD_OPT_Id(&info->format), - SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate), SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); if (position == NULL ||
View file
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/channelmix-ops-c.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/channelmix-ops-c.c
Changed
@@ -246,6 +246,16 @@ for (i = 0; i < n_dst; i++) memset(d[i], 0, n_samples * sizeof(float)); } + else if (mix->upmix == CHANNELMIX_UPMIX_SIMPLE) { + for (n = 0; n < n_samples; n++) { + float c = s[0][n] + s[1][n]; + d[0][n] = s[0][n] * v0; + d[1][n] = s[1][n] * v1; + d[2][n] = c * v2; + d[3][n] = c; + } + lr4_process(&mix->lr4[3], d[3], d[3], v3, n_samples); + } else if (v0 == 1.0f && v1 == 1.0f) { for (n = 0; n < n_samples; n++) { float c = s[0][n] + s[1][n]; @@ -289,6 +299,18 @@ for (i = 0; i < n_dst; i++) memset(d[i], 0, n_samples * sizeof(float)); } + else if (mix->upmix == CHANNELMIX_UPMIX_SIMPLE) { + for (n = 0; n < n_samples; n++) { + float c = s[0][n] + s[1][n]; + d[0][n] = s[0][n] * v0; + d[1][n] = s[1][n] * v1; + d[2][n] = c * v2; + d[3][n] = c; + d[4][n] = s[0][n] * v4; + d[5][n] = s[1][n] * v5; + } + lr4_process(&mix->lr4[3], d[3], d[3], v3, n_samples); + } else if (v0 == 1.0f && v1 == 1.0f) { for (n = 0; n < n_samples; n++) { float c = s[0][n] + s[1][n]; @@ -347,6 +369,20 @@ for (i = 0; i < n_dst; i++) memset(d[i], 0, n_samples * sizeof(float)); } + else if (mix->upmix == CHANNELMIX_UPMIX_SIMPLE) { + for (n = 0; n < n_samples; n++) { + float c = s[0][n] + s[1][n]; + d[0][n] = s[0][n] * v0; + d[1][n] = s[1][n] * v1; + d[2][n] = c * v2; + d[3][n] = c; + d[4][n] = s[0][n] * v4; + d[5][n] = s[1][n] * v5; + d[6][n] = s[0][n] * v6; + d[7][n] = s[1][n] * v7; + } + lr4_process(&mix->lr4[3], d[3], d[3], v3, n_samples); + } else if (v0 == 1.0f && v1 == 1.0f && v4 == 1.0f && v5 == 1.0f) { for (n = 0; n < n_samples; n++) { float c = s[0][n] + s[1][n];
View file
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/channelmix-ops.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/channelmix-ops.c
Changed
@@ -210,7 +210,8 @@ unassigned = src_mask & ~dst_mask; keep = dst_mask & ~src_mask; - if (!SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_UPMIX)) + if (!SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_UPMIX) || + mix->upmix == CHANNELMIX_UPMIX_NONE) keep = 0; keep |= FRONT;
View file
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/channelmix-ops.h -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/channelmix-ops.h
Changed
@@ -26,6 +26,7 @@ #include <stdio.h> #include <spa/utils/defs.h> +#include <spa/utils/string.h> #include <spa/param/audio/raw.h> #undef SPA_LOG_TOPIC_DEFAULT @@ -61,6 +62,10 @@ #define CHANNELMIX_OPTION_NORMALIZE (1<<1) /**< normalize volumes */ #define CHANNELMIX_OPTION_UPMIX (1<<2) /**< do simple upmixing */ uint32_t options; +#define CHANNELMIX_UPMIX_NONE (0) /**< disable upmixing */ +#define CHANNELMIX_UPMIX_SIMPLE (1) /**< simple upmixing */ +#define CHANNELMIX_UPMIX_PSD (2) /**< Passive Surround Decoding upmixing */ + uint32_t upmix; struct spa_log *log; @@ -97,6 +102,26 @@ int channelmix_init(struct channelmix *mix); +static const struct channelmix_upmix_info { + const char *label; + const char *description; + uint32_t upmix; +} channelmix_upmix_info[] = { + [CHANNELMIX_UPMIX_NONE] = { "none", "Disabled", CHANNELMIX_UPMIX_NONE }, + [CHANNELMIX_UPMIX_SIMPLE] = { "simple", "Simple upmixing", CHANNELMIX_UPMIX_SIMPLE }, + [CHANNELMIX_UPMIX_PSD] = { "psd", "Passive Surround Decoding", CHANNELMIX_UPMIX_PSD } +}; + +static inline uint32_t channelmix_upmix_from_label(const char *label) +{ + uint32_t i; + for (i = 0; i < SPA_N_ELEMENTS(channelmix_upmix_info); i++) { + if (spa_streq(channelmix_upmix_info[i].label, label)) + return channelmix_upmix_info[i].upmix; + } + return CHANNELMIX_UPMIX_NONE; +} + #define channelmix_process(mix,...) (mix)->process(mix, __VA_ARGS__) #define channelmix_set_volume(mix,...) (mix)->set_volume(mix, __VA_ARGS__) #define channelmix_free(mix) (mix)->free(mix)
View file
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/channelmix.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/channelmix.c
Changed
@@ -292,7 +292,8 @@ { struct volumes *vol; - if (this->mix.set_volume == NULL) + if (this->mix.set_volume == NULL || + this->props.disabled) return; if (this->props.have_soft_volume) @@ -545,6 +546,31 @@ this->mix.hilbert_taps, 0, MAX_TAPS), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; + case 17: + { + struct spa_pod_frame f[2]; + uint32_t i; + spa_pod_builder_push_object(&b, &f[0], + SPA_TYPE_OBJECT_PropInfo, id); + spa_pod_builder_add(&b, + SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix-method"), + SPA_PROP_INFO_description, SPA_POD_String("Upmix Method to use"), + SPA_PROP_INFO_type, SPA_POD_String( + channelmix_upmix_info[this->mix.upmix].label), + 0); + spa_pod_builder_prop(&b, SPA_PROP_INFO_labels, 0); + spa_pod_builder_push_struct(&b, &f[1]); + for (i = 0; i < SPA_N_ELEMENTS(channelmix_upmix_info); i++) { + spa_pod_builder_string(&b, channelmix_upmix_info[i].label); + spa_pod_builder_string(&b, channelmix_upmix_info[i].description); + } + spa_pod_builder_pop(&b, &f[1]); + spa_pod_builder_add(&b, + SPA_PROP_INFO_params, SPA_POD_Bool(true), + 0); + param = spa_pod_builder_pop(&b, &f[0]); + break; + } default: return 0; } @@ -604,6 +630,8 @@ spa_pod_builder_float(&b, this->mix.widen); spa_pod_builder_string(&b, "channelmix.hilbert-taps"); spa_pod_builder_int(&b, this->mix.hilbert_taps); + spa_pod_builder_string(&b, "channelmix.upmix-method"); + spa_pod_builder_string(&b, channelmix_upmix_info[this->mix.upmix].label); spa_pod_builder_pop(&b, &f[1]); param = spa_pod_builder_pop(&b, &f[0]); break; @@ -647,6 +675,8 @@ spa_atof(s, &this->mix.widen); else if (spa_streq(k, "channelmix.hilbert-taps")) spa_atou32(s, &this->mix.hilbert_taps, 0); + else if (spa_streq(k, "channelmix.upmix-method")) + this->mix.upmix = channelmix_upmix_from_label(s); else return 0; return 1; @@ -665,7 +695,7 @@ while (true) { const char *name; struct spa_pod *pod; - char value[512]; + char value[512], buf[128]; if (spa_pod_parser_get_string(&prs, &name) < 0) break; @@ -676,8 +706,9 @@ if (spa_pod_is_string(pod)) { spa_pod_copy_string(pod, sizeof(value), value); } else if (spa_pod_is_float(pod)) { - snprintf(value, sizeof(value), "%f", - SPA_POD_VALUE(struct spa_pod_float, pod)); + snprintf(value, sizeof(value), "%s", + spa_json_format_float(buf, sizeof(buf), + SPA_POD_VALUE(struct spa_pod_float, pod))); } else if (spa_pod_is_int(pod)) { snprintf(value, sizeof(value), "%d", SPA_POD_VALUE(struct spa_pod_int, pod)); @@ -691,7 +722,7 @@ spa_log_info(this->log, "key:'%s' val:'%s'", name, value); changed += channelmix_set_param(this, name, value); } - if (changed) + if (changed && !this->props.disabled) channelmix_init(&this->mix); return changed; } @@ -709,9 +740,6 @@ if (param == NULL) return 0; - if (this->props.disabled) - return 0; - SPA_POD_OBJECT_FOREACH(obj, prop) { switch (prop->key) { case SPA_PROP_volume: @@ -771,6 +799,7 @@ break; } } + if (changed) { struct port *port = GET_PORT(this, this->direction, 0); if (have_soft_volume) @@ -780,6 +809,7 @@ if (port->have_format) remap_volumes(this, &port->format); + set_volume(this); } return changed; @@ -794,9 +824,6 @@ if (size < 3) return -EINVAL; - if (this->props.disabled) - return 0; - if ((val[0] & 0xf0) != 0xb0 || val[1] != 7) return 0; @@ -1627,9 +1654,13 @@ props_reset(&this->props); - this->mix.options = CHANNELMIX_OPTION_NORMALIZE; + this->mix.options = CHANNELMIX_OPTION_UPMIX; + this->mix.upmix = CHANNELMIX_UPMIX_SIMPLE; this->mix.log = this->log; + this->mix.lfe_cutoff = 120.0f; + this->mix.fc_cutoff = 6000.0f; this->mix.rear_delay = 12.0f; + this->mix.widen = 0.1f; for (i = 0; info && i < info->n_items; i++) { const char *k = info->items[i].key;
View file
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/fmt-ops-sse2.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/fmt-ops-sse2.c
Changed
@@ -544,6 +544,354 @@ } static void +conv_interleave_32_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src[], + uint32_t n_channels, uint32_t n_samples) +{ + const int32_t *s0 = src[0]; + int32_t *d = dst; + uint32_t n, unrolled; + __m128i out[4]; + + if (SPA_IS_ALIGNED(s0, 16)) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + out[0] = _mm_load_si128((__m128i*)&s0[n]); + out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1)); + out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2)); + out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3)); + + d[0*n_channels] = _mm_cvtsi128_si32(out[0]); + d[1*n_channels] = _mm_cvtsi128_si32(out[1]); + d[2*n_channels] = _mm_cvtsi128_si32(out[2]); + d[3*n_channels] = _mm_cvtsi128_si32(out[3]); + d += 4*n_channels; + } + for(; n < n_samples; n++) { + *d = s0[n]; + d += n_channels; + } +} +static void +conv_interleave_32_4s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src[], + uint32_t n_channels, uint32_t n_samples) +{ + const float *s0 = src[0], *s1 = src[1], *s2 = src[2], *s3 = src[3]; + float *d = dst; + uint32_t n, unrolled; + __m128 out[4]; + + if (SPA_IS_ALIGNED(s0, 16) && + SPA_IS_ALIGNED(s1, 16) && + SPA_IS_ALIGNED(s2, 16) && + SPA_IS_ALIGNED(s3, 16)) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + out[0] = _mm_load_ps(&s0[n]); + out[1] = _mm_load_ps(&s1[n]); + out[2] = _mm_load_ps(&s2[n]); + out[3] = _mm_load_ps(&s3[n]); + + _MM_TRANSPOSE4_PS(out[0], out[1], out[2], out[3]); + + _mm_storeu_ps((d + 0*n_channels), out[0]); + _mm_storeu_ps((d + 1*n_channels), out[1]); + _mm_storeu_ps((d + 2*n_channels), out[2]); + _mm_storeu_ps((d + 3*n_channels), out[3]); + d += 4*n_channels; + } + for(; n < n_samples; n++) { + out[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]); + _mm_storeu_ps(d, out[0]); + d += n_channels; + } +} + +void +conv_interleave_32_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + int32_t *d = dst[0]; + uint32_t i = 0, n_channels = conv->n_channels; + + for(; i + 3 < n_channels; i += 4) + conv_interleave_32_4s_sse2(conv, &d[i], &src[i], n_channels, n_samples); + for(; i < n_channels; i++) + conv_interleave_32_1s_sse2(conv, &d[i], &src[i], n_channels, n_samples); +} + +#define _MM_BSWAP_EPI32(x) \ +({ \ + __m128i a = _mm_or_si128( \ + _mm_slli_epi16(x, 8), \ + _mm_srli_epi16(x, 8)); \ + a = _mm_shufflelo_epi16(a, _MM_SHUFFLE(2, 3, 0, 1)); \ + a = _mm_shufflehi_epi16(a, _MM_SHUFFLE(2, 3, 0, 1)); \ +}) + +static void +conv_interleave_32s_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src[], + uint32_t n_channels, uint32_t n_samples) +{ + const int32_t *s0 = src[0]; + int32_t *d = dst; + uint32_t n, unrolled; + __m128i out[4]; + + if (SPA_IS_ALIGNED(s0, 16)) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + out[0] = _mm_load_si128((__m128i*)&s0[n]); + out[0] = _MM_BSWAP_EPI32(out[0]); + out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1)); + out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2)); + out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3)); + + d[0*n_channels] = _mm_cvtsi128_si32(out[0]); + d[1*n_channels] = _mm_cvtsi128_si32(out[1]); + d[2*n_channels] = _mm_cvtsi128_si32(out[2]); + d[3*n_channels] = _mm_cvtsi128_si32(out[3]); + d += 4*n_channels; + } + for(; n < n_samples; n++) { + *d = bswap_32(s0[n]); + d += n_channels; + } +} +static void +conv_interleave_32s_4s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src[], + uint32_t n_channels, uint32_t n_samples) +{ + const float *s0 = src[0], *s1 = src[1], *s2 = src[2], *s3 = src[3]; + float *d = dst; + uint32_t n, unrolled; + __m128 out[4]; + + if (SPA_IS_ALIGNED(s0, 16) && + SPA_IS_ALIGNED(s1, 16) && + SPA_IS_ALIGNED(s2, 16) && + SPA_IS_ALIGNED(s3, 16)) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + out[0] = _mm_load_ps(&s0[n]); + out[1] = _mm_load_ps(&s1[n]); + out[2] = _mm_load_ps(&s2[n]); + out[3] = _mm_load_ps(&s3[n]); + + _MM_TRANSPOSE4_PS(out[0], out[1], out[2], out[3]); + + out[0] = (__m128) _MM_BSWAP_EPI32((__m128i)out[0]); + out[1] = (__m128) _MM_BSWAP_EPI32((__m128i)out[1]); + out[2] = (__m128) _MM_BSWAP_EPI32((__m128i)out[2]); + out[3] = (__m128) _MM_BSWAP_EPI32((__m128i)out[3]); + + _mm_storeu_ps(&d[0*n_channels], out[0]); + _mm_storeu_ps(&d[1*n_channels], out[1]); + _mm_storeu_ps(&d[2*n_channels], out[2]); + _mm_storeu_ps(&d[3*n_channels], out[3]); + d += 4*n_channels; + } + for(; n < n_samples; n++) { + out[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]); + out[0] = (__m128) _MM_BSWAP_EPI32((__m128i)out[0]); + _mm_storeu_ps(d, out[0]); + d += n_channels; + } +} + +void +conv_interleave_32s_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + int32_t *d = dst[0]; + uint32_t i = 0, n_channels = conv->n_channels; + + for(; i + 3 < n_channels; i += 4) + conv_interleave_32s_4s_sse2(conv, &d[i], &src[i], n_channels, n_samples); + for(; i < n_channels; i++) + conv_interleave_32s_1s_sse2(conv, &d[i], &src[i], n_channels, n_samples); +} + +static void +conv_deinterleave_32_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src, + uint32_t n_channels, uint32_t n_samples) +{ + const float *s = src; + float *d0 = dst[0]; + uint32_t n, unrolled; + __m128 out; + + if (SPA_IS_ALIGNED(d0, 16)) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + out = _mm_setr_ps(s[0*n_channels], + s[1*n_channels], + s[2*n_channels], + s[3*n_channels]); + _mm_store_ps(&d0[n], out); + s += 4*n_channels; + } + for(; n < n_samples; n++) { + d0[n] = *s; + s += n_channels; + } +} + +static void +conv_deinterleave_32_4s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src, + uint32_t n_channels, uint32_t n_samples) +{ + const float *s = src; + float *d0 = dst[0], *d1 = dst[1], *d2 = dst[2], *d3 = dst[3]; + uint32_t n, unrolled; + __m128 out[4]; + + if (SPA_IS_ALIGNED(d0, 16) && + SPA_IS_ALIGNED(d1, 16) && + SPA_IS_ALIGNED(d2, 16) && + SPA_IS_ALIGNED(d3, 16)) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + out[0] = _mm_loadu_ps(&s[0 * n_channels]); + out[1] = _mm_loadu_ps(&s[1 * n_channels]); + out[2] = _mm_loadu_ps(&s[2 * n_channels]); + out[3] = _mm_loadu_ps(&s[3 * n_channels]); + + _MM_TRANSPOSE4_PS(out[0], out[1], out[2], out[3]); + + _mm_store_ps(&d0[n], out[0]); + _mm_store_ps(&d1[n], out[1]); + _mm_store_ps(&d2[n], out[2]); + _mm_store_ps(&d3[n], out[3]); + s += 4 * n_channels; + } + for(; n < n_samples; n++) { + d0[n] = s[0]; + d1[n] = s[1]; + d2[n] = s[2]; + d3[n] = s[3]; + s += n_channels; + } +} + +void +conv_deinterleave_32_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + const float *s = src[0]; + uint32_t i = 0, n_channels = conv->n_channels; + + for(; i + 3 < n_channels; i += 4) + conv_deinterleave_32_4s_sse2(conv, &dst[i], &s[i], n_channels, n_samples); + for(; i < n_channels; i++) + conv_deinterleave_32_1s_sse2(conv, &dst[i], &s[i], n_channels, n_samples); +} + +static void +conv_deinterleave_32s_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src, + uint32_t n_channels, uint32_t n_samples) +{ + const float *s = src; + float *d0 = dst[0]; + uint32_t n, unrolled; + __m128 out; + + if (SPA_IS_ALIGNED(d0, 16)) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + out = _mm_setr_ps(s[0*n_channels], + s[1*n_channels], + s[2*n_channels], + s[3*n_channels]); + out = (__m128) _MM_BSWAP_EPI32((__m128i)out); + _mm_store_ps(&d0[n], out); + s += 4*n_channels; + } + for(; n < n_samples; n++) { + d0[n] = bswap_32(*s); + s += n_channels; + } +} + +static void +conv_deinterleave_32s_4s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src, + uint32_t n_channels, uint32_t n_samples) +{ + const float *s = src; + float *d0 = dst[0], *d1 = dst[1], *d2 = dst[2], *d3 = dst[3]; + uint32_t n, unrolled; + __m128 out[4]; + + if (SPA_IS_ALIGNED(d0, 16) && + SPA_IS_ALIGNED(d1, 16) && + SPA_IS_ALIGNED(d2, 16) && + SPA_IS_ALIGNED(d3, 16)) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + out[0] = _mm_loadu_ps(&s[0 * n_channels]); + out[1] = _mm_loadu_ps(&s[1 * n_channels]); + out[2] = _mm_loadu_ps(&s[2 * n_channels]); + out[3] = _mm_loadu_ps(&s[3 * n_channels]); + + _MM_TRANSPOSE4_PS(out[0], out[1], out[2], out[3]); + + out[0] = (__m128) _MM_BSWAP_EPI32((__m128i)out[0]); + out[1] = (__m128) _MM_BSWAP_EPI32((__m128i)out[1]); + out[2] = (__m128) _MM_BSWAP_EPI32((__m128i)out[2]); + out[3] = (__m128) _MM_BSWAP_EPI32((__m128i)out[3]); + + _mm_store_ps(&d0[n], out[0]); + _mm_store_ps(&d1[n], out[1]); + _mm_store_ps(&d2[n], out[2]); + _mm_store_ps(&d3[n], out[3]); + s += 4 * n_channels; + } + for(; n < n_samples; n++) { + d0[n] = bswap_32(s[0]); + d1[n] = bswap_32(s[1]); + d2[n] = bswap_32(s[2]); + d3[n] = bswap_32(s[3]); + s += n_channels; + } +} + +void +conv_deinterleave_32s_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + const float *s = src[0]; + uint32_t i = 0, n_channels = conv->n_channels; + + for(; i + 3 < n_channels; i += 4) + conv_deinterleave_32s_4s_sse2(conv, &dst[i], &s[i], n_channels, n_samples); + for(; i < n_channels; i++) + conv_deinterleave_32s_1s_sse2(conv, &dst[i], &s[i], n_channels, n_samples); +} + +static void conv_f32_to_s16_1_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_samples) {
View file
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/fmt-ops.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/fmt-ops.c
Changed
@@ -84,10 +84,22 @@ { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32, 0, 0, conv_copy32_c }, { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_copy32d_c }, +#if defined (HAVE_SSE2) + { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32_sse2 }, +#endif { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32_c }, +#if defined (HAVE_SSE2) + { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_interleave_32_sse2 }, +#endif { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_interleave_32_c }, +#if defined (HAVE_SSE2) + { SPA_AUDIO_FORMAT_F32_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32s_sse2 }, +#endif { SPA_AUDIO_FORMAT_F32_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32s_c }, +#if defined (HAVE_SSE2) + { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32_OE, 0, 0, conv_interleave_32s_sse2 }, +#endif { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32_OE, 0, 0, conv_interleave_32s_c }, { SPA_AUDIO_FORMAT_U32, SPA_AUDIO_FORMAT_F32, 0, 0, conv_u32_to_f32_c }, @@ -259,7 +271,13 @@ /* s32 */ { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_S32, 0, 0, conv_copy32_c }, { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_copy32d_c }, +#if defined (HAVE_SSE2) + { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_deinterleave_32_sse2 }, +#endif { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_deinterleave_32_c }, +#if defined (HAVE_SSE2) + { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_S32, 0, 0, conv_interleave_32_sse2 }, +#endif { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_S32, 0, 0, conv_interleave_32_c }, /* s24 */ @@ -271,7 +289,13 @@ /* s24_32 */ { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_copy32_c }, { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_copy32d_c }, +#if defined (HAVE_SSE2) + { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_deinterleave_32_sse2 }, +#endif { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_deinterleave_32_c }, +#if defined (HAVE_SSE2) + { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_interleave_32_sse2 }, +#endif { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_interleave_32_c }, /* F64 */
View file
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/fmt-ops.h -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/fmt-ops.h
Changed
@@ -328,6 +328,10 @@ DEFINE_FUNCTION(f32d_to_s16_2, sse2); DEFINE_FUNCTION(f32d_to_s16, sse2); DEFINE_FUNCTION(f32d_to_s16d, sse2); +DEFINE_FUNCTION(deinterleave_32, sse2); +DEFINE_FUNCTION(deinterleave_32s, sse2); +DEFINE_FUNCTION(interleave_32, sse2); +DEFINE_FUNCTION(interleave_32s, sse2); #endif #if defined(HAVE_SSSE3) DEFINE_FUNCTION(s24_to_f32d, ssse3);
View file
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/merger.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/merger.c
Changed
@@ -306,78 +306,7 @@ case SPA_PARAM_PropInfo: { - struct props *p = &this->props; - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_volume), - SPA_PROP_INFO_name, SPA_POD_String("Volume"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0)); - break; - case 1: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_mute), - SPA_PROP_INFO_name, SPA_POD_String("Mute"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->channel.mute)); - break; - case 2: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_channelVolumes), - SPA_PROP_INFO_name, SPA_POD_String("Channel Volumes"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0), - SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array)); - break; - case 3: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_channelMap), - SPA_PROP_INFO_name, SPA_POD_String("Channel Map"), - SPA_PROP_INFO_type, SPA_POD_Id(SPA_AUDIO_CHANNEL_UNKNOWN), - SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array)); - break; - case 4: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_monitorMute), - SPA_PROP_INFO_name, SPA_POD_String("Monitor Mute"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->monitor.mute)); - break; - case 5: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_monitorVolumes), - SPA_PROP_INFO_name, SPA_POD_String("Monitor Volumes"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0), - SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array)); - break; - case 6: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_softMute), - SPA_PROP_INFO_name, SPA_POD_String("Soft Mute"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->soft.mute)); - break; - case 7: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_softVolumes), - SPA_PROP_INFO_name, SPA_POD_String("Soft Volumes"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0), - SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array)); - break; - case 8: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("monitor.channel-volumes"), - SPA_PROP_INFO_description, SPA_POD_String("Monitor channel volume"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool( - this->monitor_channel_volumes), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; default: return 0; } @@ -386,46 +315,7 @@ case SPA_PARAM_Props: { -#if 0 - struct props *p = &this->props; - struct spa_pod_frame f[2]; -#endif - switch (result.index) { -#if 0 - case 0: - spa_pod_builder_push_object(&b, &f[0], - SPA_TYPE_OBJECT_Props, id); - spa_pod_builder_add(&b, - SPA_PROP_volume, SPA_POD_Float(p->volume), - SPA_PROP_mute, SPA_POD_Bool(p->channel.mute), - SPA_PROP_channelVolumes, SPA_POD_Array(sizeof(float), - SPA_TYPE_Float, - p->channel.n_volumes, - p->channel.volumes), - SPA_PROP_channelMap, SPA_POD_Array(sizeof(uint32_t), - SPA_TYPE_Id, - p->n_channels, - p->channel_map), - SPA_PROP_softMute, SPA_POD_Bool(p->soft.mute), - SPA_PROP_softVolumes, SPA_POD_Array(sizeof(float), - SPA_TYPE_Float, - p->soft.n_volumes, - p->soft.volumes), - SPA_PROP_monitorMute, SPA_POD_Bool(p->monitor.mute), - SPA_PROP_monitorVolumes, SPA_POD_Array(sizeof(float), - SPA_TYPE_Float, - p->monitor.n_volumes, - p->monitor.volumes), - 0); - spa_pod_builder_prop(&b, SPA_PROP_params, 0); - spa_pod_builder_push_struct(&b, &f[1]); - spa_pod_builder_string(&b, "monitor.channel-volumes"); - spa_pod_builder_bool(&b, this->monitor_channel_volumes); - spa_pod_builder_pop(&b, &f[1]); - param = spa_pod_builder_pop(&b, &f[0]); - break; -#endif default: return 0; }
View file
pipewire-0.3.48.tar.gz/spa/plugins/audioconvert/resample.c -> pipewire-0.3.49.tar.gz/spa/plugins/audioconvert/resample.c
Changed
@@ -307,9 +307,6 @@ if (spa_pod_is_string(pod)) { spa_pod_copy_string(pod, sizeof(value), value); - } else if (spa_pod_is_float(pod)) { - snprintf(value, sizeof(value), "%f", - SPA_POD_VALUE(struct spa_pod_float, pod)); } else if (spa_pod_is_int(pod)) { snprintf(value, sizeof(value), "%d", SPA_POD_VALUE(struct spa_pod_int, pod)); @@ -395,10 +392,10 @@ static void update_rate_match(struct impl *this, bool passthrough, uint32_t out_size, uint32_t in_queued) { if (this->io_rate_match) { - uint32_t match_size; + uint32_t delay, match_size; if (passthrough) { - this->io_rate_match->delay = 0; + delay = in_queued; match_size = out_size; } else { if (SPA_FLAG_IS_SET(this->io_rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE)) @@ -406,12 +403,14 @@ else resample_update_rate(&this->resample, this->rate_scale); - this->io_rate_match->delay = resample_delay(&this->resample); + delay = resample_delay(&this->resample) + in_queued; match_size = resample_in_len(&this->resample, out_size); } match_size -= SPA_MIN(match_size, in_queued); this->io_rate_match->size = match_size; - spa_log_trace_fp(this->log, "%p: next match %u", this, match_size); + this->io_rate_match->delay = delay; + spa_log_trace_fp(this->log, "%p: next match:%u queued:%u delay:%u", this, match_size, + in_queued, delay); } else { resample_update_rate(&this->resample, this->rate_scale * this->props.rate); } @@ -997,7 +996,7 @@ if (SPA_LIKELY(this->io_position)) { double r = this->rate_scale; - max = this->io_position->clock.duration; + max = this->io_position->clock.duration * sizeof(float); if (this->mode == MODE_SPLIT) { if (this->io_position->clock.rate.denom != this->resample.o_rate) r = (double) this->io_position->clock.rate.denom / this->resample.o_rate; @@ -1015,13 +1014,13 @@ } } else - max = maxsize / sizeof(float); + max = maxsize; switch (this->mode) { case MODE_SPLIT: /* in split mode we need to output exactly the size of the * duration so we don't try to flush early */ - maxsize = SPA_MIN(maxsize, max * sizeof(float)); + maxsize = SPA_MIN(maxsize, max); flush_out = false; break; case MODE_MERGE: @@ -1112,8 +1111,8 @@ spa_log_trace_fp(this->log, "%p: no output buffer", this); } - update_rate_match(this, passthrough, max - outport->offset / sizeof(float), - size - inport->offset / sizeof(float)); + update_rate_match(this, passthrough, (max - outport->offset) / sizeof(float), + (size - inport->offset) / sizeof(float)); return res; }
View file
pipewire-0.3.48.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c -> pipewire-0.3.49.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c
Changed
@@ -984,6 +984,13 @@ return 0; } +static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *this = user_data; + spa_loop_remove_source(this->data_loop, &this->timer_source); + return 0; +} + static int impl_clear(struct spa_handle *handle) { struct impl *this; @@ -993,7 +1000,7 @@ this = (struct impl *) handle; if (this->data_loop) - spa_loop_remove_source(this->data_loop, &this->timer_source); + spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this); spa_system_close(this->data_system, this->timer_source.fd); return 0;
View file
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c
Changed
@@ -343,6 +343,8 @@ if (this->hd) this->max_frames = (this->mtu - sizeof(struct rtp_header)) / this->frame_length; + else if (codec_is_ll(codec)) + this->max_frames = SPA_MIN(256u, this->mtu) / this->frame_length; else this->max_frames = this->mtu / this->frame_length;
View file
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/a2dp-codecs.h -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/a2dp-codecs.h
Changed
@@ -43,7 +43,7 @@ #define SPA_TYPE_INTERFACE_Bluez5CodecA2DP SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:A2DP:Private" -#define SPA_VERSION_BLUEZ5_CODEC_A2DP 0 +#define SPA_VERSION_BLUEZ5_CODEC_A2DP 1 struct spa_bluez5_codec_a2dp { struct spa_interface iface;
View file
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/a2dp-sink.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/a2dp-sink.c
Changed
@@ -70,6 +70,9 @@ #define FILL_FRAMES 2 #define MAX_BUFFERS 32 +#define MIN_LATENCY 128 +#define MAX_LATENCY 8192 +#define BUFFER_SIZE (MAX_LATENCY*8) struct buffer { uint32_t id; @@ -137,6 +140,8 @@ struct spa_source source; int timerfd; struct spa_source flush_source; + struct spa_source flush_timer_source; + int flush_timerfd; struct spa_io_clock *clock; struct spa_io_position *position; @@ -153,14 +158,14 @@ int need_flush; uint32_t block_size; - uint8_t buffer[4096]; + uint8_t buffer[BUFFER_SIZE]; uint32_t buffer_used; uint32_t header_size; uint32_t frame_count; uint16_t seqnum; uint32_t timestamp; uint64_t sample_count; - uint8_t tmp_buffer[4096]; + uint8_t tmp_buffer[BUFFER_SIZE]; uint32_t tmp_buffer_used; uint32_t fd_buffer_size; }; @@ -169,11 +174,7 @@ static void reset_props(struct impl *this, struct props *props) { - if (this->codec->id == SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL) { - props->min_latency = 256; - } else { - props->min_latency = MIN_LATENCY; - } + props->min_latency = MIN_LATENCY; props->max_latency = MAX_LATENCY; props->latency_offset = 0; strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name)); @@ -481,11 +482,6 @@ return written; } -static bool want_flush(struct impl *this) -{ - return (this->frame_count * this->block_size / this->port.frame_size >= this->props.min_latency); -} - static int encode_buffer(struct impl *this, const void *data, uint32_t size) { int processed; @@ -542,7 +538,7 @@ spa_log_trace(this->log, "%p: used:%d block_size:%d", this, this->buffer_used, this->block_size); - if (this->need_flush || want_flush(this)) + if (this->need_flush) return send_buffer(this); return 0; @@ -565,12 +561,25 @@ return total; } -static void enable_flush(struct impl *this, bool enabled) +static void enable_flush(struct impl *this, bool enabled, uint64_t timeout) { - if (SPA_FLAG_IS_SET(this->flush_source.mask, SPA_IO_OUT) != enabled) { - SPA_FLAG_UPDATE(this->flush_source.mask, SPA_IO_OUT, enabled); + bool flush_enabled = enabled && (timeout == 0); + struct itimerspec ts; + + if (SPA_FLAG_IS_SET(this->flush_source.mask, SPA_IO_OUT) != flush_enabled) { + SPA_FLAG_UPDATE(this->flush_source.mask, SPA_IO_OUT, flush_enabled); spa_loop_update_source(this->data_loop, &this->flush_source); } + + if (!enabled) + timeout = 0; + + ts.it_value.tv_sec = timeout / SPA_NSEC_PER_SEC; + ts.it_value.tv_nsec = timeout % SPA_NSEC_PER_SEC; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + spa_system_timerfd_settime(this->data_system, + this->flush_timerfd, 0, &ts, NULL); } static int flush_data(struct impl *this, uint64_t now_time) @@ -579,6 +588,11 @@ uint32_t total_frames; struct port *port = &this->port; + if (!this->flush_source.loop) { + /* I/O in error state */ + return -EIO; + } + total_frames = 0; again: written = 0; @@ -640,42 +654,80 @@ } if (written > 0 && this->buffer_used == this->header_size) { - enable_flush(this, false); + enable_flush(this, false, 0); return 0; } written = flush_buffer(this); + if (written == -EAGAIN) { - spa_log_trace(this->log, "%p: delay flush", this); + spa_log_trace(this->log, "%p: fail flush", this); if (now_time - this->last_error > SPA_NSEC_PER_SEC / 2) { + spa_log_trace(this->log, "%p: reduce bitpool", this); this->codec->reduce_bitpool(this->codec_data); this->last_error = now_time; } - this->need_flush = true; - enable_flush(this, true); + + /* + * The socket buffer is full, and the device is not processing data + * fast enough, so should just skip this packet. There will be a sound + * glitch in any case. + */ + written = this->buffer_used; + reset_buffer(this); } - else if (written < 0) { + + if (written < 0) { spa_log_trace(this->log, "%p: error flushing %s", this, spa_strerror(written)); reset_buffer(this); - enable_flush(this, false); + enable_flush(this, false, 0); return written; } else if (written > 0) { + /* + * We cannot write all data we have at once, since this can exceed + * device buffers. We'll want a limited number of "excess" + * samples. This is an issue for the "low-latency" A2DP codecs. + * + * Flushing the rest of the data (if any) is delayed after a timeout, + * selected on an average-rate basis: + * + * npackets = quantum / packet_samples + * write_end_time = npackets * timeout + * max_excess = quantum - sample_rate * write_end_time + * packet_time = packet_samples / sample_rate + * => timeout = (quantum - max_excess)/quantum * packet_time + */ + uint64_t max_excess = 2*256; + uint64_t packet_samples = this->frame_count * this->block_size / port->frame_size; + uint64_t packet_time = packet_samples * SPA_NSEC_PER_SEC / port->current_format.info.raw.rate; + uint64_t quantum = SPA_LIKELY(this->clock) ? this->clock->duration : 0; + uint64_t timeout = (quantum > max_excess) ? + (packet_time * (quantum - max_excess) / quantum) : 0; + reset_buffer(this); if (now_time - this->last_error > SPA_NSEC_PER_SEC) { - this->codec->increase_bitpool(this->codec_data); + if (get_transport_unused_size(this) == (int)this->fd_buffer_size) { + spa_log_trace(this->log, "%p: increase bitpool", this); + this->codec->increase_bitpool(this->codec_data); + } this->last_error = now_time; } - if (!spa_list_is_empty(&port->ready)) - goto again; - - enable_flush(this, false); + if (!spa_list_is_empty(&port->ready)) { + spa_log_trace(this->log, "%p: flush after %d ns", this, (int)timeout); + if (timeout == 0) + goto again; + else + enable_flush(this, true, timeout); + } else { + enable_flush(this, false, 0); + } } else { /* Don't want to flush yet, or failed to write anything */ spa_log_trace(this->log, "%p: skip flush", this); - enable_flush(this, false); + enable_flush(this, false, 0); } return 0; } @@ -692,6 +744,30 @@ spa_loop_remove_source(this->data_loop, &this->flush_source); return; } + + if (this->transport == NULL) { + enable_flush(this, false, 0); + return; + } + + flush_data(this, this->current_time); +} + +static void a2dp_on_flush_timeout(struct spa_source *source) +{ + struct impl *this = source->data; + uint64_t exp; + + spa_log_trace(this->log, "%p: flush on timeout", this); + + if (spa_system_timerfd_read(this->data_system, this->flush_timerfd, &exp) < 0) + spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); + + if (this->transport == NULL) { + enable_flush(this, false, 0); + return; + } + flush_data(this, this->current_time); } @@ -835,6 +911,13 @@ this->source.rmask = 0; spa_loop_add_source(this->data_loop, &this->source); + this->flush_timer_source.data = this; + this->flush_timer_source.fd = this->flush_timerfd; + this->flush_timer_source.func = a2dp_on_flush_timeout; + this->flush_timer_source.mask = SPA_IO_IN; + this->flush_timer_source.rmask = 0; + spa_loop_add_source(this->data_loop, &this->flush_timer_source); + this->flush_source.data = this; this->flush_source.fd = this->transport->fd; this->flush_source.func = a2dp_on_flush; @@ -865,9 +948,18 @@ ts.it_interval.tv_sec = 0; ts.it_interval.tv_nsec = 0; spa_system_timerfd_settime(this->data_system, this->timerfd, 0, &ts, NULL); + if (this->flush_source.loop) spa_loop_remove_source(this->data_loop, &this->flush_source); + if (this->flush_timer_source.loop) + spa_loop_remove_source(this->data_loop, &this->flush_timer_source); + ts.it_value.tv_sec = 0; + ts.it_value.tv_nsec = 0; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + spa_system_timerfd_settime(this->data_system, this->flush_timerfd, 0, &ts, NULL); + return 0; } @@ -928,22 +1020,15 @@ static void emit_node_info(struct impl *this, bool full) { - char latency[64] = SPA_STRINGIFY(MIN_LATENCY)"/48000"; struct spa_dict_item node_info_items[] = { { SPA_KEY_DEVICE_API, "bluez5" }, { SPA_KEY_MEDIA_CLASS, "Audio/Sink" }, { SPA_KEY_NODE_DRIVER, "true" }, - { SPA_KEY_NODE_LATENCY, latency }, }; uint64_t old = full ? this->info.change_mask : 0; if (full) this->info.change_mask = this->info_all; if (this->info.change_mask) { - if (this->transport && this->port.have_format) - snprintf(latency, sizeof(latency), "%d/%d", (int)this->props.min_latency, - (int)this->port.current_format.info.raw.rate); - else - snprintf(latency, sizeof(latency), "%d/48000", (int)this->props.min_latency); this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items); spa_node_emit_info(&this->hooks, &this->info); this->info.change_mask = old; @@ -1426,6 +1511,7 @@ if (this->transport) spa_hook_remove(&this->transport_listener); spa_system_close(this->data_system, this->timerfd); + spa_system_close(this->data_system, this->flush_timerfd); return 0; } @@ -1533,6 +1619,9 @@ this->timerfd = spa_system_timerfd_create(this->data_system, CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); + this->flush_timerfd = spa_system_timerfd_create(this->data_system, + CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); + return 0; }
View file
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/a2dp-source.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/a2dp-source.c
Changed
@@ -70,6 +70,8 @@ #define FILL_FRAMES 2 #define MAX_BUFFERS 32 +#define MIN_LATENCY 512 +#define MAX_LATENCY 1024 struct buffer { uint32_t id;
View file
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/backend-ofono.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/backend-ofono.c
Changed
@@ -38,10 +38,14 @@ #include <spa/support/plugin.h> #include <spa/utils/string.h> #include <spa/utils/type.h> +#include <spa/utils/result.h> #include <spa/param/audio/raw.h> #include "defs.h" +#define INITIAL_INTERVAL_NSEC (500 * SPA_NSEC_PER_MSEC) +#define ACTION_INTERVAL_NSEC (3000 * SPA_NSEC_PER_MSEC) + static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.ofono"); #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic @@ -53,17 +57,23 @@ struct spa_log *log; struct spa_loop *main_loop; + struct spa_system *main_system; struct spa_dbus *dbus; + struct spa_loop_utils *loop_utils; DBusConnection *conn; const struct spa_bt_quirks *quirks; + struct spa_source *timer; + unsigned int filters_added:1; unsigned int msbc_supported:1; }; struct transport_data { struct spa_source sco; + unsigned int broken:1; + unsigned int activated:1; }; #define OFONO_HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager" @@ -161,6 +171,13 @@ dbus_error_init(&err); + /* + * XXX: We assume here oFono replies. It however can happen that the headset does + * XXX: not properly respond to the codec negotiation RFCOMM commands. + * XXX: oFono (1.34) fails to handle this condition, and will not send DBus reply + * XXX: in this case. The transport acquire API is synchronous, so we can't + * XXX: do better here right now. + */ r = dbus_connection_send_with_reply_and_block(backend->conn, m, -1, &err); dbus_message_unref(m); m = NULL; @@ -196,12 +213,19 @@ static int ofono_audio_acquire(void *data, bool optional) { struct spa_bt_transport *transport = data; + struct transport_data *td = transport->user_data; struct impl *backend = SPA_CONTAINER_OF(transport->backend, struct impl, this); uint8_t codec; int ret = 0; if (transport->fd >= 0) goto finish; + if (td->broken) { + ret = -EIO; + goto finish; + } + + spa_bt_device_update_last_bluez_action_time(transport->device); ret = _audio_acquire(backend, transport->path, &codec); if (ret < 0) @@ -210,27 +234,28 @@ transport->fd = ret; if (transport->codec != codec) { - struct spa_bt_transport *t = NULL; + struct timespec ts; - spa_log_warn(backend->log, "Acquired codec (%d) differs from transport one (%d)", - codec, transport->codec); + spa_log_info(backend->log, "transport %p: acquired codec (%d) differs from transport one (%d)", + transport, codec, transport->codec); /* shutdown to make sure connection is dropped immediately */ shutdown(transport->fd, SHUT_RDWR); close(transport->fd); transport->fd = -1; - /* Create a new transport which differs only for codec */ - t = _transport_create(backend, transport->path, transport->device, - transport->profile, codec, &transport->impl); - - spa_bt_transport_free(transport); - spa_bt_device_connect_profile(t->device, t->profile); - - ret = -EIO; - goto finish; + /* schedule immediate profile update, from main loop */ + transport->codec = codec; + td->broken = true; + ts.tv_sec = 0; + ts.tv_nsec = 1; + spa_loop_utils_update_timer(backend->loop_utils, backend->timer, + &ts, NULL, false); + return -EIO; } + td->broken = false; + spa_log_debug(backend->log, "transport %p: Acquire %s, fd %d codec %d", transport, transport->path, transport->fd, transport->codec); @@ -293,14 +318,74 @@ .release = ofono_audio_release, }; +bool activate_transport(struct spa_bt_transport *t, const void *data) +{ + struct impl *backend = (void *)data; + struct transport_data *td = t->user_data; + struct timespec ts; + uint64_t now, threshold; + + if (t->backend != &backend->this) + return false; + + /* Check device-specific rate limit */ + spa_system_clock_gettime(backend->main_system, CLOCK_MONOTONIC, &ts); + now = SPA_TIMESPEC_TO_NSEC(&ts); + threshold = t->device->last_bluez_action_time + ACTION_INTERVAL_NSEC; + if (now < threshold) { + ts.tv_sec = (threshold - now) / SPA_NSEC_PER_SEC; + ts.tv_nsec = (threshold - now) % SPA_NSEC_PER_SEC; + spa_loop_utils_update_timer(backend->loop_utils, backend->timer, + &ts, NULL, false); + return false; + } + + if (!td->activated) { + /* Connect profile */ + spa_log_debug(backend->log, "Transport %s activated", t->path); + td->activated = true; + spa_bt_device_connect_profile(t->device, t->profile); + } + + if (td->broken) { + /* Recreate the transport */ + struct spa_bt_transport *t_copy; + + t_copy = _transport_create(backend, t->path, t->device, + t->profile, t->codec, (struct spa_callbacks *)&ofono_transport_impl); + spa_bt_transport_free(t); + + if (t_copy) + spa_bt_device_connect_profile(t_copy->device, t_copy->profile); + + return true; + } + + return false; +} + +static void activate_transports(struct impl *backend) +{ + while (spa_bt_transport_find_full(backend->monitor, activate_transport, backend)); +} + +static void activate_timer_event(void *userdata, uint64_t expirations) +{ + struct impl *backend = userdata; + spa_loop_utils_update_timer(backend->loop_utils, backend->timer, NULL, NULL, false); + activate_transports(backend); +} + static DBusHandlerResult ofono_audio_card_found(struct impl *backend, char *path, DBusMessageIter *props_i) { const char *remote_address = NULL; const char *local_address = NULL; struct spa_bt_device *d; struct spa_bt_transport *t; + struct transport_data *td; enum spa_bt_profile profile = SPA_BT_PROFILE_HFP_AG; - uint8_t codec = HFP_AUDIO_CODEC_CVSD; + uint8_t codec = backend->msbc_supported ? + HFP_AUDIO_CODEC_MSBC : HFP_AUDIO_CODEC_CVSD; spa_assert(backend); spa_assert(path); @@ -340,24 +425,6 @@ dbus_message_iter_next(props_i); } - /* - * Acquire and close immediately to figure out the codec. - * This is necessary if we are in HF mode, because we need to emit - * nodes and the advertised sample rate of the node depends on the codec. - * For AG mode, we delay the emission of the nodes, so it is not necessary - * to know the codec in advance - */ - if (profile == SPA_BT_PROFILE_HFP_HF) { - int fd = _audio_acquire(backend, path, &codec); - if (fd < 0) { - spa_log_error(backend->log, "Failed to retrieve codec for %s", path); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - /* shutdown to make sure connection is dropped immediately */ - shutdown(fd, SHUT_RDWR); - close(fd); - } - if (!remote_address || !local_address) { spa_log_error(backend->log, "Missing addresses for %s", path); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -371,8 +438,31 @@ spa_bt_device_add_profile(d, profile); t = _transport_create(backend, path, d, profile, codec, (struct spa_callbacks *)&ofono_transport_impl); + if (t == NULL) { + spa_log_error(backend->log, "failed to create transport: %s", spa_strerror(-errno)); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } - spa_bt_device_connect_profile(t->device, profile); + td = t->user_data; + + /* + * For HF profile, delay profile connect, so that we likely don't do it at the + * same time as the device is busy with A2DP connect. This avoids some oFono + * misbehavior (see comment in _audio_acquire above). + * + * For AG mode, we delay the emission of the nodes, so it is not necessary + * to know the codec in advance. + */ + if (profile == SPA_BT_PROFILE_HFP_HF) { + struct timespec ts; + ts.tv_sec = INITIAL_INTERVAL_NSEC / SPA_NSEC_PER_SEC; + ts.tv_nsec = INITIAL_INTERVAL_NSEC % SPA_NSEC_PER_SEC; + spa_loop_utils_update_timer(backend->loop_utils, backend->timer, + &ts, NULL, false); + } else { + td->activated = true; + spa_bt_device_connect_profile(t->device, t->profile); + } spa_log_debug(backend->log, "Transport %s available, codec %d", t->path, t->codec); @@ -754,6 +844,9 @@ backend->filters_added = false; } + if (backend->timer) + spa_loop_utils_destroy_source(backend->loop_utils, backend->timer); + dbus_connection_unregister_object_path(backend->conn, OFONO_AUDIO_CLIENT); free(backend); @@ -819,6 +912,8 @@ backend->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus); backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); + backend->main_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System); + backend->loop_utils = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_LoopUtils); backend->conn = dbus_connection; if (info && (str = spa_dict_lookup(info, "bluez5.enable-msbc"))) backend->msbc_supported = spa_atob(str); @@ -827,6 +922,12 @@ spa_log_topic_init(backend->log, &log_topic); + backend->timer = spa_loop_utils_add_timer(backend->loop_utils, activate_timer_event, backend); + if (backend->timer == NULL) { + free(backend); + return NULL; + } + if (!dbus_connection_register_object_path(backend->conn, OFONO_AUDIO_CLIENT, &vtable_profile, backend)) {
View file
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/bluez5-dbus.c
Changed
@@ -837,7 +837,7 @@ return NULL; } -static void device_update_last_bluez_action_time(struct spa_bt_device *device) +void spa_bt_device_update_last_bluez_action_time(struct spa_bt_device *device) { struct timespec ts; spa_system_clock_gettime(device->monitor->main_system, CLOCK_MONOTONIC, &ts); @@ -867,7 +867,7 @@ spa_list_prepend(&monitor->device_list, &d->link); - device_update_last_bluez_action_time(d); + spa_bt_device_update_last_bluez_action_time(d); return d; } @@ -2595,7 +2595,7 @@ goto next; } - device_update_last_bluez_action_time(sw->device); + spa_bt_device_update_last_bluez_action_time(sw->device); spa_log_info(sw->device->monitor->log, "a2dp codec switch %p: trying codec %s for endpoint %s, local endpoint %s", sw, codec->name, ep->path, local_endpoint); @@ -2727,7 +2727,7 @@ dbus_pending_call_unref(pending); sw->pending = NULL; - device_update_last_bluez_action_time(device); + spa_bt_device_update_last_bluez_action_time(device); if (!a2dp_codec_switch_goto_active(sw)) { if (r != NULL) @@ -3030,7 +3030,7 @@ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - device_update_last_bluez_action_time(transport->device); + spa_bt_device_update_last_bluez_action_time(transport->device); if (profile & SPA_BT_PROFILE_A2DP_SOURCE) { /* PW is the rendering device so it's responsible for reporting hardware volume. */
View file
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/codec-loader.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/codec-loader.c
Changed
@@ -103,7 +103,7 @@ spa_log_debug(impl->log, "loading codecs from %s", factory_name); if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Bluez5CodecA2DP, &iface)) < 0) { - spa_log_info(impl->log, "Bluetooth codec plugin %s has no codec interface", + spa_log_warn(impl->log, "Bluetooth codec plugin %s has no codec interface", factory_name); goto fail; } @@ -111,7 +111,7 @@ bluez5_codec_a2dp = iface; if (bluez5_codec_a2dp->iface.version != SPA_VERSION_BLUEZ5_CODEC_A2DP) { - spa_log_info(impl->log, "codec plugin %s has incompatible ABI version (%d != %d)", + spa_log_warn(impl->log, "codec plugin %s has incompatible ABI version (%d != %d)", factory_name, bluez5_codec_a2dp->iface.version, SPA_VERSION_BLUEZ5_CODEC_A2DP); res = -ENOENT; goto fail;
View file
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/defs.h
Changed
@@ -60,9 +60,6 @@ #define PIPEWIRE_BATTERY_PROVIDER "/org/freedesktop/pipewire/battery" -#define MIN_LATENCY 512 -#define MAX_LATENCY 1024 - #define OBJECT_MANAGER_INTROSPECT_XML \ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ "<node>\n" \ @@ -499,6 +496,7 @@ int spa_bt_device_supports_hfp_codec(struct spa_bt_device *device, unsigned int codec); int spa_bt_device_release_transports(struct spa_bt_device *device); int spa_bt_device_report_battery_level(struct spa_bt_device *device, uint8_t percentage); +void spa_bt_device_update_last_bluez_action_time(struct spa_bt_device *device); #define spa_bt_device_emit(d,m,v,...) spa_hook_list_call(&(d)->listener_list, \ struct spa_bt_device_events, \
View file
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/quirks.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/quirks.c
Changed
@@ -262,14 +262,16 @@ } else { char path[PATH_MAX]; const char *dir = getenv("SPA_DATA_DIR"); + int res; if (dir == NULL) dir = SPADATADIR; if (spa_scnprintf(path, sizeof(path), "%s/bluez5/bluez-hardware.conf", dir) >= 0) - load_conf(this, path); + if ((res = load_conf(this, path)) < 0) + spa_log_warn(this->log, "failed to load '%s': %s", path, + spa_strerror(res)); } - if (!(this->kernel_rules && this->adapter_rules && this->device_rules)) spa_log_warn(this->log, "failed to load bluez-hardware.conf");
View file
pipewire-0.3.48.tar.gz/spa/plugins/bluez5/sco-sink.c -> pipewire-0.3.49.tar.gz/spa/plugins/bluez5/sco-sink.c
Changed
@@ -66,6 +66,8 @@ }; #define MAX_BUFFERS 32 +#define MIN_LATENCY 512 +#define MAX_LATENCY 1024 struct buffer { uint32_t id;
View file
pipewire-0.3.48.tar.gz/spa/plugins/support/loop.c -> pipewire-0.3.49.tar.gz/spa/plugins/support/loop.c
Changed
@@ -90,6 +90,7 @@ uint8_t buffer_mem[DATAS_SIZE + MAX_ALIGN]; unsigned int flushing:1; + unsigned int polling:1; }; struct source_impl { @@ -98,7 +99,6 @@ struct impl *impl; struct spa_list link; - bool close; union { spa_source_io_func_t io; spa_source_idle_func_t idle; @@ -106,8 +106,11 @@ spa_source_timer_func_t timer; spa_source_signal_func_t signal; } func; - bool enabled; + struct spa_source *fallback; + + bool close; + bool enabled; }; /** \endcond */ @@ -116,28 +119,51 @@ struct impl *impl = object; source->loop = &impl->loop; source->priv = NULL; + source->rmask = 0; return spa_system_pollfd_add(impl->system, impl->poll_fd, source->fd, source->mask, source); } static int loop_update_source(void *object, struct spa_source *source) { struct impl *impl = object; + + spa_assert(source->loop == &impl->loop); + return spa_system_pollfd_mod(impl->system, impl->poll_fd, source->fd, source->mask, source); } -static int loop_remove_source(void *object, struct spa_source *source) +static void detach_source(struct spa_source *source) { - struct impl *impl = object; struct spa_poll_event *e; + + source->loop = NULL; + source->rmask = 0; + if ((e = source->priv)) { /* active in an iteration of the loop, remove it from there */ e->data = NULL; source->priv = NULL; } - source->loop = NULL; +} + +static int remove_from_poll(struct impl *impl, struct spa_source *source) +{ + spa_assert(source->loop == &impl->loop); + return spa_system_pollfd_del(impl->system, impl->poll_fd, source->fd); } +static int loop_remove_source(void *object, struct spa_source *source) +{ + struct impl *impl = object; + spa_assert(!impl->polling); + + int res = remove_from_poll(impl, source); + detach_source(source); + + return res; +} + static void flush_items(struct impl *impl) { uint32_t index; @@ -151,7 +177,7 @@ item = SPA_PTROFF(impl->buffer_data, index & (DATAS_SIZE - 1), struct invoke_item); block = item->block; - spa_log_trace(impl->log, "%p: flush item %p", impl, item); + spa_log_trace_fp(impl->log, "%p: flush item %p", impl, item); item->res = item->func ? item->func(&impl->loop, true, item->seq, item->data, item->size, item->user_data) : 0; @@ -223,7 +249,7 @@ item->user_data = user_data; item->item_size = SPA_ROUND_UP_N(sizeof(struct invoke_item) + size, ITEM_ALIGN); - spa_log_trace(impl->log, "%p: add item %p filled:%d", impl, item, filled); + spa_log_trace_fp(impl->log, "%p: add item %p filled:%d", impl, item, filled); if (l0 >= item->item_size) { /* item + size fit in current ringbuffer idx */ @@ -322,15 +348,25 @@ spa_log_trace(impl->log, "%p: leave %lu", impl, impl->thread); - if (--impl->enter_count == 0) + if (--impl->enter_count == 0) { impl->thread = 0; + impl->polling = false; + } +} + +static inline void free_source(struct source_impl *s) +{ + detach_source(&s->source); + free(s); } static inline void process_destroy(struct impl *impl) { struct source_impl *source, *tmp; + spa_list_for_each_safe(source, tmp, &impl->destroy_list, link) - free(source); + free_source(source); + spa_list_init(&impl->destroy_list); } @@ -340,20 +376,22 @@ struct spa_poll_event ep[MAX_EP], *e; int i, nfds; + impl->polling = true; spa_loop_control_hook_before(&impl->hooks_list); - nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, MAX_EP, timeout); + nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, SPA_N_ELEMENTS(ep), timeout); spa_loop_control_hook_after(&impl->hooks_list); - - if (SPA_UNLIKELY(nfds < 0)) - return nfds; + impl->polling = false; /* first we set all the rmasks, then call the callbacks. The reason is that * some callback might also want to look at other sources it manages and * can then reset the rmask to suppress the callback */ for (i = 0; i < nfds; i++) { struct spa_source *s = ep[i].data; + + spa_assert(s->loop == &impl->loop); + s->rmask = ep[i].events; /* already active in another iteration of the loop, * remove it from that iteration */ @@ -361,24 +399,32 @@ e->data = NULL; s->priv = &ep[i]; } + + if (SPA_UNLIKELY(!spa_list_is_empty(&impl->destroy_list))) + process_destroy(impl); + for (i = 0; i < nfds; i++) { struct spa_source *s = ep[i].data; - if (SPA_LIKELY(s && s->rmask && s->loop)) { - s->priv = NULL; + if (SPA_LIKELY(s && s->rmask)) s->func(s); + } + + for (i = 0; i < nfds; i++) { + struct spa_source *s = ep[i].data; + if (SPA_LIKELY(s)) { + s->rmask = 0; + s->priv = NULL; } } - if (SPA_UNLIKELY(!spa_list_is_empty(&impl->destroy_list))) - process_destroy(impl); return nfds; } static void source_io_func(struct spa_source *source) { - struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); - spa_log_trace_fp(impl->impl->log, "%p: io %08x", impl, source->rmask); - impl->func.io(source->data, source->fd, source->rmask); + struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); + spa_log_trace_fp(s->impl->log, "%p: io %08x", s, source->rmask); + s->func.io(source->data, source->fd, source->rmask); } static struct spa_source *loop_add_io(void *object, @@ -394,7 +440,6 @@ if (source == NULL) goto error_exit; - source->source.loop = &impl->loop; source->source.func = source_io_func; source->source.data = data; source->source.fd = fd; @@ -434,8 +479,13 @@ struct impl *impl = object; struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); int res; + + spa_assert(s->impl == object); + spa_assert(source->func == source_io_func); + + spa_log_trace(impl->log, "%p: update %08x -> %08x", s, source->mask, mask); source->mask = mask; - spa_log_trace(impl->log, "%p: update %08x", s, mask); + if (s->fallback) res = spa_loop_utils_enable_idle(&impl->utils, s->fallback, mask & (SPA_IO_IN | SPA_IO_OUT) ? true : false); @@ -446,26 +496,29 @@ static void source_idle_func(struct spa_source *source) { - struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); - impl->func.idle(source->data); + struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); + s->func.idle(source->data); } static int loop_enable_idle(void *object, struct spa_source *source, bool enabled) { - struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); + struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); int res = 0; - if (enabled && !impl->enabled) { - if ((res = spa_system_eventfd_write(impl->impl->system, source->fd, 1)) < 0) - spa_log_warn(impl->impl->log, "%p: failed to write idle fd %d: %s", + spa_assert(s->impl == object); + spa_assert(source->func == source_idle_func); + + if (enabled && !s->enabled) { + if ((res = spa_system_eventfd_write(s->impl->system, source->fd, 1)) < 0) + spa_log_warn(s->impl->log, "%p: failed to write idle fd %d: %s", source, source->fd, spa_strerror(res)); - } else if (!enabled && impl->enabled) { + } else if (!enabled && s->enabled) { uint64_t count; - if ((res = spa_system_eventfd_read(impl->impl->system, source->fd, &count)) < 0) - spa_log_warn(impl->impl->log, "%p: failed to read idle fd %d: %s", + if ((res = spa_system_eventfd_read(s->impl->system, source->fd, &count)) < 0) + spa_log_warn(s->impl->log, "%p: failed to read idle fd %d: %s", source, source->fd, spa_strerror(res)); } - impl->enabled = enabled; + s->enabled = enabled; return res; } @@ -483,7 +536,6 @@ if ((res = spa_system_eventfd_create(impl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0) goto error_exit_free; - source->source.loop = &impl->loop; source->source.func = source_idle_func; source->source.data = data; source->source.fd = res; @@ -513,15 +565,15 @@ static void source_event_func(struct spa_source *source) { - struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); + struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); uint64_t count = 0; int res; - if ((res = spa_system_eventfd_read(impl->impl->system, source->fd, &count)) < 0) - spa_log_warn(impl->impl->log, "%p: failed to read event fd %d: %s", + if ((res = spa_system_eventfd_read(s->impl->system, source->fd, &count)) < 0) + spa_log_warn(s->impl->log, "%p: failed to read event fd %d: %s", source, source->fd, spa_strerror(res)); - impl->func.event(source->data, count); + s->func.event(source->data, count); } static struct spa_source *loop_add_event(void *object, @@ -538,7 +590,6 @@ if ((res = spa_system_eventfd_create(impl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0) goto error_exit_free; - source->source.loop = &impl->loop; source->source.func = source_event_func; source->source.data = data; source->source.fd = res; @@ -565,27 +616,30 @@ static int loop_signal_event(void *object, struct spa_source *source) { - struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); + struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); int res; - if (SPA_UNLIKELY((res = spa_system_eventfd_write(impl->impl->system, source->fd, 1)) < 0)) - spa_log_warn(impl->impl->log, "%p: failed to write event fd %d: %s", + spa_assert(s->impl == object); + spa_assert(source->func == source_event_func); + + if (SPA_UNLIKELY((res = spa_system_eventfd_write(s->impl->system, source->fd, 1)) < 0)) + spa_log_warn(s->impl->log, "%p: failed to write event fd %d: %s", source, source->fd, spa_strerror(res)); return res; } static void source_timer_func(struct spa_source *source) { - struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); + struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); uint64_t expirations = 0; int res; - if (SPA_UNLIKELY((res = spa_system_timerfd_read(impl->impl->system, + if (SPA_UNLIKELY((res = spa_system_timerfd_read(s->impl->system, source->fd, &expirations)) < 0)) - spa_log_warn(impl->impl->log, "%p: failed to read timer fd %d: %s", + spa_log_warn(s->impl->log, "%p: failed to read timer fd %d: %s", source, source->fd, spa_strerror(res)); - impl->func.timer(source->data, expirations); + s->func.timer(source->data, expirations); } static struct spa_source *loop_add_timer(void *object, @@ -603,7 +657,6 @@ SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0) goto error_exit_free; - source->source.loop = &impl->loop; source->source.func = source_timer_func; source->source.data = data; source->source.fd = res; @@ -632,10 +685,13 @@ loop_update_timer(void *object, struct spa_source *source, struct timespec *value, struct timespec *interval, bool absolute) { - struct impl *impl = object; + struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); struct itimerspec its; int flags = 0, res; + spa_assert(s->impl == object); + spa_assert(source->func == source_timer_func); + spa_zero(its); if (SPA_LIKELY(value)) { its.it_value = *value; @@ -648,7 +704,7 @@ if (SPA_LIKELY(absolute)) flags |= SPA_FD_TIMER_ABSTIME; - if (SPA_UNLIKELY((res = spa_system_timerfd_settime(impl->system, source->fd, flags, &its, NULL)) < 0)) + if (SPA_UNLIKELY((res = spa_system_timerfd_settime(s->impl->system, source->fd, flags, &its, NULL)) < 0)) return res; return 0; @@ -656,14 +712,14 @@ static void source_signal_func(struct spa_source *source) { - struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); + struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); int res, signal_number = 0; - if ((res = spa_system_signalfd_read(impl->impl->system, source->fd, &signal_number)) < 0) - spa_log_warn(impl->impl->log, "%p: failed to read signal fd %d: %s", + if ((res = spa_system_signalfd_read(s->impl->system, source->fd, &signal_number)) < 0) + spa_log_warn(s->impl->log, "%p: failed to read signal fd %d: %s", source, source->fd, spa_strerror(res)); - impl->func.signal(source->data, signal_number); + s->func.signal(source->data, signal_number); } static struct spa_source *loop_add_signal(void *object, @@ -682,7 +738,6 @@ signal_number, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0) goto error_exit_free; - source->source.loop = &impl->loop; source->source.func = source_signal_func; source->source.data = data; source->source.fd = res; @@ -709,22 +764,28 @@ static void loop_destroy_source(void *object, struct spa_source *source) { - struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); + struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); + + spa_assert(s->impl == object); - spa_log_trace(impl->impl->log, "%p ", impl); + spa_log_trace(s->impl->log, "%p ", s); - spa_list_remove(&impl->link); + spa_list_remove(&s->link); - if (impl->fallback) - loop_destroy_source(impl->impl, impl->fallback); - else if (source->loop) - loop_remove_source(impl->impl, source); + if (s->fallback) + loop_destroy_source(s->impl, s->fallback); + else + remove_from_poll(s->impl, source); - if (source->fd != -1 && impl->close) { - spa_system_close(impl->impl->system, source->fd); + if (source->fd != -1 && s->close) { + spa_system_close(s->impl->system, source->fd); source->fd = -1; } - spa_list_insert(&impl->impl->destroy_list, &impl->link); + + if (!s->impl->polling) + free_source(s); + else + spa_list_insert(&s->impl->destroy_list, &s->link); } static const struct spa_loop_methods impl_loop = { @@ -792,11 +853,11 @@ spa_log_warn(impl->log, "%p: loop is entered %d times", impl, impl->enter_count); + spa_assert(!impl->polling); + spa_list_consume(source, &impl->source_list, link) loop_destroy_source(impl, &source->source); - process_destroy(impl); - spa_system_close(impl->system, impl->ack_fd); spa_system_close(impl->system, impl->poll_fd);
View file
pipewire-0.3.48.tar.gz/spa/plugins/support/node-driver.c -> pipewire-0.3.49.tar.gz/spa/plugins/support/node-driver.c
Changed
@@ -350,6 +350,13 @@ return 0; } +static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *this = user_data; + spa_loop_remove_source(this->data_loop, &this->timer_source); + return 0; +} + static int impl_clear(struct spa_handle *handle) { struct impl *this; @@ -358,7 +365,7 @@ this = (struct impl *) handle; - spa_loop_remove_source(this->data_loop, &this->timer_source); + spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this); spa_system_close(this->data_system, this->timer_source.fd); return 0;
View file
pipewire-0.3.48.tar.gz/spa/plugins/support/null-audio-sink.c -> pipewire-0.3.49.tar.gz/spa/plugins/support/null-audio-sink.c
Changed
@@ -779,6 +779,13 @@ return 0; } +static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *this = user_data; + spa_loop_remove_source(this->data_loop, &this->timer_source); + return 0; +} + static int impl_clear(struct spa_handle *handle) { struct impl *this; @@ -787,7 +794,7 @@ this = (struct impl *) handle; - spa_loop_remove_source(this->data_loop, &this->timer_source); + spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this); spa_system_close(this->data_system, this->timer_source.fd); return 0;
View file
pipewire-0.3.48.tar.gz/spa/plugins/test/fakesink.c -> pipewire-0.3.49.tar.gz/spa/plugins/test/fakesink.c
Changed
@@ -698,6 +698,13 @@ return 0; } +static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *this = user_data; + spa_loop_remove_source(this->data_loop, &this->timer_source); + return 0; +} + static int impl_clear(struct spa_handle *handle) { struct impl *this; @@ -707,7 +714,7 @@ this = (struct impl *) handle; if (this->data_loop) - spa_loop_remove_source(this->data_loop, &this->timer_source); + spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this); spa_system_close(this->data_system, this->timer_source.fd); return 0;
View file
pipewire-0.3.48.tar.gz/spa/plugins/test/fakesrc.c -> pipewire-0.3.49.tar.gz/spa/plugins/test/fakesrc.c
Changed
@@ -731,6 +731,13 @@ return 0; } +static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *this = user_data; + spa_loop_remove_source(this->data_loop, &this->timer_source); + return 0; +} + static int impl_clear(struct spa_handle *handle) { struct impl *this; @@ -740,7 +747,7 @@ this = (struct impl *) handle; if (this->data_loop) - spa_loop_remove_source(this->data_loop, &this->timer_source); + spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this); spa_system_close(this->data_system, this->timer_source.fd); return 0;
View file
pipewire-0.3.48.tar.gz/spa/plugins/videotestsrc/videotestsrc.c -> pipewire-0.3.49.tar.gz/spa/plugins/videotestsrc/videotestsrc.c
Changed
@@ -838,6 +838,13 @@ return 0; } +static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *this = user_data; + spa_loop_remove_source(this->data_loop, &this->timer_source); + return 0; +} + static int impl_clear(struct spa_handle *handle) { struct impl *this; @@ -847,7 +854,7 @@ this = (struct impl *) handle; if (this->data_loop) - spa_loop_remove_source(this->data_loop, &this->timer_source); + spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this); spa_system_close(this->data_system, this->timer_source.fd); return 0;
View file
pipewire-0.3.48.tar.gz/spa/plugins/vulkan/vulkan-compute-source.c -> pipewire-0.3.49.tar.gz/spa/plugins/vulkan/vulkan-compute-source.c
Changed
@@ -854,6 +854,13 @@ return 0; } +static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *this = user_data; + spa_loop_remove_source(this->data_loop, &this->timer_source); + return 0; +} + static int impl_clear(struct spa_handle *handle) { struct impl *this; @@ -863,7 +870,7 @@ this = (struct impl *) handle; if (this->data_loop) - spa_loop_remove_source(this->data_loop, &this->timer_source); + spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this); spa_system_close(this->data_system, this->timer_source.fd); return 0;
View file
pipewire-0.3.48.tar.gz/src/daemon/client-rt.conf.in -> pipewire-0.3.49.tar.gz/src/daemon/client-rt.conf.in
Changed
@@ -81,12 +81,13 @@ #node.latency = 1024/48000 #node.autoconnect = true #resample.quality = 4 - #channelmix.normalize = true + #channelmix.normalize = false #channelmix.mix-lfe = true - #channelmix.upmix = false - #channelmix.lfe-cutoff = 0 - #channelmix.fc-cutoff = 0 + #channelmix.upmix = true + #channelmix.upmix-method = simple # none, psd + #channelmix.lfe-cutoff = 120 + #channelmix.fc-cutoff = 6000 #channelmix.rear-delay = 12.0 - #channelmix.stereo-widen = 0.0 + #channelmix.stereo-widen = 0.1 #channelmix.hilbert-taps = 0 }
View file
pipewire-0.3.48.tar.gz/src/daemon/client.conf.in -> pipewire-0.3.49.tar.gz/src/daemon/client.conf.in
Changed
@@ -71,12 +71,13 @@ #node.latency = 1024/48000 #node.autoconnect = true #resample.quality = 4 - #channelmix.normalize = true + #channelmix.normalize = false #channelmix.mix-lfe = false - #channelmix.upmix = false - #channelmix.lfe-cutoff = 0 - #channelmix.fc-cutoff = 0 + #channelmix.upmix = true + #channelmix.upmix-method = simple # none, psd + #channelmix.lfe-cutoff = 120 + #channelmix.fc-cutoff = 6000 #channelmix.rear-delay = 12.0 - #channelmix.stereo-widen = 0.0 + #channelmix.stereo-widen = 0.1 #channelmix.hilbert-taps = 0 }
View file
pipewire-0.3.48.tar.gz/src/daemon/minimal.conf.in -> pipewire-0.3.49.tar.gz/src/daemon/minimal.conf.in
Changed
@@ -202,13 +202,14 @@ #resample.quality = 4 resample.disable = true #monitor.channel-volumes = false - #channelmix.normalize = true + #channelmix.normalize = false #channelmix.mix-lfe = false - #channelmix.upmix = false - #channelmix.lfe-cutoff = 0 - #channelmix.fc-cutoff = 0 + #channelmix.upmix = true + #channelmix.upmix-method = simple # none, psd + #channelmix.lfe-cutoff = 120 + #channelmix.fc-cutoff = 6000 #channelmix.rear-delay = 12.0 - #channelmix.stereo-widen = 0.0 + #channelmix.stereo-widen = 0.1 #channelmix.hilbert-taps = 0 channelmix.disable = true #node.param.Props = { @@ -261,13 +262,14 @@ #audio.position = "FL,FR" #resample.quality = 4 resample.disable = true - #channelmix.normalize = true + #channelmix.normalize = false #channelmix.mix-lfe = false - #channelmix.upmix = false - #channelmix.lfe-cutoff = 0 - #channelmix.fc-cutoff = 0 + #channelmix.upmix = true + #channelmix.upmix-method = simple # none, psd + #channelmix.lfe-cutoff = 120 + #channelmix.fc-cutoff = 6000 #channelmix.rear-delay = 12.0 - #channelmix.stereo-widen = 0.0 + #channelmix.stereo-widen = 0.1 #channelmix.hilbert-taps = 0 channelmix.disable = true #node.param.Props = {
View file
pipewire-0.3.48.tar.gz/src/daemon/pipewire-pulse.conf.in -> pipewire-0.3.49.tar.gz/src/daemon/pipewire-pulse.conf.in
Changed
@@ -40,32 +40,8 @@ { name = libpipewire-module-protocol-pulse args = { - # the addresses this server listens on - server.address = [ - "unix:native" - #"unix:/tmp/something" # absolute paths may be used - #"tcp:4713" # IPv4 and IPv6 on all addresses - #"tcp:[::]:9999" # IPv6 on all addresses - #"tcp:127.0.0.1:8888" # IPv4 on a single address - # - #{ address = "tcp:4713" # address - # max-clients = 64 # maximum number of clients - # listen-backlog = 32 # backlog in the server listen queue - # client.access = "restricted" # permissions for clients - #} - ] - #pulse.min.req = 256/48000 # 5ms - #pulse.default.req = 960/48000 # 20 milliseconds - #pulse.min.frag = 256/48000 # 5ms - #pulse.default.frag = 96000/48000 # 2 seconds - #pulse.default.tlength = 96000/48000 # 2 seconds - #pulse.min.quantum = 256/48000 # 5ms - #pulse.default.format = F32 - #pulse.default.position = [ FL FR ] - # These overrides are only applied when running in a vm. - vm.overrides = { - pulse.min.quantum = 1024/48000 # 22ms - } + # contents of pulse.properties can also be placed here + # to have config per server. } } ] @@ -81,16 +57,46 @@ #node.latency = 1024/48000 #node.autoconnect = true #resample.quality = 4 - #channelmix.normalize = true + #channelmix.normalize = false #channelmix.mix-lfe = false - #channelmix.upmix = false - #channelmix.lfe-cutoff = 0 - #channelmix.fc-cutoff = 0 + #channelmix.upmix = true + #channelmix.upmix-method = simple # none, psd + #channelmix.lfe-cutoff = 120 + #channelmix.fc-cutoff = 6000 #channelmix.rear-delay = 12.0 - #channelmix.stereo-widen = 0.0 + #channelmix.stereo-widen = 0.1 #channelmix.hilbert-taps = 0 } +pulse.properties = { + # the addresses this server listens on + server.address = [ + "unix:native" + #"unix:/tmp/something" # absolute paths may be used + #"tcp:4713" # IPv4 and IPv6 on all addresses + #"tcp:[::]:9999" # IPv6 on all addresses + #"tcp:127.0.0.1:8888" # IPv4 on a single address + # + #{ address = "tcp:4713" # address + # max-clients = 64 # maximum number of clients + # listen-backlog = 32 # backlog in the server listen queue + # client.access = "restricted" # permissions for clients + #} + ] + #pulse.min.req = 256/48000 # 5ms + #pulse.default.req = 960/48000 # 20 milliseconds + #pulse.min.frag = 256/48000 # 5ms + #pulse.default.frag = 96000/48000 # 2 seconds + #pulse.default.tlength = 96000/48000 # 2 seconds + #pulse.min.quantum = 256/48000 # 5ms + #pulse.default.format = F32 + #pulse.default.position = [ FL FR ] + # These overrides are only applied when running in a vm. + vm.overrides = { + pulse.min.quantum = 1024/48000 # 22ms + } +} + # client/stream specific properties pulse.rules = [ {
View file
pipewire-0.3.48.tar.gz/src/daemon/pipewire.c -> pipewire-0.3.49.tar.gz/src/daemon/pipewire.c
Changed
@@ -26,6 +26,7 @@ #include <signal.h> #include <getopt.h> #include <libgen.h> +#include <locale.h> #include <spa/utils/result.h> #include <pipewire/pipewire.h> @@ -74,6 +75,7 @@ snprintf(path, sizeof(path), "%s.conf", argv[0]); config_name = basename(path); + setlocale(LC_ALL, ""); pw_init(&argc, &argv); while ((c = getopt_long(argc, argv, "hVc:v", long_options, NULL)) != -1) {
View file
pipewire-0.3.48.tar.gz/src/gst/gstpipewiredeviceprovider.c -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiredeviceprovider.c
Changed
@@ -44,6 +44,7 @@ enum { PROP_ID = 1, + PROP_SERIAL, }; static GstElement * @@ -51,12 +52,16 @@ { GstPipeWireDevice *pipewire_dev = GST_PIPEWIRE_DEVICE (device); GstElement *elem; - gchar *str; + gchar *id_str, *serial_str; elem = gst_element_factory_make (pipewire_dev->element, name); - str = g_strdup_printf ("%u", pipewire_dev->id); - g_object_set (elem, "path", str, NULL); - g_free (str); + + /* XXX: eventually only add target-object here */ + id_str = g_strdup_printf ("%u", pipewire_dev->id); + serial_str = g_strdup_printf ("%"PRIu64, pipewire_dev->serial); + g_object_set (elem, "path", id_str, "target-object", serial_str, NULL); + g_free (id_str); + g_free (serial_str); return elem; } @@ -65,7 +70,7 @@ gst_pipewire_device_reconfigure_element (GstDevice * device, GstElement * element) { GstPipeWireDevice *pipewire_dev = GST_PIPEWIRE_DEVICE (device); - gchar *str; + gchar *id_str, *serial_str; if (spa_streq(pipewire_dev->element, "pipewiresrc")) { if (!GST_IS_PIPEWIRE_SRC (element)) @@ -77,9 +82,12 @@ g_assert_not_reached (); } - str = g_strdup_printf ("%u", pipewire_dev->id); - g_object_set (element, "path", str, NULL); - g_free (str); + /* XXX: eventually only add target-object here */ + id_str = g_strdup_printf ("%u", pipewire_dev->id); + serial_str = g_strdup_printf ("%"PRIu64, pipewire_dev->serial); + g_object_set (element, "path", id_str, "target-object", serial_str, NULL); + g_free (id_str); + g_free (serial_str); return TRUE; } @@ -97,6 +105,9 @@ case PROP_ID: g_value_set_uint (value, device->id); break; + case PROP_SERIAL: + g_value_set_uint64 (value, device->serial); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -115,6 +126,9 @@ case PROP_ID: device->id = g_value_get_uint (value); break; + case PROP_SERIAL: + device->serial = g_value_get_uint64 (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -144,6 +158,11 @@ g_param_spec_uint ("id", "Id", "The internal id of the PipeWire device", 0, G_MAXUINT32, SPA_ID_INVALID, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_SERIAL, + g_param_spec_uint64 ("serial", "Serial", + "The internal serial of the PipeWire device", 0, G_MAXUINT64, SPA_ID_INVALID, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void @@ -176,6 +195,7 @@ struct pw_node *proxy; struct spa_hook proxy_listener; uint32_t id; + uint64_t serial; struct spa_hook node_listener; struct pw_node_info *info; GstCaps *caps; @@ -187,6 +207,7 @@ struct pw_port *proxy; struct spa_hook proxy_listener; uint32_t id; + uint64_t serial; struct spa_hook port_listener; }; @@ -236,9 +257,10 @@ gstdev = g_object_new (GST_TYPE_PIPEWIRE_DEVICE, "display-name", name, "caps", data->caps, "device-class", klass, - "id", data->id, "properties", props, NULL); + "id", data->id, "serial", data->serial, "properties", props, NULL); gstdev->id = data->id; + gstdev->serial = data->serial; gstdev->type = type; gstdev->element = element; if (props) @@ -476,6 +498,8 @@ nd->self = self; nd->proxy = node; nd->id = id; + if (!props || !spa_atou64(spa_dict_lookup(props, PW_KEY_OBJECT_SERIAL), &nd->serial, 0)) + nd->serial = SPA_ID_INVALID; spa_list_append(&rd->nodes, &nd->link); pw_node_add_listener(node, &nd->node_listener, &node_events, nd); pw_proxy_add_listener((struct pw_proxy*)node, &nd->proxy_listener, &proxy_node_events, nd); @@ -500,6 +524,8 @@ pd->node_data = nd; pd->proxy = port; pd->id = id; + if (!props || !spa_atou64(spa_dict_lookup(props, PW_KEY_OBJECT_SERIAL), &pd->serial, 0)) + pd->serial = SPA_ID_INVALID; pw_port_add_listener(port, &pd->port_listener, &port_events, pd); pw_proxy_add_listener((struct pw_proxy*)port, &pd->proxy_listener, &proxy_port_events, pd); resync(self);
View file
pipewire-0.3.48.tar.gz/src/gst/gstpipewiredeviceprovider.h -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiredeviceprovider.h
Changed
@@ -56,6 +56,7 @@ GstPipeWireDeviceType type; uint32_t id; + uint64_t serial; const gchar *element; };
View file
pipewire-0.3.48.tar.gz/src/gst/gstpipewiresink.c -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiresink.c
Changed
@@ -60,6 +60,7 @@ { PROP_0, PROP_PATH, + PROP_TARGET_OBJECT, PROP_CLIENT_NAME, PROP_STREAM_PROPERTIES, PROP_MODE, @@ -124,6 +125,7 @@ if (pwsink->properties) gst_structure_free (pwsink->properties); g_free (pwsink->path); + g_free (pwsink->target_object); g_free (pwsink->client_name); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -160,6 +162,16 @@ "The sink path to connect to (NULL = default)", NULL, G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_DEPRECATED)); + + g_object_class_install_property (gobject_class, + PROP_TARGET_OBJECT, + g_param_spec_string ("target-object", + "Target object", + "The sink name/serial to connect to (NULL = default)", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, @@ -339,6 +351,11 @@ pwsink->path = g_value_dup_string (value); break; + case PROP_TARGET_OBJECT: + g_free (pwsink->target_object); + pwsink->target_object = g_value_dup_string (value); + break; + case PROP_CLIENT_NAME: g_free (pwsink->client_name); pwsink->client_name = g_value_dup_string (value); @@ -376,6 +393,10 @@ g_value_set_string (value, pwsink->path); break; + case PROP_TARGET_OBJECT: + g_value_set_string (value, pwsink->target_object); + break; + case PROP_CLIENT_NAME: g_value_set_string (value, pwsink->client_name); break; @@ -522,15 +543,37 @@ if (state == PW_STREAM_STATE_UNCONNECTED) { enum pw_stream_flags flags = 0; + uint32_t target_id; if (pwsink->mode != GST_PIPEWIRE_SINK_MODE_PROVIDE) flags |= PW_STREAM_FLAG_AUTOCONNECT; else flags |= PW_STREAM_FLAG_DRIVER; + target_id = pwsink->path ? (uint32_t)atoi(pwsink->path) : PW_ID_ANY; + + if (pwsink->target_object) { + struct spa_dict_item items[2] = { + SPA_DICT_ITEM_INIT(PW_KEY_TARGET_OBJECT, pwsink->target_object), + SPA_DICT_ITEM_INIT(PW_KEY_NODE_TARGET, NULL), + }; + struct spa_dict dict = SPA_DICT_INIT_ARRAY(items); + uint64_t serial; + + /* If target.object is a name, set it also to node.target */ + if (spa_atou64(pwsink->target_object, &serial, 0)) { + dict.n_items = 1; + } else { + target_id = PW_ID_ANY; + items[1].value = pwsink->target_object; + } + + pw_stream_update_properties (pwsink->stream, &dict); + } + pw_stream_connect (pwsink->stream, PW_DIRECTION_OUTPUT, - pwsink->path ? (uint32_t)atoi(pwsink->path) : PW_ID_ANY, + target_id, flags, (const struct spa_pod **) possible->pdata, possible->len);
View file
pipewire-0.3.48.tar.gz/src/gst/gstpipewiresink.h -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiresink.h
Changed
@@ -78,6 +78,7 @@ /*< private >*/ gchar *path; + gchar *target_object; gchar *client_name; int fd;
View file
pipewire-0.3.48.tar.gz/src/gst/gstpipewiresrc.c -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiresrc.c
Changed
@@ -67,6 +67,7 @@ { PROP_0, PROP_PATH, + PROP_TARGET_OBJECT, PROP_CLIENT_NAME, PROP_STREAM_PROPERTIES, PROP_ALWAYS_COPY, @@ -116,6 +117,11 @@ pwsrc->path = g_value_dup_string (value); break; + case PROP_TARGET_OBJECT: + g_free (pwsrc->target_object); + pwsrc->target_object = g_value_dup_string (value); + break; + case PROP_CLIENT_NAME: g_free (pwsrc->client_name); pwsrc->client_name = g_value_dup_string (value); @@ -169,6 +175,10 @@ g_value_set_string (value, pwsrc->path); break; + case PROP_TARGET_OBJECT: + g_value_set_string (value, pwsrc->target_object); + break; + case PROP_CLIENT_NAME: g_value_set_string (value, pwsrc->client_name); break; @@ -244,6 +254,7 @@ if (pwsrc->clock) gst_object_unref (pwsrc->clock); g_free (pwsrc->path); + g_free (pwsrc->target_object); g_free (pwsrc->client_name); g_object_unref(pwsrc->pool); @@ -274,6 +285,16 @@ "The source path to connect to (NULL = default)", NULL, G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_DEPRECATED)); + + g_object_class_install_property (gobject_class, + PROP_TARGET_OBJECT, + g_param_spec_string ("target-object", + "Target object", + "The source name/serial to connect to (NULL = default)", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, @@ -675,6 +696,7 @@ GPtrArray *possible; const char *error = NULL; struct timespec abstime; + uint32_t target_id; /* first see what is possible on our source pad */ thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL); @@ -727,11 +749,33 @@ } } - GST_DEBUG_OBJECT (basesrc, "connect capture with path %s", pwsrc->path); + target_id = pwsrc->path ? (uint32_t)atoi(pwsrc->path) : PW_ID_ANY; + + if (pwsrc->target_object) { + struct spa_dict_item items[2] = { + SPA_DICT_ITEM_INIT(PW_KEY_TARGET_OBJECT, pwsrc->target_object), + SPA_DICT_ITEM_INIT(PW_KEY_NODE_TARGET, NULL), + }; + struct spa_dict dict = SPA_DICT_INIT_ARRAY(items); + uint64_t serial; + + /* If target.object is a name, set it also to node.target */ + if (spa_atou64(pwsrc->target_object, &serial, 0)) { + dict.n_items = 1; + } else { + target_id = PW_ID_ANY; + items[1].value = pwsrc->target_object; + } + + pw_stream_update_properties (pwsrc->stream, &dict); + } + + GST_DEBUG_OBJECT (basesrc, "connect capture with path %s, target-object %s", + pwsrc->path, pwsrc->target_object); pwsrc->negotiated = FALSE; pw_stream_connect (pwsrc->stream, PW_DIRECTION_INPUT, - pwsrc->path ? (uint32_t)atoi(pwsrc->path) : PW_ID_ANY, + target_id, PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_DONT_RECONNECT, (const struct spa_pod **)possible->pdata, possible->len);
View file
pipewire-0.3.48.tar.gz/src/gst/gstpipewiresrc.h -> pipewire-0.3.49.tar.gz/src/gst/gstpipewiresrc.h
Changed
@@ -60,6 +60,7 @@ /*< private >*/ gchar *path; + gchar *target_object; gchar *client_name; gboolean always_copy; gint min_buffers;
View file
pipewire-0.3.48.tar.gz/src/modules/meson.build -> pipewire-0.3.49.tar.gz/src/modules/meson.build
Changed
@@ -131,8 +131,6 @@ dependencies : [spa_dep, mathlib, dl_lib, pipewire_dep], ) -build_module_rt = dbus_dep.found() -if build_module_rt pipewire_module_rt = shared_library('pipewire-module-rt', [ 'module-rt.c' ], include_directories : [configinc], install : true, @@ -140,6 +138,9 @@ install_rpath: modules_install_dir, dependencies : [dbus_dep, mathlib, dl_lib, pipewire_dep], ) + +build_module_rtkit = dbus_dep.found() +if build_module_rtkit # TODO: This serves as a temporary alias to prevent breaking existing setups # while `module-rtkit` is being migrated to `module-rt` pipewire_module_rtkit = shared_library('pipewire-module-rtkit', [ 'module-rt.c' ], @@ -150,7 +151,7 @@ dependencies : [dbus_dep, mathlib, dl_lib, pipewire_dep], ) endif -summary({'rt': build_module_rt}, bool_yn: true, section: 'Optional Modules') +summary({'rt': '@0@ RTKit'.format(build_module_rtkit ? 'with' : 'without')}, section: 'Optional Modules') build_module_portal = dbus_dep.found() if build_module_portal
View file
pipewire-0.3.48.tar.gz/src/modules/module-client-node/remote-node.c -> pipewire-0.3.49.tar.gz/src/modules/module-client-node/remote-node.c
Changed
@@ -843,9 +843,9 @@ { struct link *link = user_data; struct spa_system *data_system = link->data->context->data_system; - struct timespec ts; + struct timespec ts = { 0, 0 }; - pw_log_trace("link %p: signal", link); + pw_log_trace_fp("link %p: signal", link); spa_system_clock_gettime(data_system, CLOCK_MONOTONIC, &ts); link->target.activation->status = PW_NODE_ACTIVATION_TRIGGERED;
View file
pipewire-0.3.48.tar.gz/src/modules/module-echo-cancel.c -> pipewire-0.3.49.tar.gz/src/modules/module-echo-cancel.c
Changed
@@ -71,7 +71,7 @@ * - `source.props = {}`: properties to be passed to the source stream * - `sink.props = {}`: properties to be passed to the sink stream * - `library.name = <str>`: the echo cancellation library Currently supported: - * `aec/libspa-aec-exaudio`. Leave unset to use the default method (`aec/libspa-aec-exaudio`). + * `aec/libspa-aec-webrtc`. Leave unset to use the default method (`aec/libspa-aec-webrtc`). * - `aec.args = <str>`: arguments to pass to the echo cancellation method * * ## General options @@ -96,7 +96,7 @@ * context.modules = [ * { name = libpipewire-module-echo-cancel * args = { - * # library.name = aec/libspa-aec-exaudio + * # library.name = aec/libspa-aec-webrtc * # node.latency = 1024/48000 * source.props = { * node.name = "Echo Cancellation Source" @@ -139,7 +139,7 @@ "[ audio.channels=<number of channels> ] " "[ audio.position=<channel map> ] " "[ buffer.max_size=<max buffer size in ms> ] " - "[ buffer.play_delay=<play delay in ms> ] " + "[ buffer.play_delay=<delay as fraction> ] " "[ library.name =<library name> ] " "[ aec.args=<aec arguments> ] " "[ source.props=<properties> ] " @@ -731,7 +731,7 @@ return res; impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000; - impl->play_ringsize = sizeof(float) * (impl->max_buffer_size + impl->buffer_delay) * impl->info.rate / 1000; + impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->info.rate / 1000) + impl->buffer_delay); impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000; for (i = 0; i < impl->info.channels; i++) { impl->rec_buffer[i] = malloc(impl->rec_ringsize); @@ -744,9 +744,9 @@ spa_ringbuffer_init(&impl->out_ring); spa_ringbuffer_get_write_index(&impl->play_ring, &index); - spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay) * impl->info.rate / 1000)); + spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay))); spa_ringbuffer_get_read_index(&impl->play_ring, &index); - spa_ringbuffer_read_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay) * impl->info.rate / 1000)); + spa_ringbuffer_read_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay))); return 0; } @@ -1054,7 +1054,23 @@ copy_props(impl, props, PW_KEY_NODE_LATENCY); impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS); - impl->buffer_delay = pw_properties_get_uint32(props,"buffer.play_delay", DELAY_MS); + + if ((str = pw_properties_get(props, "buffer.play_delay")) != NULL) { + int req_num, req_denom; + if (sscanf(str, "%u/%u", &req_num, &req_denom) == 2) { + if (req_denom != 0) { + impl->buffer_delay = (impl->info.rate*req_num)/req_denom; + } else { + impl->buffer_delay = DELAY_MS * impl->info.rate / 1000; + pw_log_warn("Sample rate for buffer.play_delay is 0 using default"); + } + } else { + impl->buffer_delay = DELAY_MS * impl->info.rate / 1000; + pw_log_warn("Wrong value/format for buffer.play_delay using default"); + } + } else { + impl->buffer_delay = DELAY_MS * impl->info.rate / 1000; + } pw_properties_free(props);
View file
pipewire-0.3.48.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.49.tar.gz/src/modules/module-filter-chain.c
Changed
@@ -408,14 +408,36 @@ SPA_PROP_INFO_name, SPA_POD_String(name), 0); spa_pod_builder_prop(b, SPA_PROP_INFO_type, 0); - if (min == max) { - spa_pod_builder_float(b, def); + if (p->hint & FC_HINT_BOOLEAN) { + if (min == max) { + spa_pod_builder_bool(b, def <= 0.0 ? false : true); + } else { + spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Enum, 0); + spa_pod_builder_bool(b, def <= 0.0 ? false : true); + spa_pod_builder_bool(b, false); + spa_pod_builder_bool(b, true); + spa_pod_builder_pop(b, &f[1]); + } + } else if (p->hint & FC_HINT_INTEGER) { + if (min == max) { + spa_pod_builder_int(b, def); + } else { + spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Range, 0); + spa_pod_builder_int(b, def); + spa_pod_builder_int(b, min); + spa_pod_builder_int(b, max); + spa_pod_builder_pop(b, &f[1]); + } } else { - spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Range, 0); - spa_pod_builder_float(b, def); - spa_pod_builder_float(b, min); - spa_pod_builder_float(b, max); - spa_pod_builder_pop(b, &f[1]); + if (min == max) { + spa_pod_builder_float(b, def); + } else { + spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Range, 0); + spa_pod_builder_float(b, def); + spa_pod_builder_float(b, min); + spa_pod_builder_float(b, max); + spa_pod_builder_pop(b, &f[1]); + } } spa_pod_builder_prop(b, SPA_PROP_INFO_params, 0); spa_pod_builder_bool(b, true); @@ -446,7 +468,13 @@ snprintf(name, sizeof(name), "%s", p->name); spa_pod_builder_string(b, name); - spa_pod_builder_float(b, port->control_data); + if (p->hint & FC_HINT_BOOLEAN) { + spa_pod_builder_bool(b, port->control_data <= 0.0 ? false : true); + } else if (p->hint & FC_HINT_INTEGER) { + spa_pod_builder_int(b, port->control_data); + } else { + spa_pod_builder_float(b, port->control_data); + } } spa_pod_builder_pop(b, &f[1]); return spa_pod_builder_pop(b, &f[0]); @@ -487,12 +515,23 @@ while (true) { const char *name; float value, *val = NULL; + bool bool_val; + int32_t int_val; if (spa_pod_parser_get_string(&prs, &name) < 0) break; - if (spa_pod_parser_get_float(&prs, &value) >= 0) + if (spa_pod_parser_get_float(&prs, &value) >= 0) { val = &value; - + } else if (spa_pod_parser_get_int(&prs, &int_val) >= 0) { + value = int_val; + val = &value; + } else if (spa_pod_parser_get_bool(&prs, &bool_val) >= 0) { + value = bool_val ? 1.0f : 0.0f; + val = &value; + } else { + struct spa_pod *pod; + spa_pod_parser_get_pod(&prs, &pod); + } changed += set_control_value(def_node, name, val); } return changed; @@ -524,13 +563,14 @@ } if (changed > 0) { uint8_t buffer[1024]; - struct spa_pod_builder b; + struct spa_pod_dynamic_builder b; const struct spa_pod *params[1]; - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - params[0] = get_props_param(graph, &b); + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); + params[0] = get_props_param(graph, &b.b); pw_stream_update_params(impl->capture, params, 1); + spa_pod_dynamic_builder_clean(&b); } }
View file
pipewire-0.3.48.tar.gz/src/modules/module-filter-chain/plugin.h -> pipewire-0.3.49.tar.gz/src/modules/module-filter-chain/plugin.h
Changed
@@ -47,7 +47,9 @@ #define FC_PORT_AUDIO (1ULL << 3) uint64_t flags; +#define FC_HINT_BOOLEAN (1ULL << 2) #define FC_HINT_SAMPLE_RATE (1ULL << 3) +#define FC_HINT_INTEGER (1ULL << 5) uint64_t hint; float def; float min;
View file
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/extensions/ext-stream-restore.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/extensions/ext-stream-restore.c
Changed
@@ -235,7 +235,7 @@ FILE *f; char *ptr; size_t size; - char key[1024]; + char key[1024], buf[128]; spa_zero(map); spa_zero(vol); @@ -260,7 +260,8 @@ if (vol.channels > 0) { fprintf(f, ", \"volumes\": ["); for (i = 0; i < vol.channels; i++) - fprintf(f, "%s%f", (i == 0 ? " ":", "), vol.values[i]); + fprintf(f, "%s%s", (i == 0 ? " ":", "), + spa_json_format_float(buf, sizeof(buf), vol.values[i])); fprintf(f, " ]"); } if (map.channels > 0) {
View file
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/manager.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/manager.c
Changed
@@ -812,6 +812,8 @@ struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this); struct object *o; + spa_hook_list_clean(&m->hooks); + spa_hook_remove(&m->core_listener); spa_list_consume(o, &m->this.object_list, this.link)
View file
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/module.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/module.c
Changed
@@ -67,6 +67,7 @@ if (module == NULL) return NULL; + module->index = SPA_ID_INVALID; module->impl = impl; module->methods = methods; spa_hook_list_init(&module->listener_list); @@ -102,8 +103,10 @@ if (module->index != SPA_ID_INVALID) pw_map_remove(&impl->modules, module->index & MODULE_INDEX_MASK); + if (module->unloading) + pw_work_queue_cancel(impl->work_queue, module, SPA_ID_INVALID); + spa_hook_list_clean(&module->listener_list); - pw_work_queue_cancel(impl->work_queue, module, SPA_ID_INVALID); pw_properties_free(module->props); free((char*)module->name);
View file
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/modules/module-combine-sink.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/modules/module-combine-sink.c
Changed
@@ -166,8 +166,7 @@ pw_stream_queue_buffer(data->streams[i].stream, out); } - if (in != NULL) - pw_stream_queue_buffer(data->sink, in); + pw_stream_queue_buffer(data->sink, in); } static void check_initialized(struct module_combine_sink_data *data)
View file
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-sink.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-sink.c
Changed
@@ -78,7 +78,7 @@ fprintf(f, "{"); pw_properties_serialize_dict(f, &data->roc_props->dict, 0); - fprintf(f, " } sink.props = {"); + fprintf(f, " sink.props = {"); pw_properties_serialize_dict(f, &data->sink_props->dict, 0); fprintf(f, " } }"); fclose(f); @@ -125,6 +125,7 @@ { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" }, { PW_KEY_MODULE_DESCRIPTION, "roc sink" }, { PW_KEY_MODULE_USAGE, "sink_name=<name for the sink> " + "sink_properties=<properties for the sink> " "local_ip=<local sender ip> " "remote_ip=<remote receiver ip> " "remote_source_port=<remote receiver port for source packets> " @@ -156,6 +157,10 @@ pw_properties_set(sink_props, PW_KEY_NODE_NAME, str); pw_properties_set(props, "sink_name", NULL); } + if ((str = pw_properties_get(props, "sink_properties")) != NULL) { + module_args_add_props(sink_props, str); + pw_properties_set(props, "sink_properties", NULL); + } if ((str = pw_properties_get(props, PW_KEY_MEDIA_CLASS)) == NULL) { pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
View file
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-source.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-source.c
Changed
@@ -78,7 +78,7 @@ fprintf(f, "{"); pw_properties_serialize_dict(f, &data->roc_props->dict, 0); - fprintf(f, " } source.props = {"); + fprintf(f, " source.props = {"); pw_properties_serialize_dict(f, &data->source_props->dict, 0); fprintf(f, " } }"); fclose(f); @@ -125,6 +125,7 @@ { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" }, { PW_KEY_MODULE_DESCRIPTION, "roc source" }, { PW_KEY_MODULE_USAGE, "source_name=<name for the source> " + "source_properties=<properties for the source> " "resampler_profile=<empty>|disable|high|medium|low " "sess_latency_msec=<target network latency in milliseconds> " "local_ip=<local receiver ip> " @@ -158,6 +159,10 @@ pw_properties_set(source_props, PW_KEY_NODE_NAME, str); pw_properties_set(props, "source_name", NULL); } + if ((str = pw_properties_get(props, "source_properties")) != NULL) { + module_args_add_props(source_props, str); + pw_properties_set(props, "source_properties", NULL); + } if ((str = pw_properties_get(props, PW_KEY_MEDIA_CLASS)) == NULL) { pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Source");
View file
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/pulse-server.c
Changed
@@ -1822,7 +1822,7 @@ n_valid_formats++; } } - if (n_params < MAX_FORMATS && + else if (n_params < MAX_FORMATS && (params[n_params] = format_build_param(&b, SPA_PARAM_EnumFormat, &ss, ss.channels > 0 ? &map : NULL)) != NULL) { @@ -5350,6 +5350,8 @@ support = pw_context_get_support(context, &n_support); cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); + pw_context_conf_update_props(context, "pulse.properties", props); + if ((str = pw_properties_get(props, "vm.overrides")) != NULL) { if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE) pw_properties_update_string(props, str, strlen(str));
View file
pipewire-0.3.48.tar.gz/src/modules/module-protocol-pulse/sample-play.c -> pipewire-0.3.49.tar.gz/src/modules/module-protocol-pulse/sample-play.c
Changed
@@ -211,6 +211,8 @@ if (p->stream) pw_stream_destroy(p->stream); + spa_hook_list_clean(&p->hooks); + free(p); }
View file
pipewire-0.3.48.tar.gz/src/modules/module-rt.c -> pipewire-0.3.49.tar.gz/src/modules/module-rt.c
Changed
@@ -1,6 +1,6 @@ /* PipeWire * - * Copyright © 2018 Wim Taymans + * Copyright © 2022 Wim Taymans * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -21,6 +21,30 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +/*** + Copyright 2009 Lennart Poettering + Copyright 2010 David Henningsson <diwic@ubuntu.com> + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ #include <stdlib.h> #include <stdbool.h> @@ -35,16 +59,21 @@ #include <unistd.h> #include <pthread.h> #include <sys/resource.h> +#include <sys/syscall.h> #include "config.h" -#include <spa/support/dbus.h> #include <spa/utils/result.h> #include <spa/utils/string.h> #include <pipewire/impl.h> #include <pipewire/thread.h> +#ifdef HAVE_DBUS +#include <spa/support/dbus.h> +#include <dbus/dbus.h> +#endif + /** \page page_module_rt PipeWire Module: RT * * The `rt` module uses the operating system's scheduler to enable realtime @@ -87,7 +116,15 @@ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; -struct pw_rtkit_bus; +#ifdef HAVE_DBUS +#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1" +#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1" + +/** \cond */ +struct pw_rtkit_bus { + DBusConnection *bus; +}; +/** \endcond */ struct thread { struct impl *impl; @@ -97,6 +134,7 @@ void *(*start)(void*); void *arg; }; +#endif /* HAVE_DBUS */ struct impl { struct pw_context *context; @@ -110,6 +148,7 @@ struct spa_hook module_listener; +#ifdef HAVE_DBUS bool use_rtkit; struct pw_rtkit_bus *system_bus; @@ -119,52 +158,29 @@ pthread_mutex_t lock; pthread_cond_t cond; struct spa_list threads_list; +#endif }; -/*** - Copyright 2009 Lennart Poettering - Copyright 2010 David Henningsson <diwic@ubuntu.com> - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -***/ - -#include <dbus/dbus.h> - -#include "config.h" - -#include <sys/syscall.h> - -#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1" -#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1" - #ifndef RLIMIT_RTTIME #define RLIMIT_RTTIME 15 #endif -/** \cond */ -struct pw_rtkit_bus { - DBusConnection *bus; -}; -/** \endcond */ +static pid_t _gettid(void) +{ +#if defined(HAVE_GETTID) + return (pid_t) gettid(); +#elif defined(__linux__) + return syscall(SYS_gettid); +#elif defined(__FreeBSD__) + long pid; + thr_self(&pid); + return (pid_t)pid; +#else +#error "No gettid impl" +#endif +} +#ifdef HAVE_DBUS struct pw_rtkit_bus *pw_rtkit_bus_get_system(void) { struct pw_rtkit_bus *bus; @@ -204,21 +220,6 @@ free(system_bus); } -static pid_t _gettid(void) -{ -#if defined(HAVE_GETTID) - return (pid_t) gettid(); -#elif defined(__linux__) - return syscall(SYS_gettid); -#elif defined(__FreeBSD__) - long pid; - thr_self(&pid); - return (pid_t)pid; -#else -#error "No gettid impl" -#endif -} - static int translate_error(const char *name) { pw_log_warn("RTKit error: %s", name); @@ -458,6 +459,7 @@ return ret; } +#endif /* HAVE_DBUS */ static void module_destroy(void *data) { @@ -466,8 +468,11 @@ pw_thread_utils_set(NULL); spa_hook_remove(&impl->module_listener); +#ifdef HAVE_DBUS if (impl->system_bus) pw_rtkit_bus_free(impl->system_bus); +#endif + free(impl); } @@ -480,7 +485,7 @@ * Check if the current user has permissions to use realtime scheduling at the * specified priority. */ -static bool check_realtime_priviliges(rlim_t priority) +static bool check_realtime_privileges(rlim_t priority) { int old_policy; struct sched_param old_sched_params; @@ -516,28 +521,33 @@ } } +static int sched_set_nice(int nice_level) +{ + if (setpriority(PRIO_PROCESS, _gettid(), nice_level) == 0) + return 0; + else + return -errno; +} + static int set_nice(struct impl *impl, int nice_level) { int res = 0; - pid_t tid; - if (impl->use_rtkit) { - if ((res = pw_rtkit_make_high_priority(impl->system_bus, 0, nice_level)) < 0) { - pw_log_warn("could not set nice-level to %d: %s", - nice_level, spa_strerror(res)); - } else { - pw_log_info("main thread nice level set to %d", nice_level); - } +#ifdef HAVE_DBUS + if (impl->use_rtkit) + res = pw_rtkit_make_high_priority(impl->system_bus, 0, nice_level); + else + res = sched_set_nice(nice_level); +#else + res = sched_set_nice(nice_level); +#endif + + if (res < 0) { + pw_log_warn("could not set nice-level to %d: %s", + nice_level, spa_strerror(res)); } else { - tid = _gettid(); - if (setpriority(PRIO_PROCESS, tid, nice_level) == 0) { - pw_log_info("main thread nice level set to %d", - nice_level); - } else { - res = -errno; - pw_log_warn("could not set nice-level to %d: %s", - nice_level, spa_strerror(res)); - } + pw_log_info("main thread nice level set to %d", + nice_level); } return res; @@ -546,14 +556,15 @@ static int set_rlimit(struct impl *impl) { struct rlimit rl; - long long rttime; int res = 0; spa_zero(rl); rl.rlim_cur = impl->rt_time_soft; rl.rlim_max = impl->rt_time_hard; +#ifdef HAVE_DBUS if (impl->use_rtkit) { + long long rttime; rttime = pw_rtkit_get_rttime_usec_max(impl->system_bus); if (rttime >= 0) { if ((rlim_t)rttime < rl.rlim_cur) { @@ -565,6 +576,7 @@ rl.rlim_max = SPA_MIN(rl.rlim_max, (rlim_t)rttime); } } +#endif if (setrlimit(RLIMIT_RTTIME, &rl) < 0) res = -errno; @@ -578,6 +590,46 @@ return res; } +static int impl_acquire_rt_sched(struct spa_thread *thread, int priority) +{ + int err; + struct sched_param sp; + pthread_t pt = (pthread_t)thread; + + if (priority < sched_get_priority_min(REALTIME_POLICY) || + priority > sched_get_priority_max(REALTIME_POLICY)) { + pw_log_warn("invalid priority %d for policy %d", priority, REALTIME_POLICY); + return -EINVAL; + } + + spa_zero(sp); + sp.sched_priority = priority; + if ((err = pthread_setschedparam(pt, REALTIME_POLICY | PW_SCHED_RESET_ON_FORK, &sp)) != 0) { + pw_log_warn("could not make thread %p realtime: %s", thread, strerror(err)); + return -err; + } + + pw_log_info("acquired realtime priority %d for thread %p", priority, thread); + return 0; +} + +static int impl_drop_rt_generic(void *data, struct spa_thread *thread) +{ + struct sched_param sp; + pthread_t pt = (pthread_t)thread; + int err; + + spa_zero(sp); + if ((err = pthread_setschedparam(pt, SCHED_OTHER | PW_SCHED_RESET_ON_FORK, &sp)) != 0) { + pw_log_debug("thread %p: SCHED_OTHER|SCHED_RESET_ON_FORK failed: %s", + thread, strerror(err)); + return -err; + } + pw_log_info("thread %p dropped realtime priority", thread); + return 0; +} + +#ifdef HAVE_DBUS static struct thread *find_thread_by_pt(struct impl *impl, pthread_t pt) { struct thread *t; @@ -714,42 +766,66 @@ if ((err = pw_rtkit_make_realtime(impl->system_bus, pid, priority)) < 0) { pw_log_warn("could not make thread %d realtime using RTKit: %s", pid, spa_strerror(err)); - } else { - pw_log_info("acquired realtime priority %d for thread %d using RTKit", priority, pid); + return err; } + + pw_log_info("acquired realtime priority %d for thread %d using RTKit", priority, pid); + return 0; } else { - if (priority < sched_get_priority_min(REALTIME_POLICY) || - priority > sched_get_priority_max(REALTIME_POLICY)) { - pw_log_warn("invalid priority %d for policy %d", priority, REALTIME_POLICY); - return -EINVAL; - } + return impl_acquire_rt_sched(thread, priority); + } +} - spa_zero(sp); - sp.sched_priority = priority; - if ((err = pthread_setschedparam(pt, REALTIME_POLICY | PW_SCHED_RESET_ON_FORK, &sp)) != 0) { - pw_log_warn("could not make thread %p realtime: %s", thread, strerror(err)); - return -err; - } - pw_log_info("acquired realtime priority %d for thread %p", priority, thread); +static const struct spa_thread_utils_methods impl_thread_utils = { + SPA_VERSION_THREAD_UTILS_METHODS, + .create = impl_create, + .join = impl_join, + .get_rt_range = impl_get_rt_range, + .acquire_rt = impl_acquire_rt, + .drop_rt = impl_drop_rt_generic, +}; + +#else /* HAVE_DBUS */ + +static struct spa_thread *impl_create(void *data, const struct spa_dict *props, + void *(*start_routine)(void*), void *arg) +{ + pthread_t pt; + int err; + + err = pthread_create(&pt, NULL, start_routine, arg); + if (err != 0) { + errno = err; + return NULL; } + return (struct spa_thread*)pt; +} + +static int impl_join(void *data, struct spa_thread *thread, void **retval) +{ + return pthread_join((pthread_t)thread, retval); +} +static int impl_get_rt_range(void *data, const struct spa_dict *props, + int *min, int *max) +{ + if (min) + *min = sched_get_priority_min(REALTIME_POLICY); + if (max) + *max = sched_get_priority_max(REALTIME_POLICY); return 0; } -static int impl_drop_rt(void *data, struct spa_thread *thread) +static int impl_acquire_rt(void *data, struct spa_thread *thread, int priority) { - struct sched_param sp; - pthread_t pt = (pthread_t)thread; - int err; + struct impl *impl = data; - spa_zero(sp); - if ((err = pthread_setschedparam(pt, SCHED_OTHER | PW_SCHED_RESET_ON_FORK, &sp)) != 0) { - pw_log_debug("thread %p: SCHED_OTHER|SCHED_RESET_ON_FORK failed: %s", - thread, strerror(err)); - return -err; + /* See the docstring on `spa_thread_utils_methods::acquire_rt` */ + if (priority == -1) { + priority = impl->rt_prio; } - pw_log_info("thread %p dropped realtime priority", thread); - return 0; + + return impl_acquire_rt_sched(thread, priority); } static const struct spa_thread_utils_methods impl_thread_utils = { @@ -758,36 +834,56 @@ .join = impl_join, .get_rt_range = impl_get_rt_range, .acquire_rt = impl_acquire_rt, - .drop_rt = impl_drop_rt, + .drop_rt = impl_drop_rt_generic, }; +#endif /* HAVE_DBUS */ + + +#ifdef HAVE_DBUS +static int should_use_rtkit(struct impl *impl, struct pw_context *context, bool *use_rtkit) +{ + const struct pw_properties *context_props; + const char *str; + + *use_rtkit = true; + if ((context_props = pw_context_get_properties(context)) != NULL && + (str = pw_properties_get(context_props, "support.dbus")) != NULL && + !pw_properties_parse_bool(str)) + *use_rtkit = false; + + /* If the user has permissions to use regular realtime scheduling, then + * we'll use that instead of RTKit */ + if (check_realtime_privileges(impl->rt_prio)) { + *use_rtkit = false; + } else { + if (!(*use_rtkit)) { + pw_log_warn("neither regular realtime scheduling nor RTKit are available"); + return -ENOTSUP; + } + + /* TODO: Should this be pw_log_warn or pw_log_debug instead? */ + pw_log_info("could not use realtime scheduling, falling back to using RTKit instead"); + } + + return 0; +} +#endif /* HAVE_DBUS */ SPA_EXPORT int pipewire__module_init(struct pw_impl_module *module, const char *args) { struct pw_context *context = pw_impl_module_get_context(module); struct impl *impl; - const struct pw_properties *context_props; struct pw_properties *props; - const char *str; - bool use_rtkit = true; int res = 0; PW_LOG_TOPIC_INIT(mod_topic); - if ((context_props = pw_context_get_properties(context)) != NULL && - (str = pw_properties_get(context_props, "support.dbus")) != NULL && - !pw_properties_parse_bool(str)) - use_rtkit = false; - impl = calloc(1, sizeof(struct impl)); if (impl == NULL) return -ENOMEM; - spa_list_init(&impl->threads_list); - pthread_mutex_init(&impl->lock, NULL); - pthread_cond_init(&impl->cond, NULL); - pw_log_debug("module %p: new", impl); props = args ? pw_properties_new_string(args) : pw_properties_new(NULL, NULL); @@ -802,22 +898,15 @@ impl->rt_time_soft = pw_properties_get_int32(props, "rt.time.soft", DEFAULT_RT_TIME_SOFT); impl->rt_time_hard = pw_properties_get_int32(props, "rt.time.hard", DEFAULT_RT_TIME_HARD); - /* If the user has permissions to use regular realtime scheduling, then - * we'll use that instead of RTKit */ - if (check_realtime_priviliges(impl->rt_prio)) { - use_rtkit = false; - } else { - if (!use_rtkit) { - res = -ENOTSUP; - pw_log_warn("neither regular realtime scheduling nor RTKit are available"); - goto error; - } +#ifdef HAVE_DBUS + spa_list_init(&impl->threads_list); + pthread_mutex_init(&impl->lock, NULL); + pthread_cond_init(&impl->cond, NULL); - /* TODO: Should this be pw_log_warn or pw_log_debug instead? */ - pw_log_info("could not use realtime scheduling, falling back to using RTKit instead"); + if ((res = should_use_rtkit(impl, context, &impl->use_rtkit)) < 0) { + goto error; } - impl->use_rtkit = use_rtkit; if (impl->use_rtkit) { impl->system_bus = pw_rtkit_bus_get_system(); if (impl->system_bus == NULL) { @@ -826,6 +915,13 @@ goto error; } } +#else + if (!check_realtime_privileges(impl->rt_prio)) { + res = -ENOTSUP; + pw_log_warn("regular realtime scheduling not available (RTKit fallback disabled)"); + goto error; + } +#endif if (IS_VALID_NICE_LEVEL(impl->nice_level)) set_nice(impl, impl->nice_level); @@ -842,16 +938,23 @@ pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props)); pw_impl_module_update_properties(module, &props->dict); +#ifdef HAVE_DBUS if (impl->use_rtkit) { pw_log_debug("initialized using RTKit"); } else { pw_log_debug("initialized using regular realtime scheduling"); } +#else + pw_log_debug("initialized using regular realtime scheduling"); +#endif + goto done; error: +#ifdef HAVE_DBUS if (impl->system_bus) pw_rtkit_bus_free(impl->system_bus); +#endif free(impl); done: pw_properties_free(props);
View file
pipewire-0.3.48.tar.gz/src/modules/module-zeroconf-discover/avahi-poll.c -> pipewire-0.3.49.tar.gz/src/modules/module-zeroconf-discover/avahi-poll.c
Changed
@@ -37,6 +37,7 @@ AvahiWatchEvent events; AvahiWatchCallback callback; void *userdata; + unsigned int dispatching; }; struct AvahiTimeout { @@ -65,9 +66,14 @@ { AvahiWatch *w = data; + w->dispatching += 1; + w->events = from_pw_events(mask); w->callback(w, fd, w->events, w->userdata); w->events = 0; + + if (--w->dispatching == 0 && !w->source) + free(w); } static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, @@ -103,9 +109,11 @@ static void watch_free(AvahiWatch *w) { - struct impl *impl = w->impl; - pw_loop_destroy_source(impl->loop, w->source); - free(w); + pw_loop_destroy_source(w->impl->loop, w->source); + w->source = NULL; + + if (!w->dispatching) + free(w); } static void timeout_callback(void *data, uint64_t expirations)
View file
pipewire-0.3.48.tar.gz/src/pipewire/filter.c -> pipewire-0.3.49.tar.gz/src/pipewire/filter.c
Changed
@@ -642,7 +642,7 @@ clear_params(impl, port, id); } else { for (i = 0; i < n_params; i++) { - if (!spa_pod_is_object(params[i])) + if (params[i] == NULL || !spa_pod_is_object(params[i])) continue; clear_params(impl, port, SPA_POD_OBJECT_ID(params[i])); }
View file
pipewire-0.3.48.tar.gz/src/pipewire/pipewire.c -> pipewire-0.3.49.tar.gz/src/pipewire/pipewire.c
Changed
@@ -89,7 +89,7 @@ struct spa_interface i18n_iface; struct spa_support support[MAX_SUPPORT]; uint32_t n_support; - unsigned int initialized:1; + uint32_t init_count; unsigned int in_valgrind:1; unsigned int no_color:1; unsigned int no_config:1; @@ -413,11 +413,9 @@ static void init_i18n(struct support *support) { - /* Load locale from the environment. */ - setlocale(LC_ALL, ""); - /* Set LC_NUMERIC to C so that floating point strings are consistently - * formatted and parsed across locales. */ - setlocale(LC_NUMERIC, "C"); + /* XXX: we should remove this setlocale() call, after wireplumber + * XXX: starts setting the locale */ + setlocale(LC_MESSAGES, ""); bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); pw_set_domain(GETTEXT_PACKAGE); @@ -594,7 +592,7 @@ char level[32]; pthread_mutex_lock(&init_lock); - if (support->initialized) + if (support->init_count > 0) goto done; pthread_mutex_lock(&support_lock); @@ -666,9 +664,9 @@ add_i18n(support); pw_log_info("version %s", pw_get_library_version()); - support->initialized = true; pthread_mutex_unlock(&support_lock); done: + support->init_count++; pthread_mutex_unlock(&init_lock); } @@ -680,6 +678,11 @@ struct plugin *p; pthread_mutex_lock(&init_lock); + if (support->init_count == 0) + goto done; + if (--support->init_count > 0) + goto done; + pthread_mutex_lock(&support_lock); pw_log_set(NULL); spa_list_consume(p, ®istry->plugins, link) { @@ -693,6 +696,7 @@ free(support->i18n_domain); spa_zero(global_support); pthread_mutex_unlock(&support_lock); +done: pthread_mutex_unlock(&init_lock); }
View file
pipewire-0.3.48.tar.gz/src/pipewire/stream.c -> pipewire-0.3.49.tar.gz/src/pipewire/stream.c
Changed
@@ -116,6 +116,8 @@ struct spa_io_clock *clock; struct spa_io_position *position; struct spa_io_buffers *io; + struct spa_io_rate_match *rate_match; + uint32_t rate_queued; struct { struct spa_io_position *position; } rt; @@ -295,7 +297,7 @@ clear_params(impl, id); } else { for (i = 0; i < n_params; i++) { - if (!spa_pod_is_object(params[i])) + if (params[i] == NULL || !spa_pod_is_object(params[i])) continue; clear_params(impl, SPA_POD_OBJECT_ID(params[i])); } @@ -390,14 +392,14 @@ { struct stream *impl = user_data; struct pw_stream *stream = &impl->this; - pw_log_trace("%p: do process", stream); + pw_log_trace_fp("%p: do process", stream); pw_stream_emit_process(stream); return 0; } static void call_process(struct stream *impl) { - pw_log_trace("%p: call process rt:%u", impl, impl->process_rt); + pw_log_trace_fp("%p: call process rt:%u", impl, impl->process_rt); if (impl->process_rt) spa_callbacks_call(&impl->rt_callbacks, struct pw_stream_events, process, 0); else @@ -411,7 +413,7 @@ { struct stream *impl = user_data; struct pw_stream *stream = &impl->this; - pw_log_trace("%p: drained", stream); + pw_log_trace_fp("%p: drained", stream); pw_stream_emit_drained(stream); return 0; } @@ -428,7 +430,7 @@ { struct stream *impl = user_data; struct pw_stream *stream = &impl->this; - pw_log_trace("%p: trigger_done", stream); + pw_log_trace_fp("%p: trigger_done", stream); pw_stream_emit_trigger_done(stream); return 0; } @@ -669,6 +671,12 @@ else impl->io = NULL; break; + case SPA_IO_RateMatch: + if (data && size >= sizeof(struct spa_io_rate_match)) + impl->rate_match = data; + else + impl->rate_match = NULL; + break; } pw_stream_emit_io_changed(stream, id, data, size); @@ -917,8 +925,9 @@ static inline void copy_position(struct stream *impl, int64_t queued) { struct spa_io_position *p = impl->rt.position; - if (SPA_UNLIKELY(p != NULL)) { - SEQ_WRITE(impl->seq); + + SEQ_WRITE(impl->seq); + if (SPA_LIKELY(p != NULL)) { impl->time.now = p->clock.nsec; impl->time.rate = p->clock.rate; if (SPA_UNLIKELY(impl->clock_id != p->clock.id)) { @@ -929,8 +938,10 @@ impl->time.delay = 0; impl->time.queued = queued; impl->quantum = p->clock.duration; - SEQ_WRITE(impl->seq); } + if (SPA_LIKELY(impl->rate_match != NULL)) + impl->rate_queued = impl->rate_match->delay; + SEQ_WRITE(impl->seq); } static int impl_node_process_input(void *object) @@ -940,7 +951,7 @@ struct spa_io_buffers *io = impl->io; struct buffer *b; - pw_log_trace("%p: process in status:%d id:%d ticks:%"PRIu64" delay:%"PRIi64, + pw_log_trace_fp("%p: process in status:%d id:%d ticks:%"PRIu64" delay:%"PRIi64, stream, io->status, io->buffer_id, impl->time.ticks, impl->time.delay); if (io->status == SPA_STATUS_HAVE_DATA && @@ -956,7 +967,7 @@ if (io->status != SPA_STATUS_NEED_DATA) { /* pop buffer to recycle */ if ((b = pop_queue(impl, &impl->queued))) { - pw_log_trace("%p: recycle buffer %d", stream, b->id); + pw_log_trace_fp("%p: recycle buffer %d", stream, b->id); } else if (io->status == -EPIPE) return io->status; io->buffer_id = b ? b->id : SPA_ID_INVALID; @@ -976,16 +987,19 @@ struct buffer *b; int res; uint32_t index; + bool recycled; again: - pw_log_trace("%p: process out status:%d id:%d", stream, + pw_log_trace_fp("%p: process out status:%d id:%d", stream, io->status, io->buffer_id); + recycled = false; if ((res = io->status) != SPA_STATUS_HAVE_DATA) { /* recycle old buffer */ if ((b = get_buffer(stream, io->buffer_id)) != NULL) { - pw_log_trace("%p: recycle buffer %d", stream, b->id); + pw_log_trace_fp("%p: recycle buffer %d", stream, b->id); push_queue(impl, &impl->dequeued, b); + recycled = true; } /* pop new buffer */ @@ -993,17 +1007,17 @@ impl->drained = false; io->buffer_id = b->id; res = io->status = SPA_STATUS_HAVE_DATA; - pw_log_trace("%p: pop %d %p", stream, b->id, io); + pw_log_trace_fp("%p: pop %d %p", stream, b->id, io); } else if (impl->draining || impl->drained) { impl->draining = true; impl->drained = true; io->buffer_id = SPA_ID_INVALID; res = io->status = SPA_STATUS_DRAINED; - pw_log_trace("%p: draining", stream); + pw_log_trace_fp("%p: draining", stream); } else { io->buffer_id = SPA_ID_INVALID; res = io->status = SPA_STATUS_NEED_DATA; - pw_log_trace("%p: no more buffers %p", stream, io); + pw_log_trace_fp("%p: no more buffers %p", stream, io); } } @@ -1012,12 +1026,12 @@ if (!impl->draining && !impl->driving) { /* we're not draining, not a driver check if we need to get * more buffers */ - if (!impl->process_rt) { + if (!impl->process_rt && (recycled || res == SPA_STATUS_NEED_DATA)) { /* not realtime and we have a free buffer, trigger process so that we have * data in the next round. */ if (spa_ringbuffer_get_read_index(&impl->dequeued.ring, &index) > 0) call_process(impl); - } else if (io->status == SPA_STATUS_NEED_DATA) { + } else if (res == SPA_STATUS_NEED_DATA) { /* realtime and we don't have a buffer, trigger process and try * again when there is something in the queue now */ call_process(impl); @@ -1027,7 +1041,7 @@ } } - pw_log_trace("%p: res %d", stream, res); + pw_log_trace_fp("%p: res %d", stream, res); if (impl->driving && impl->using_trigger && res != SPA_STATUS_HAVE_DATA) call_trigger_done(impl); @@ -2061,10 +2075,12 @@ { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); uintptr_t seq1, seq2; + uint32_t rate_queued; do { seq1 = SEQ_READ(impl->seq); *time = impl->time; + rate_queued = impl->rate_queued; seq2 = SEQ_READ(impl->seq); } while (!SEQ_READ_SUCCESS(seq1, seq2)); @@ -2073,11 +2089,13 @@ else time->queued = (int64_t)(impl->queued.incount - time->queued); + time->queued += rate_queued; + time->delay += ((impl->latency.min_quantum + impl->latency.max_quantum) / 2) * impl->quantum; time->delay += (impl->latency.min_rate + impl->latency.max_rate) / 2; time->delay += ((impl->latency.min_ns + impl->latency.max_ns) / 2) * time->rate.denom / SPA_NSEC_PER_SEC; - pw_log_trace("%p: %"PRIi64" %"PRIi64" %"PRIu64" %d/%d %"PRIu64" %" + pw_log_trace_fp("%p: %"PRIi64" %"PRIi64" %"PRIu64" %d/%d %"PRIu64" %" PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, stream, time->now, time->delay, time->ticks, time->rate.num, time->rate.denom, time->queued, @@ -2105,17 +2123,17 @@ if ((b = pop_queue(impl, &impl->dequeued)) == NULL) { res = -errno; - pw_log_trace("%p: no more buffers: %m", stream); + pw_log_trace_fp("%p: no more buffers: %m", stream); errno = -res; return NULL; } - pw_log_trace("%p: dequeue buffer %d", stream, b->id); + pw_log_trace_fp("%p: dequeue buffer %d size:%"PRIu64, stream, b->id, b->this.size); if (b->busy && impl->direction == SPA_DIRECTION_OUTPUT) { if (ATOMIC_INC(b->busy->count) > 1) { ATOMIC_DEC(b->busy->count); push_queue(impl, &impl->dequeued, b); - pw_log_trace("%p: buffer busy", stream); + pw_log_trace_fp("%p: buffer busy", stream); errno = EBUSY; return NULL; } @@ -2133,7 +2151,7 @@ if (b->busy) ATOMIC_DEC(b->busy->count); - pw_log_trace("%p: queue buffer %d", stream, b->id); + pw_log_trace_fp("%p: queue buffer %d", stream, b->id); if ((res = push_queue(impl, &impl->queued, b)) < 0) return res; @@ -2153,7 +2171,7 @@ struct stream *impl = user_data; struct buffer *b; - pw_log_trace("%p: flush", impl); + pw_log_trace_fp("%p: flush", impl); do { b = pop_queue(impl, &impl->queued); if (b != NULL) @@ -2171,7 +2189,7 @@ bool async, uint32_t seq, const void *data, size_t size, void *user_data) { struct stream *impl = user_data; - pw_log_trace("%p", impl); + pw_log_trace_fp("%p", impl); impl->draining = true; impl->drained = false; return 0; @@ -2230,7 +2248,7 @@ struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); int res = 0; - pw_log_trace("%p", impl); + pw_log_trace_fp("%p", impl); /* flag to check for old or new behaviour */ impl->using_trigger = true;
View file
pipewire-0.3.48.tar.gz/src/pipewire/stream.h -> pipewire-0.3.49.tar.gz/src/pipewire/stream.h
Changed
@@ -193,16 +193,30 @@ /** A time structure */ struct pw_time { - int64_t now; /**< the monotonic time in nanoseconds */ - struct spa_fraction rate; /**< the rate of \a ticks and delay */ + int64_t now; /**< the monotonic time in nanoseconds. This is the time + * when this time report was updated. It is usually + * updated every graph cycle. You can use the current + * monotonic time to calculate the elapsed time between + * this report and the current state and calculate + * updated ticks and delay values. */ + struct spa_fraction rate; /**< the rate of \a ticks and delay. This is usually + * expressed in 1/<samplerate>. */ uint64_t ticks; /**< the ticks at \a now. This is the current time that * the remote end is reading/writing. */ - int64_t delay; /**< delay to device, add to ticks to get the time of the - * device. Positive for INPUT streams and - * negative for OUTPUT streams. */ + int64_t delay; /**< delay to device. This is the time it will take for + * the next sample in the stream to be presented by + * the playback device or the time a sample traveled + * from the capture device. This delay includes the + * delay introduced by all filters on the path between + * the stream and the device. The delay is normally + * constant in a graph and can change when the topology + * of the graph or the quantum changes. */ uint64_t queued; /**< data queued in the stream, this is the sum * of the size fields in the pw_buffer that are - * currently queued */ + * currently queued and, for audio streams, the extra + * data queued in the resampler. For audio streams, it + * is thus highly recommended to use the buffer size + * field as the sample count in the buffer. */ }; #include <pipewire/port.h>
View file
pipewire-0.3.48.tar.gz/src/tools/dsffile.c -> pipewire-0.3.49.tar.gz/src/tools/dsffile.c
Changed
@@ -214,33 +214,39 @@ dsf_file_read(struct dsf_file *f, void *data, size_t samples, const struct dsf_layout *layout) { uint8_t *d = data; - const uint8_t *s; - uint32_t i, j, k, total, stride, bytes; - int32_t interleave = layout->interleave; + int step = SPA_ABS(layout->interleave); bool rev = layout->lsb != f->info.lsb; - - stride = layout->channels * layout->interleave; - bytes = samples * stride; - bytes -= bytes % f->info.blocksize; - - for (total = 0; total < bytes; total += layout->channels * f->info.blocksize) { - s = f->p + f->offset; - - for (i = 0; i < f->info.blocksize; i += interleave) { - for (j = 0; j < layout->channels; j++) { - const uint8_t *c = &s[f->info.blocksize * j + i]; - if (interleave > 0) { - for (k = 0; k < (uint32_t)interleave; k++) - *d++ = rev ? bitrev[c[k]] : c[k]; - } else { - for (k = -interleave; k > 0; k--) - *d++ = rev ? bitrev[c[k-1]] : c[k-1]; - } + size_t total, block, offset, pos; + + block = f->offset / f->info.blocksize; + offset = block * f->info.blocksize * f->info.channels; + pos = f->offset % f->info.blocksize; + + for (total = 0; total < samples && offset + pos < f->info.length; total++) { + const uint8_t *s = f->p + offset + pos; + uint32_t i; + + for (i = 0; i < layout->channels; i++) { + const uint8_t *c = &s[f->info.blocksize * i]; + int j; + + if (layout->interleave > 0) { + for (j = 0; j < step; j++) + *d++ = rev ? bitrev[c[j]] : c[j]; + } else { + for (j = step-1; j >= 0; j--) + *d++ = rev ? bitrev[c[j]] : c[j]; } } - f->offset += f->info.channels * f->info.blocksize; + pos += step; + if (pos == f->info.blocksize) { + pos = 0; + offset += f->info.blocksize * f->info.channels; + } } - return total / stride; + f->offset += total * step; + + return total; } int dsf_file_close(struct dsf_file *f)
View file
pipewire-0.3.48.tar.gz/src/tools/pw-cat.c -> pipewire-0.3.49.tar.gz/src/tools/pw-cat.c
Changed
@@ -34,6 +34,7 @@ #include <unistd.h> #include <assert.h> #include <ctype.h> +#include <locale.h> #include <sndfile.h> @@ -915,7 +916,7 @@ data->dsf.layout.channels = info.info.dsd.channels; data->dsf.layout.lsb = info.info.dsd.bitorder == SPA_PARAM_BITORDER_lsb; - data->stride = data->dsf.info.channels * SPA_ABS(data->dsf.layout.interleave); + data->stride = data->dsf.layout.channels * SPA_ABS(data->dsf.layout.interleave); if (data->verbose) { printf("DSD out: channels:%d bitorder:%s interleave:%d\n", @@ -1268,13 +1269,14 @@ } if (data->verbose) - printf("opened file \"%s\" channels:%d rate:%d bitorder:%s\n", + printf("opened file \"%s\" channels:%d rate:%d samples:%"PRIu64" bitorder:%s\n", data->filename, data->dsf.info.channels, data->dsf.info.rate, + data->dsf.info.samples, data->dsf.info.lsb ? "lsb" : "msb"); data->fill = dsf_play; -; + return 0; } @@ -1488,6 +1490,7 @@ int exit_code = EXIT_FAILURE, c, ret; enum pw_stream_flags flags = 0; + setlocale(LC_ALL, ""); pw_init(&argc, &argv); flags |= PW_STREAM_FLAG_AUTOCONNECT;
View file
pipewire-0.3.48.tar.gz/src/tools/pw-cli.c -> pipewire-0.3.49.tar.gz/src/tools/pw-cli.c
Changed
@@ -35,6 +35,7 @@ #include <fnmatch.h> #include <readline/readline.h> #include <readline/history.h> +#include <locale.h> #if !defined(FNM_EXTMATCH) #define FNM_EXTMATCH 0 @@ -3076,6 +3077,7 @@ setlinebuf(stdout); + setlocale(LC_ALL, ""); pw_init(&argc, &argv); while ((c = getopt_long(argc, argv, "hVdr:", long_options, NULL)) != -1) {
View file
pipewire-0.3.48.tar.gz/src/tools/pw-dot.c -> pipewire-0.3.49.tar.gz/src/tools/pw-dot.c
Changed
@@ -25,9 +25,16 @@ #include <stdio.h> #include <signal.h> #include <getopt.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <locale.h> #include <spa/utils/result.h> #include <spa/utils/string.h> +#include <spa/utils/json.h> #include <spa/debug/pod.h> #include <spa/debug/format.h> #include <spa/debug/types.h> @@ -77,7 +84,6 @@ #define INTERFACE_Module 5 #define INTERFACE_Factory 6 uint32_t type; - struct pw_properties *props; void *info; pw_destroy_t info_destroy; @@ -152,9 +158,8 @@ } } -static SPA_PRINTF_FUNC(7,0) void draw_vlabel(char **str, const char *name, uint32_t id, bool detail, - const struct spa_dict *info_p, const struct spa_dict *p, - const char *fmt, va_list varargs) +static SPA_PRINTF_FUNC(6,0) void draw_vlabel(char **str, const char *name, uint32_t id, bool detail, + const struct spa_dict *props, const char *fmt, va_list varargs) { /* draw the label header */ dot_str_add(str, "%s_%u [label=\"", name, id); @@ -162,22 +167,19 @@ /* draw the label body */ dot_str_vadd(str, fmt, varargs); - if (detail) { - draw_dict(str, "info_props", info_p); - draw_dict(str, "properties", p); - } + if (detail) + draw_dict(str, "properties", props); /*draw the label footer */ dot_str_add(str, "%s", "\"];\n"); } -static SPA_PRINTF_FUNC(7,8) void draw_label(char **str, const char *name, uint32_t id, bool detail, - const struct spa_dict *info_p, const struct spa_dict *p, - const char *fmt, ...) +static SPA_PRINTF_FUNC(6,7) void draw_label(char **str, const char *name, uint32_t id, bool detail, + const struct spa_dict *props, const char *fmt, ...) { va_list varargs; va_start(varargs, fmt); - draw_vlabel(str, name, id, detail, info_p, p, fmt, varargs); + draw_vlabel(str, name, id, detail, props, fmt, varargs); va_end(varargs); } @@ -199,7 +201,7 @@ /* draw the label */ draw_label(dot_str, - "port", g->id, g->data->show_detail, info->props, &g->props->dict, + "port", g->id, g->data->show_detail, info->props, "port_id: %u\\lname: %s\\ldirection: %s\\l", g->id, spa_dict_lookup(info->props, PW_KEY_PORT_NAME), @@ -237,10 +239,9 @@ g->id, spa_dict_lookup(info->props, PW_KEY_NODE_NAME), spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS)); - if (g->data->show_detail) { - draw_dict(dot_str, "info_props", info->props); - draw_dict(dot_str, "properties", &g->props->dict); - } + + if (g->data->show_detail) + draw_dict(dot_str, "properties", info->props); /*draw the label footer */ dot_str_add(dot_str, "%s", "\"\n"); @@ -249,11 +250,13 @@ struct global *p; const char *prop_node_id; spa_list_for_each(p, &g->data->globals, link) { + struct pw_port_info *pinfo; if (p->info == NULL) continue; if (p->type != INTERFACE_Port) continue; - prop_node_id = pw_properties_get(p->props, PW_KEY_NODE_ID); + pinfo = p->info; + prop_node_id = spa_dict_lookup(pinfo->props, PW_KEY_NODE_ID); if (!prop_node_id || (uint32_t)atoi(prop_node_id) != g->id) continue; if (p->draw) @@ -290,7 +293,7 @@ /* draw the label */ draw_label(dot_str, - "link", g->id, g->data->show_detail, info->props, &g->props->dict, + "link", g->id, g->data->show_detail, info->props, "link_id: %u\\loutput_node_id: %u\\linput_node_id: %u\\loutput_port_id: %u\\linput_port_id: %u\\lstate: %s\\l", g->id, info->output_node_id, @@ -318,7 +321,7 @@ /* draw the label */ draw_label(dot_str, - "client", g->id, g->data->show_detail, info->props, &g->props->dict, + "client", g->id, g->data->show_detail, info->props, "client_id: %u\\lname: %s\\lpid: %s\\l", g->id, spa_dict_lookup(info->props, PW_KEY_APP_NAME), @@ -345,7 +348,7 @@ /* draw the label */ draw_label(dot_str, - "device", g->id, g->data->show_detail, info->props, &g->props->dict, + "device", g->id, g->data->show_detail, info->props, "device_id: %u\\lname: %s\\lmedia_class: %s\\lapi: %s\\lpath: %s\\l", g->id, spa_dict_lookup(info->props, PW_KEY_DEVICE_NAME), @@ -376,7 +379,7 @@ /* draw the label */ draw_label(dot_str, - "factory", g->id, g->data->show_detail, info->props, &g->props->dict, + "factory", g->id, g->data->show_detail, info->props, "factory_id: %u\\lname: %s\\lmodule_id: %u\\l", g->id, info->name, module_id ); @@ -399,7 +402,7 @@ /* draw the label */ draw_label(dot_str, - "module", g->id, g->data->show_detail, info->props, &g->props->dict, + "module", g->id, g->data->show_detail, info->props, "module_id: %u\\lname: %s\\l", g->id, info->name ); @@ -611,9 +614,6 @@ struct global *g = user_data; spa_hook_remove(&g->object_listener); spa_hook_remove(&g->proxy_listener); - pw_properties_free(g->props); - if (g->info) - g->info_destroy(g->info); } static const struct pw_proxy_events proxy_events = { @@ -701,20 +701,17 @@ return; } - proxy = pw_registry_bind(d->registry, id, type, - client_version, - sizeof(struct global)); + proxy = pw_registry_bind(d->registry, id, type, client_version, 0); if (proxy == NULL) return; /* set the global data */ - g = pw_proxy_get_user_data(proxy); + g = calloc(1, sizeof(struct global)); g->data = d; g->proxy = proxy; g->id = id; g->type = object_type; - g->props = props ? pw_properties_new_dict(props) : NULL; g->info = NULL; g->info_destroy = info_destroy; @@ -762,6 +759,300 @@ pw_main_loop_quit(d->loop); } +static int get_data_from_pipewire(struct data *data, const char *opt_remote) +{ + struct pw_loop *l; + struct global *g; + + data->loop = pw_main_loop_new(NULL); + if (data->loop == NULL) { + fprintf(stderr, "can't create main loop: %m\n"); + return -1; + } + + l = pw_main_loop_get_loop(data->loop); + pw_loop_add_signal(l, SIGINT, do_quit, &data); + pw_loop_add_signal(l, SIGTERM, do_quit, &data); + + data->context = pw_context_new(l, NULL, 0); + if (data->context == NULL) { + fprintf(stderr, "can't create context: %m\n"); + pw_main_loop_destroy(data->loop); + return -1; + } + + data->core = pw_context_connect(data->context, + pw_properties_new( + PW_KEY_REMOTE_NAME, opt_remote, + NULL), + 0); + if (data->core == NULL) { + fprintf(stderr, "can't connect: %m\n"); + pw_context_destroy(data->context); + pw_main_loop_destroy(data->loop); + return -1; + } + + pw_core_add_listener(data->core, + &data->core_listener, + &core_events, data); + + data->registry = pw_core_get_registry(data->core, + PW_VERSION_REGISTRY, 0); + pw_registry_add_listener(data->registry, + &data->registry_listener, + ®istry_events, data); + + pw_main_loop_run(data->loop); + + spa_hook_remove(&data->registry_listener); + pw_proxy_destroy((struct pw_proxy*)data->registry); + spa_list_for_each(g, &data->globals, link) + pw_proxy_destroy(g->proxy); + spa_hook_remove(&data->core_listener); + pw_context_destroy(data->context); + pw_main_loop_destroy(data->loop); + + return 0; +} + +static void handle_json_obj(struct data *data, struct pw_properties *obj) +{ + struct global *g; + struct pw_properties *info, *props; + const char *str; + + str = pw_properties_get(obj, "type"); + if (!str) { + fprintf(stderr, "invalid object without type\n"); + return; + } + + g = calloc(1, sizeof (struct global)); + g->data = data; + + if (spa_streq(str, PW_TYPE_INTERFACE_Port)) { + g->info_destroy = (pw_destroy_t)pw_port_info_free; + g->draw = draw_port; + g->type = INTERFACE_Port; + } + else if (spa_streq(str, PW_TYPE_INTERFACE_Node)) { + g->info_destroy = (pw_destroy_t)pw_node_info_free; + g->draw = draw_node; + g->type = INTERFACE_Node; + } + else if (spa_streq(str, PW_TYPE_INTERFACE_Link)) { + g->info_destroy = (pw_destroy_t)pw_link_info_free; + g->draw = draw_link; + g->type = INTERFACE_Link; + } + else if (spa_streq(str, PW_TYPE_INTERFACE_Client)) { + g->info_destroy = (pw_destroy_t)pw_client_info_free; + g->draw = draw_client; + g->type = INTERFACE_Client; + } + else if (spa_streq(str, PW_TYPE_INTERFACE_Device)) { + g->info_destroy = (pw_destroy_t)pw_device_info_free; + g->draw = draw_device; + g->type = INTERFACE_Device; + } + else if (spa_streq(str, PW_TYPE_INTERFACE_Factory)) { + g->info_destroy = (pw_destroy_t)pw_factory_info_free; + g->draw = draw_factory; + g->type = INTERFACE_Factory; + } + else if (spa_streq(str, PW_TYPE_INTERFACE_Module)) { + g->info_destroy = (pw_destroy_t)pw_module_info_free; + g->draw = draw_module; + g->type = INTERFACE_Module; + } + else { + free(g); + return; + } + + g->id = pw_properties_get_uint32(obj, "id", 0); + + str = pw_properties_get(obj, "info"); + info = pw_properties_new_string(str); + + str = pw_properties_get(info, "props"); + props = str ? pw_properties_new_string(str) : NULL; + + switch (g->type) { + case INTERFACE_Port: { + struct pw_port_info pinfo = {0}; + pinfo.id = g->id; + str = pw_properties_get(info, "direction"); + pinfo.direction = spa_streq(str, "output") ? + PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT; + pinfo.props = props ? &props->dict : NULL; + pinfo.change_mask = PW_PORT_CHANGE_MASK_PROPS; + g->info = pw_port_info_update(NULL, &pinfo); + break; + } + case INTERFACE_Node: { + struct pw_node_info ninfo = {0}; + ninfo.id = g->id; + ninfo.max_input_ports = + pw_properties_get_uint32(info, "max-input-ports", 0); + ninfo.max_output_ports = + pw_properties_get_uint32(info, "max-output-ports", 0); + ninfo.n_input_ports = + pw_properties_get_uint32(info, "n-input-ports", 0); + ninfo.n_output_ports = + pw_properties_get_uint32(info, "n-output-ports", 0); + + str = pw_properties_get(info, "state"); + if (spa_streq(str, "running")) + ninfo.state = PW_NODE_STATE_RUNNING; + else if (spa_streq(str, "idle")) + ninfo.state = PW_NODE_STATE_IDLE; + else if (spa_streq(str, "suspended")) + ninfo.state = PW_NODE_STATE_SUSPENDED; + else if (spa_streq(str, "creating")) + ninfo.state = PW_NODE_STATE_CREATING; + else + ninfo.state = PW_NODE_STATE_ERROR; + ninfo.error = pw_properties_get(info, "error"); + + ninfo.props = props ? &props->dict : NULL; + ninfo.change_mask = PW_NODE_CHANGE_MASK_INPUT_PORTS | + PW_NODE_CHANGE_MASK_OUTPUT_PORTS | + PW_NODE_CHANGE_MASK_STATE | + PW_NODE_CHANGE_MASK_PROPS; + g->info = pw_node_info_update(NULL, &ninfo); + break; + } + case INTERFACE_Link: { + struct pw_link_info linfo = {0}; + linfo.id = g->id; + linfo.output_node_id = + pw_properties_get_uint32(info, "output-node-id", 0); + linfo.output_port_id = + pw_properties_get_uint32(info, "output-port-id", 0); + linfo.input_node_id = + pw_properties_get_uint32(info, "input-node-id", 0); + linfo.input_port_id = + pw_properties_get_uint32(info, "input-port-id", 0); + + str = pw_properties_get(info, "state"); + if (spa_streq(str, "active")) + linfo.state = PW_LINK_STATE_ACTIVE; + else if (spa_streq(str, "paused")) + linfo.state = PW_LINK_STATE_PAUSED; + else if (spa_streq(str, "allocating")) + linfo.state = PW_LINK_STATE_ALLOCATING; + else if (spa_streq(str, "negotiating")) + linfo.state = PW_LINK_STATE_NEGOTIATING; + else if (spa_streq(str, "init")) + linfo.state = PW_LINK_STATE_INIT; + else if (spa_streq(str, "unlinked")) + linfo.state = PW_LINK_STATE_UNLINKED; + else + linfo.state = PW_LINK_STATE_ERROR; + linfo.error = pw_properties_get(info, "error"); + + linfo.props = props ? &props->dict : NULL; + linfo.change_mask = PW_LINK_CHANGE_MASK_STATE | + PW_LINK_CHANGE_MASK_PROPS; + g->info = pw_link_info_update(NULL, &linfo); + break; + } + case INTERFACE_Client: { + struct pw_client_info cinfo = {0}; + cinfo.id = g->id; + cinfo.props = props ? &props->dict : NULL; + cinfo.change_mask = PW_CLIENT_CHANGE_MASK_PROPS; + g->info = pw_client_info_update(NULL, &cinfo); + break; + } + case INTERFACE_Device: { + struct pw_device_info dinfo = {0}; + dinfo.id = g->id; + dinfo.props = props ? &props->dict : NULL; + dinfo.change_mask = PW_DEVICE_CHANGE_MASK_PROPS; + g->info = pw_device_info_update(NULL, &dinfo); + break; + } + case INTERFACE_Factory: { + struct pw_factory_info finfo = {0}; + finfo.id = g->id; + finfo.name = pw_properties_get(info, "name"); + finfo.type = pw_properties_get(info, "type"); + finfo.version = pw_properties_get_uint32(info, "version", 0); + finfo.props = props ? &props->dict : NULL; + finfo.change_mask = PW_FACTORY_CHANGE_MASK_PROPS; + g->info = pw_factory_info_update(NULL, &finfo); + break; + } + case INTERFACE_Module: { + struct pw_module_info minfo = {0}; + minfo.id = g->id; + minfo.name = pw_properties_get(info, "name"); + minfo.filename = pw_properties_get(info, "filename"); + minfo.args = pw_properties_get(info, "args"); + minfo.props = props ? &props->dict : NULL; + minfo.change_mask = PW_MODULE_CHANGE_MASK_PROPS; + g->info = pw_module_info_update(NULL, &minfo); + break; + } + default: + break; + } + + pw_properties_free(info); + pw_properties_free(props); + + /* add the global to the list */ + spa_list_insert(&data->globals, &g->link); +} + +static int get_data_from_json(struct data *data, const char *json_path) +{ + int fd, len; + void *json; + struct stat sbuf; + struct spa_json it[2]; + const char *value; + + if ((fd = open(json_path, O_CLOEXEC | O_RDONLY)) < 0) { + fprintf(stderr, "error opening file '%s': %m\n", json_path); + return -1; + } + if (fstat(fd, &sbuf) < 0) { + fprintf(stderr, "error statting file '%s': %m\n", json_path); + close(fd); + return -1; + } + if ((json = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { + fprintf(stderr, "error mmapping file '%s': %m\n", json_path); + close(fd); + return -1; + } + + close(fd); + spa_json_init(&it[0], json, sbuf.st_size); + + if (spa_json_enter_array(&it[0], &it[1]) <= 0) { + fprintf(stderr, "expected top-level array in JSON file '%s'\n", json_path); + munmap(json, sbuf.st_size); + return -1; + } + + while ((len = spa_json_next(&it[1], &value)) > 0 && spa_json_is_object(value, len)) { + struct pw_properties *obj; + obj = pw_properties_new(NULL, NULL); + len = spa_json_container_len(&it[1], value, len); + pw_properties_update_string(obj, value, len); + handle_json_obj(data, obj); + pw_properties_free(obj); + } + + munmap(json, sbuf.st_size); + return 0; +} + static void show_help(const char *name, bool error) { fprintf(error ? stderr : stdout, "%s [options]\n" @@ -773,7 +1064,8 @@ " -r, --remote Remote daemon name\n" " -o, --output Output file (Default %s)\n" " -L, --lr Use left-right rank direction\n" - " -9, --90 Use orthogonal edges\n", + " -9, --90 Use orthogonal edges\n" + " -j, --json Read objects from pw-dump JSON file\n", name, DEFAULT_DOT_PATH); } @@ -781,9 +1073,10 @@ int main(int argc, char *argv[]) { struct data data = { 0 }; - struct pw_loop *l; + struct global *g; const char *opt_remote = NULL; const char *dot_path = DEFAULT_DOT_PATH; + const char *json_path = NULL; static const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, @@ -794,13 +1087,15 @@ { "output", required_argument, NULL, 'o' }, { "lr", no_argument, NULL, 'L' }, { "90", no_argument, NULL, '9' }, + { "json", required_argument, NULL, 'j' }, { NULL, 0, NULL, 0} }; int c; + setlocale(LC_ALL, ""); pw_init(&argc, &argv); - while ((c = getopt_long(argc, argv, "hVasdr:o:L9", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "hVasdr:o:L9j:", long_options, NULL)) != -1) { switch (c) { case 'h' : show_help(argv[0], false); @@ -841,63 +1136,35 @@ data.dot_orthoedges = true; fprintf(stderr, "orthogonal edges enabled\n"); break; + case 'j' : + json_path = optarg; + fprintf(stderr, "Using JSON file %s as input\n", json_path); + break; default: show_help(argv[0], true); return -1; } } - data.loop = pw_main_loop_new(NULL); - if (data.loop == NULL) { - fprintf(stderr, "can't create main loop: %m\n"); + if (!(data.dot_str = dot_str_new())) return -1; - } - l = pw_main_loop_get_loop(data.loop); - pw_loop_add_signal(l, SIGINT, do_quit, &data); - pw_loop_add_signal(l, SIGTERM, do_quit, &data); - - data.context = pw_context_new(l, NULL, 0); - if (data.context == NULL) { - fprintf(stderr, "can't create context: %m\n"); - return -1; - } + spa_list_init(&data.globals); - data.core = pw_context_connect(data.context, - pw_properties_new( - PW_KEY_REMOTE_NAME, opt_remote, - NULL), - 0); - if (data.core == NULL) { - fprintf(stderr, "can't connect: %m\n"); + if (!json_path && get_data_from_pipewire(&data, opt_remote) < 0) return -1; - } - - data.dot_str = dot_str_new(); - if (data.dot_str == NULL) + else if (json_path && get_data_from_json(&data, json_path) < 0) return -1; - spa_list_init(&data.globals); - - pw_core_add_listener(data.core, - &data.core_listener, - &core_events, &data); - data.registry = pw_core_get_registry(data.core, - PW_VERSION_REGISTRY, 0); - pw_registry_add_listener(data.registry, - &data.registry_listener, - ®istry_events, &data); - - pw_main_loop_run(data.loop); - draw_graph(&data, dot_path); dot_str_clear(&data.dot_str); - spa_hook_remove(&data.registry_listener); - pw_proxy_destroy((struct pw_proxy*)data.registry); - spa_hook_remove(&data.core_listener); - pw_context_destroy(data.context); - pw_main_loop_destroy(data.loop); + spa_list_consume(g, &data.globals, link) { + if (g->info && g->info_destroy) + g->info_destroy(g->info); + spa_list_remove(&g->link); + free(g); + } pw_deinit(); return 0;
View file
pipewire-0.3.48.tar.gz/src/tools/pw-dump.c -> pipewire-0.3.49.tar.gz/src/tools/pw-dump.c
Changed
@@ -31,6 +31,7 @@ #include <limits.h> #include <math.h> #include <fnmatch.h> +#include <locale.h> #if !defined(FNM_EXTMATCH) #define FNM_EXTMATCH 0 @@ -278,13 +279,15 @@ static void put_double(struct data *d, const char *key, double val) { - put_fmt(d, key, "%s%f%s", NUMBER, val, NORMAL); + char buf[128]; + put_fmt(d, key, "%s%s%s", NUMBER, + spa_json_format_float(buf, sizeof(buf), val), NORMAL); } static void put_value(struct data *d, const char *key, const char *val) { int64_t li; - double dv; + float fv; if (val == NULL) put_literal(d, key, "null"); @@ -292,8 +295,8 @@ put_literal(d, key, val); else if (spa_atoi64(val, &li, 10)) put_int(d, key, li); - else if (spa_atod(val, &dv)) - put_double(d, key, dv); + else if (spa_json_parse_float(val, strlen(val), &fv)) + put_double(d, key, fv); else put_string(d, key, val); } @@ -1487,6 +1490,7 @@ }; int c; + setlocale(LC_ALL, ""); pw_init(&argc, &argv); data.out = stdout;
View file
pipewire-0.3.48.tar.gz/src/tools/pw-link.c -> pipewire-0.3.49.tar.gz/src/tools/pw-link.c
Changed
@@ -27,6 +27,7 @@ #include <math.h> #include <getopt.h> #include <regex.h> +#include <locale.h> #include <spa/utils/result.h> #include <spa/utils/string.h> @@ -599,6 +600,7 @@ { NULL, 0, NULL, 0} }; + setlocale(LC_ALL, ""); pw_init(&argc, &argv); spa_list_init(&data.objects);
View file
pipewire-0.3.48.tar.gz/src/tools/pw-loopback.c -> pipewire-0.3.49.tar.gz/src/tools/pw-loopback.c
Changed
@@ -29,6 +29,7 @@ #include <getopt.h> #include <limits.h> #include <math.h> +#include <locale.h> #include <spa/utils/result.h> #include <spa/pod/builder.h> @@ -124,6 +125,7 @@ }; int c, res = -1; + setlocale(LC_ALL, ""); pw_init(&argc, &argv); data.channels = DEFAULT_CHANNELS;
View file
pipewire-0.3.48.tar.gz/src/tools/pw-metadata.c -> pipewire-0.3.49.tar.gz/src/tools/pw-metadata.c
Changed
@@ -26,6 +26,7 @@ #include <signal.h> #include <math.h> #include <getopt.h> +#include <locale.h> #include <spa/utils/result.h> #include <spa/utils/string.h> @@ -198,6 +199,7 @@ setlinebuf(stdout); + setlocale(LC_ALL, ""); pw_init(&argc, &argv); data.opt_name = "default";
View file
pipewire-0.3.48.tar.gz/src/tools/pw-mididump.c -> pipewire-0.3.49.tar.gz/src/tools/pw-mididump.c
Changed
@@ -26,6 +26,7 @@ #include <signal.h> #include <math.h> #include <getopt.h> +#include <locale.h> #include <spa/utils/result.h> #include <spa/utils/defs.h> @@ -195,6 +196,7 @@ { NULL, 0, NULL, 0} }; + setlocale(LC_ALL, ""); pw_init(&argc, &argv); setlinebuf(stdout);
View file
pipewire-0.3.48.tar.gz/src/tools/pw-mon.c -> pipewire-0.3.49.tar.gz/src/tools/pw-mon.c
Changed
@@ -26,6 +26,7 @@ #include <signal.h> #include <getopt.h> #include <unistd.h> +#include <locale.h> #include <spa/utils/result.h> #include <spa/utils/string.h> @@ -771,6 +772,7 @@ int c; bool colors = false; + setlocale(LC_ALL, ""); pw_init(&argc, &argv); setlinebuf(stdout);
View file
pipewire-0.3.48.tar.gz/src/tools/pw-profiler.c -> pipewire-0.3.49.tar.gz/src/tools/pw-profiler.c
Changed
@@ -25,6 +25,7 @@ #include <stdio.h> #include <signal.h> #include <getopt.h> +#include <locale.h> #include <spa/utils/result.h> #include <spa/utils/string.h> @@ -565,6 +566,7 @@ }; int c; + setlocale(LC_ALL, ""); pw_init(&argc, &argv); while ((c = getopt_long(argc, argv, "hVr:o:", long_options, NULL)) != -1) {
View file
pipewire-0.3.48.tar.gz/src/tools/pw-reserve.c -> pipewire-0.3.49.tar.gz/src/tools/pw-reserve.c
Changed
@@ -26,6 +26,7 @@ #include <getopt.h> #include <signal.h> +#include <locale.h> #include <dbus/dbus.h> @@ -121,6 +122,7 @@ setlinebuf(stdout); + setlocale(LC_ALL, ""); pw_init(&argc, &argv); while ((c = getopt_long(argc, argv, "hVn:a:p:m", long_options, NULL)) != -1) {
View file
pipewire-0.3.48.tar.gz/test/meson.build -> pipewire-0.3.49.tar.gz/test/meson.build
Changed
@@ -119,6 +119,28 @@ link_with: pwtest_lib) ) +openal_info = find_program('openal-info', required: false) +if openal_info.found() + cdata.set_quoted('OPENAL_INFO_PATH', openal_info.full_path()) +endif +summary({'openal-info': openal_info.found()}, bool_yn: true, section: 'Functional test programs') + +pactl = find_program('pactl', required: false) +if pactl.found() + cdata.set_quoted('PACTL_PATH', pactl.full_path()) +endif +summary({'pactl': pactl.found()}, bool_yn: true, section: 'Functional test programs') + +if default_sm == 'media-session' or default_sm == 'wireplumber' + test('test-functional', + executable('test-functional', + 'test-functional.c', + include_directories: pwtest_inc, + dependencies: [ spa_dep ], + link_with: pwtest_lib) + ) +endif + valgrind = find_program('valgrind', required: false) summary({'valgrind (test setup)': valgrind.found()}, bool_yn: true, section: 'Optional programs') if valgrind.found()
View file
pipewire-0.3.48.tar.gz/test/pwtest.c -> pipewire-0.3.49.tar.gz/test/pwtest.c
Changed
@@ -128,6 +128,8 @@ struct spa_list suites; unsigned int timeout; bool no_fork; + bool terminate; + struct spa_list cleanup_pids; const char *test_filter; bool has_iteration_filter; @@ -135,6 +137,11 @@ char *xdg_dir; }; +struct cleanup_pid { + struct spa_list link; + pid_t pid; +}; + struct pwtest_context *pwtest_get_context(struct pwtest_test *t) { return ctx; @@ -175,6 +182,56 @@ } } +static int add_cleanup_pid(struct pwtest_context *ctx, pid_t pid) +{ + struct cleanup_pid *cpid; + + if (pid == 0) + return -EINVAL; + + cpid = calloc(1, sizeof(struct cleanup_pid)); + if (cpid == NULL) + return -errno; + + cpid->pid = pid; + spa_list_append(&ctx->cleanup_pids, &cpid->link); + + return 0; +} + +static void remove_cleanup_pid(struct pwtest_context *ctx, pid_t pid) +{ + struct cleanup_pid *cpid, *t; + + spa_list_for_each_safe(cpid, t, &ctx->cleanup_pids, link) { + if (cpid->pid == pid) { + spa_list_remove(&cpid->link); + free(cpid); + } + } +} + +static void terminate_cleanup_pids(struct pwtest_context *ctx) +{ + struct cleanup_pid *cpid; + spa_list_for_each(cpid, &ctx->cleanup_pids, link) { + /* Don't free here, to be signal-safe */ + if (cpid->pid != 0) { + kill(cpid->pid, SIGTERM); + cpid->pid = 0; + } + } +} + +static void free_cleanup_pids(struct pwtest_context *ctx) +{ + struct cleanup_pid *cpid; + spa_list_consume(cpid, &ctx->cleanup_pids, link) { + spa_list_remove(&cpid->link); + free(cpid); + } +} + static void pwtest_backtrace(pid_t p) { #ifdef HAVE_GSTACK @@ -407,6 +464,34 @@ pwtest_error_with_msg("Unable to create temporary file: %s", strerror(errno)); } +int +pwtest_spawn(const char *file, char *const argv[]) +{ + int r; + int status = -1; + pid_t pid; + const int fail_code = 121; + + pid = fork(); + if (pid == 0) { + /* child process */ + execvp(file, (char **)argv); + exit(fail_code); + } else if (pid < 0) + pwtest_error_with_msg("Unable to fork: %s", strerror(errno)); + + add_cleanup_pid(ctx, pid); + r = waitpid(pid, &status, 0); + remove_cleanup_pid(ctx, pid); + if (r <= 0) + pwtest_error_with_msg("waitpid failed: %s", strerror(errno)); + + if (WEXITSTATUS(status) == fail_code) + pwtest_error_with_msg("exec %s failed", file); + + return status; +} + void _pwtest_add(struct pwtest_context *ctx, struct pwtest_suite *suite, const char *funcname, const void *func, ...) { @@ -461,6 +546,8 @@ pw_properties_set(t->args.env, key, value); break; case PWTEST_ARG_DAEMON: + if (RUNNING_ON_VALGRIND) + t->result = PWTEST_SKIP; t->args.pw_daemon = true; break; } @@ -568,6 +655,9 @@ { struct pwtest_suite *c, *tmp; + terminate_cleanup_pids(ctx); + free_cleanup_pids(ctx); + spa_list_for_each_safe(c, tmp, &ctx->suites, link) { free_suite(c); } @@ -585,13 +675,14 @@ sigaction(signal, &act, NULL); pwtest_backtrace(0); + terminate_cleanup_pids(ctx); raise(signal); } static inline void log_append(struct pw_array *buffer, int fd) { int r = 0; - const int sz = 1024; + const int sz = 65536; while (true) { r = pw_array_ensure_size(buffer, sz); @@ -646,6 +737,7 @@ pid_t pid; char pw_remote[64]; int status; + int r; spa_scnprintf(pw_remote, sizeof(pw_remote), "pwtest-pw-%u\n", count++); replace_env(t, "PIPEWIRE_REMOTE", pw_remote); @@ -653,15 +745,26 @@ pid = fork(); if (pid == 0) { /* child */ + setpgid(0, 0); + setenv("PIPEWIRE_CORE", pw_remote, 1); + setenv("PIPEWIRE_DEBUG", "4", 0); + setenv("WIREPLUMBER_DEBUG", "4", 0); + + r = dup2(stderr_fd, STDERR_FILENO); + spa_assert_se(r != -1); + r = dup2(stderr_fd, STDOUT_FILENO); + spa_assert_se(r != -1); - dup2(stderr_fd, STDERR_FILENO); - setlinebuf(stderr); execl(daemon, daemon, (char*)NULL); return -errno; + } else if (pid < 0) { + return pid; } + add_cleanup_pid(ctx, -pid); + /* parent */ sleep(1); /* FIXME how to wait for pw to be ready? */ if (waitpid(pid, &status, WNOHANG) > 0) { @@ -701,6 +804,7 @@ replace_env(t, "TMPDIR", xdg_runtime_dir); replace_env(t, "SPA_PLUGIN_DIR", BUILD_ROOT "/spa/plugins"); + replace_env(t, "SPA_DATA_DIR", SOURCE_ROOT "/spa/plugins"); replace_env(t, "PIPEWIRE_CONFIG_DIR", BUILD_ROOT "/src/daemon"); replace_env(t, "PIPEWIRE_MODULE_DIR", BUILD_ROOT "/src/modules"); replace_env(t, "ACP_PATHS_DIR", SOURCE_ROOT "/spa/plugins/alsa/mixer/paths"); @@ -711,7 +815,8 @@ static void close_pipes(int fds[_FD_LAST]) { for (int i = 0; i < _FD_LAST; i++) { - close(fds[i]); + if (fds[i] >= 0) + close(fds[i]); fds[i] = -1; } } @@ -720,20 +825,40 @@ { int r; int i; + int pipe_max_size = 4194304; for (i = 0; i < _FD_LAST; i++) { read_fds[i] = -1; write_fds[i] = -1; } +#ifdef __linux__ + { + FILE *f; + f = fopen("/proc/sys/fs/pipe-max-size", "r"); + if (f) { + if (fscanf(f, "%d", &r) == 1) + pipe_max_size = SPA_MIN(r, pipe_max_size); + fclose(f); + } + } +#endif + for (i = 0; i < _FD_LAST; i++) { int pipe[2]; - r = pipe2(pipe, O_NONBLOCK); + r = pipe2(pipe, O_CLOEXEC | O_NONBLOCK); if (r < 0) goto error; read_fds[i] = pipe[0]; write_fds[i] = pipe[1]; +#ifdef __linux__ + /* Max pipe buffers, to avoid scrambling if reading lags. + * Can't use blocking write fds, since reading too slow + * then affects execution. + */ + fcntl(write_fds[i], F_SETPIPE_SZ, pipe_max_size); +#endif } return 0; @@ -783,6 +908,9 @@ close_pipes(read_fds); + /* Reset cleanup pid list */ + free_cleanup_pids(ctx); + /* Catch any crashers so we can insert a backtrace */ sigemptyset(&act.sa_mask); act.sa_flags = 0; @@ -963,12 +1091,14 @@ errno = -r; goto error; } + add_cleanup_pid(ctx, pid); r = monitor_test_forked(t, pid, read_fds); if (r < 0) { errno = -r; goto error; } + remove_cleanup_pid(ctx, pid); } errno = 0; @@ -976,6 +1106,12 @@ if (errno) t->sig_or_errno = -errno; + if (ctx->terminate) { + char *buf = pw_array_add(&t->logs[FD_LOG], 64); + spa_scnprintf(buf, 64, "pwtest: tests terminated by signal\n"); + t->result = PWTEST_SYSTEM_ERROR; + } + for (size_t i = 0; i < SPA_N_ELEMENTS(read_fds); i++) { log_append(&t->logs[i], read_fds[i]); } @@ -983,8 +1119,16 @@ if (pw_daemon > 0) { int status; - kill(pw_daemon, SIGTERM); - r = waitpid(pw_daemon, &status, 0); + kill(-pw_daemon, SIGTERM); + remove_cleanup_pid(ctx, -pw_daemon); + + /* blocking read. the other end closes when done */ + close_pipes(write_fds); + fcntl(read_fds[FD_DAEMON], F_SETFL, O_CLOEXEC); + do { + log_append(&t->logs[FD_DAEMON], read_fds[FD_DAEMON]); + } while ((r = waitpid(pw_daemon, &status, WNOHANG)) == 0); + if (r > 0) { /* write_fds are closed in the parent process, so we append directly */ char *buf = pw_array_add(&t->logs[FD_DAEMON], 64); @@ -1147,6 +1291,11 @@ r = EXIT_FAILURE; break; } + + if (ctx->terminate) { + r = EXIT_FAILURE; + return r; + } } } } @@ -1211,7 +1360,7 @@ " -h, --help Show this help\n" " --verbose Verbose output\n" " --list List all available suites and tests\n" - " --timeout=N Set the test timeout to N seconds (default: 30)\n" + " --timeout=N Set the test timeout to N seconds (default: 15)\n" " --filter-test=glob Run only tests matching the given glob\n" " --filter-suites=glob Run only suites matching the given glob\n" " --filter-iteration=N Run only iteration N\n" @@ -1223,6 +1372,17 @@ progname); } +static void sigterm_handler(int signo) +{ + terminate_cleanup_pids(ctx); + ctx->terminate = true; + if (ctx->no_fork) { + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); + raise(signo); + } +} + int main(int argc, char **argv) { int r = EXIT_SUCCESS; @@ -1249,7 +1409,7 @@ }; struct pwtest_context test_ctx = { .suites = SPA_LIST_INIT(&test_ctx.suites), - .timeout = 30, + .timeout = 15, .has_iteration_filter = false, }; enum { @@ -1258,6 +1418,8 @@ } mode = MODE_TEST; const char *suite_filter = NULL; + spa_list_init(&test_ctx.cleanup_pids); + ctx = &test_ctx; while (1) { @@ -1315,6 +1477,8 @@ break; case MODE_TEST: setrlimit(RLIMIT_CORE, &((struct rlimit){0, 0})); + signal(SIGTERM, sigterm_handler); + signal(SIGINT, sigterm_handler); r = run_tests(ctx); break; }
View file
pipewire-0.3.48.tar.gz/test/pwtest.h -> pipewire-0.3.49.tar.gz/test/pwtest.h
Changed
@@ -558,6 +558,11 @@ */ void pwtest_mkstemp(char path[PATH_MAX]); +/** + * Run a command and wait for it to return. + */ +int pwtest_spawn(const char *file, char *const argv[]); + /** * \}
View file
pipewire-0.3.49.tar.gz/test/test-functional.c
Added
@@ -0,0 +1,62 @@ +/* PipeWire + * + * Copyright © 2019 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + +#include "config.h" + +#include "pwtest.h" + +PWTEST(openal_info_test) +{ + int status; + +#ifdef OPENAL_INFO_PATH + status = pwtest_spawn(OPENAL_INFO_PATH, (char *[]){ "openal-info", NULL }); + pwtest_int_eq(WEXITSTATUS(status), 0); + return PWTEST_PASS; +#else + return PWTEST_SKIP; +#endif +} + +PWTEST(pactl_test) +{ + int status; + +#ifdef PACTL_PATH + status = pwtest_spawn(PACTL_PATH, (char *[]){ "pactl", "info", NULL }); + pwtest_int_eq(WEXITSTATUS(status), 0); + return PWTEST_PASS; +#else + return PWTEST_SKIP; +#endif +} + +PWTEST_SUITE(pw_array) +{ + pwtest_add(pactl_test, PWTEST_ARG_DAEMON); + pwtest_add(openal_info_test, PWTEST_ARG_DAEMON); + + return PWTEST_PASS; +}
View file
pipewire-0.3.48.tar.gz/test/test-lib.c -> pipewire-0.3.49.tar.gz/test/test-lib.c
Changed
@@ -49,9 +49,21 @@ return PWTEST_PASS; } +PWTEST(init_deinit) +{ + pw_init(0, NULL); + pw_deinit(); + pw_init(0, NULL); + pw_init(0, NULL); + pw_deinit(); + pw_deinit(); + return PWTEST_PASS; +} + PWTEST_SUITE(properties) { pwtest_add(library_version, PWTEST_NOARG); + pwtest_add(init_deinit, PWTEST_NOARG); return PWTEST_PASS; }
View file
pipewire-0.3.48.tar.gz/test/test-loop.c -> pipewire-0.3.49.tar.gz/test/test-loop.c
Changed
@@ -44,6 +44,22 @@ int count; }; +static inline void write_eventfd(int evfd) +{ + uint64_t value = 1; + ssize_t r = write(evfd, &value, sizeof(value)); + pwtest_errno_ok(r); + pwtest_int_eq(r, (ssize_t) sizeof(value)); +} + +static inline void read_eventfd(int evfd) +{ + uint64_t value = 0; + ssize_t r = read(evfd, &value, sizeof(value)); + pwtest_errno_ok(r); + pwtest_int_eq(r, (ssize_t) sizeof(value)); +} + static void on_event(struct spa_source *source) { struct data *d = source->data; @@ -86,8 +102,8 @@ pw_loop_add_source(data.l, &data.a->source); pw_loop_add_source(data.l, &data.b->source); - write(data.a->source.fd, &(uint64_t){1}, sizeof(uint64_t)); - write(data.b->source.fd, &(uint64_t){1}, sizeof(uint64_t)); + write_eventfd(data.a->source.fd); + write_eventfd(data.b->source.fd); pw_main_loop_run(data.ml); pw_main_loop_destroy(data.ml); @@ -102,12 +118,11 @@ { static bool first = true; struct data *d = source->data; - uint64_t val; ++d->count; pwtest_int_lt(d->count, 3); - read(source->fd, &val, sizeof(val)); + read_eventfd(source->fd); if (first) { first = false; @@ -146,8 +161,8 @@ pw_loop_add_source(data.l, &data.a->source); pw_loop_add_source(data.l, &data.b->source); - write(data.a->source.fd, &(uint64_t){1}, sizeof(uint64_t)); - write(data.b->source.fd, &(uint64_t){1}, sizeof(uint64_t)); + write_eventfd(data.a->source.fd); + write_eventfd(data.b->source.fd); pw_main_loop_run(data.ml); pw_main_loop_destroy(data.ml); @@ -165,12 +180,11 @@ { static bool first = true; struct data *d = source->data; - uint64_t val; ++d->count; pwtest_int_lt(d->count, 3); - read(source->fd, &val, sizeof(val)); + read_eventfd(source->fd); if (first) { first = false; @@ -216,8 +230,8 @@ pw_loop_add_source(data.l, &data.a->source); pw_loop_add_source(data.l, &data.b->source); - write(data.a->source.fd, &(uint64_t){1}, sizeof(uint64_t)); - write(data.b->source.fd, &(uint64_t){1}, sizeof(uint64_t)); + write_eventfd(data.a->source.fd); + write_eventfd(data.b->source.fd); pw_main_loop_run(data.ml); pw_main_loop_destroy(data.ml); @@ -227,33 +241,133 @@ return PWTEST_PASS; } -PWTEST(thread_loop_destroy_between_poll_and_lock) +static void +on_event_fail_if_called(void *data, int fd, uint32_t mask) +{ + pwtest_fail_if_reached(); +} + +struct dmsbd_data { + struct pw_loop *l; + struct pw_main_loop *ml; + struct spa_source *source; + struct spa_hook hook; +}; + +static void dmsbd_after(void *data) +{ + struct dmsbd_data *d = data; + + pw_loop_destroy_source(d->l, d->source); + pw_main_loop_quit(d->ml); +} + +static const struct spa_loop_control_hooks dmsbd_hooks = { + SPA_VERSION_LOOP_CONTROL_HOOKS, + .after = dmsbd_after, +}; + +PWTEST(destroy_managed_source_before_dispatch) { pw_init(NULL, NULL); - struct pw_thread_loop *thread_loop = pw_thread_loop_new("uaf", NULL); - pwtest_ptr_notnull(thread_loop); + struct dmsbd_data data = {0}; + + data.ml = pw_main_loop_new(NULL); + pwtest_ptr_notnull(data.ml); + + data.l = pw_main_loop_get_loop(data.ml); + pwtest_ptr_notnull(data.l); + + data.source = pw_loop_add_io(data.l, eventfd(0, 0), SPA_IO_IN, true, on_event_fail_if_called, NULL); + pwtest_ptr_notnull(data.source); - struct pw_loop *loop = pw_thread_loop_get_loop(thread_loop); - pwtest_ptr_notnull(loop); + pw_loop_add_hook(data.l, &data.hook, &dmsbd_hooks, &data); - int evfd = eventfd(0, 0); - pwtest_errno_ok(evfd); + write_eventfd(data.source->fd); - struct spa_source *source = pw_loop_add_io(loop, evfd, SPA_IO_IN, true, NULL, NULL); - pwtest_ptr_notnull(source); + pw_main_loop_run(data.ml); + pw_main_loop_destroy(data.ml); - pw_thread_loop_start(thread_loop); + pw_deinit(); - pw_thread_loop_lock(thread_loop); - { - write(evfd, &(uint64_t){1}, sizeof(uint64_t)); - sleep(1); - pw_loop_destroy_source(loop, source); + return PWTEST_PASS; +} + +struct dmsbd_recurse_data { + struct pw_loop *l; + struct pw_main_loop *ml; + struct spa_source *a, *b; + struct spa_hook hook; + bool first; +}; + +static void dmsbd_recurse_on_event(void *data, int fd, uint32_t mask) +{ + struct dmsbd_recurse_data *d = data; + + read_eventfd(fd); + + pw_loop_enter(d->l); + pw_loop_iterate(d->l, 0); + pw_loop_leave(d->l); + + pw_main_loop_quit(d->ml); +} + +static void dmswp_recurse_before(void *data) +{ + struct dmsbd_recurse_data *d = data; + + if (d->first) { + write_eventfd(d->a->fd); + write_eventfd(d->b->fd); } - pw_thread_loop_unlock(thread_loop); +} + +static void dmsbd_recurse_after(void *data) +{ + struct dmsbd_recurse_data *d = data; + + if (d->first) { + pw_loop_destroy_source(d->l, d->b); + + d->first = false; + } +} + +static const struct spa_loop_control_hooks dmsbd_recurse_hooks = { + SPA_VERSION_LOOP_CONTROL_HOOKS, + .before = dmswp_recurse_before, + .after = dmsbd_recurse_after, +}; + +PWTEST(destroy_managed_source_before_dispatch_recurse) +{ + pw_init(NULL, NULL); + + struct dmsbd_recurse_data data = { + .first = true, + }; + + data.ml = pw_main_loop_new(NULL); + pwtest_ptr_notnull(data.ml); + + data.l = pw_main_loop_get_loop(data.ml); + pwtest_ptr_notnull(data.l); + + data.l = pw_main_loop_get_loop(data.ml); + pwtest_ptr_notnull(data.l); - pw_thread_loop_destroy(thread_loop); + data.a = pw_loop_add_io(data.l, eventfd(0, 0), SPA_IO_IN, true, dmsbd_recurse_on_event, &data); + data.b = pw_loop_add_io(data.l, eventfd(0, 0), SPA_IO_IN, true, on_event_fail_if_called, NULL); + pwtest_ptr_notnull(data.a); + pwtest_ptr_notnull(data.b); + + pw_loop_add_hook(data.l, &data.hook, &dmsbd_recurse_hooks, &data); + + pw_main_loop_run(data.ml); + pw_main_loop_destroy(data.ml); pw_deinit(); @@ -265,7 +379,8 @@ pwtest_add(pwtest_loop_destroy2, PWTEST_NOARG); pwtest_add(pwtest_loop_recurse1, PWTEST_NOARG); pwtest_add(pwtest_loop_recurse2, PWTEST_NOARG); - pwtest_add(thread_loop_destroy_between_poll_and_lock, PWTEST_NOARG); + pwtest_add(destroy_managed_source_before_dispatch, PWTEST_NOARG); + pwtest_add(destroy_managed_source_before_dispatch_recurse, PWTEST_NOARG); return PWTEST_PASS; }
View file
pipewire-0.3.48.tar.gz/test/test-spa-json.c -> pipewire-0.3.49.tar.gz/test/test-spa-json.c
Changed
@@ -22,6 +22,8 @@ * DEALINGS IN THE SOFTWARE. */ +#include <locale.h> + #include "pwtest.h" #include <spa/utils/defs.h> @@ -223,6 +225,63 @@ return PWTEST_PASS; } +PWTEST(json_float) +{ + struct { + const char *str; + double val; + } val[] = { + { "0.0", 0.0f }, + { ".0", 0.0f }, + { ".0E0", 0.0E0f }, + { "1.0", 1.0f }, + { "1.011", 1.011f }, + { "176543.123456", 176543.123456f }, + { "-176543.123456", -176543.123456f }, + { "-5678.5432E10", -5678.5432E10f }, + { "-5678.5432e10", -5678.5432e10f }, + { "-5678.5432e-10", -5678.5432e-10f }, + { "5678.5432e+10", 5678.5432e+10f }, + { "00.000100", 00.000100f }, + { "-0.000100", -0.000100f }, + }; + float v; + unsigned i; + char buf1[128], buf2[128], *b1 = buf1, *b2 = buf2; + + pwtest_int_eq(spa_json_parse_float("", 0, &v), 0); + + setlocale(LC_NUMERIC, "C"); + for (i = 0; i < SPA_N_ELEMENTS(val); i++) { + pwtest_int_gt(spa_json_parse_float(val[i].str, strlen(val[i].str), &v), 0); + pwtest_double_eq(v, val[i].val); + } + setlocale(LC_NUMERIC, "fr_FR"); + for (i = 0; i < SPA_N_ELEMENTS(val); i++) { + pwtest_int_gt(spa_json_parse_float(val[i].str, strlen(val[i].str), &v), 0); + pwtest_double_eq(v, val[i].val); + } + pwtest_ptr_eq(spa_json_format_float(buf1, sizeof(buf1), 0.0f), b1); + pwtest_str_eq(buf1, "0.000000"); + pwtest_ptr_eq(spa_json_format_float(buf1, sizeof(buf1), NAN), b1); + pwtest_str_eq(buf1, "0.000000"); + pwtest_ptr_eq(spa_json_format_float(buf1, sizeof(buf1), INFINITY), b1); + pwtest_ptr_eq(spa_json_format_float(buf2, sizeof(buf2), FLT_MAX), b2); + pwtest_str_eq(buf1, buf2); + pwtest_ptr_eq(spa_json_format_float(buf1, sizeof(buf1), -INFINITY), b1); + pwtest_ptr_eq(spa_json_format_float(buf2, sizeof(buf2), FLT_MIN), b2); + pwtest_str_eq(buf1, buf2); + + return PWTEST_PASS; +} + +PWTEST(json_int) +{ + int v; + pwtest_int_eq(spa_json_parse_int("", 0, &v), 0); + return PWTEST_PASS; +} + PWTEST_SUITE(spa_json) { pwtest_add(json_abi, PWTEST_NOARG); @@ -230,6 +289,8 @@ pwtest_add(json_encode, PWTEST_NOARG); pwtest_add(json_array, PWTEST_NOARG); pwtest_add(json_overflow, PWTEST_NOARG); + pwtest_add(json_float, PWTEST_NOARG); + pwtest_add(json_int, PWTEST_NOARG); return PWTEST_PASS; }
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.