Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
We truncated the diff of some files because they were too big. If you want to see the full diff for every file,
click here
.
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 25
View file
pipewire-aptx.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Sat Apr 8 17:49:24 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> + +- Update to version 0.3.68 + +------------------------------------------------------------------- Sat Mar 18 12:35:35 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> - Update to version 0.3.67
View file
pipewire-aptx.spec
Changed
@@ -7,7 +7,7 @@ %define soversion 0_2 Name: pipewire-aptx -Version: 0.3.67 +Version: 0.3.68 Release: 0 Summary: PipeWire Bluetooth aptX codec plugin License: MIT
View file
pipewire-0.3.67.tar.gz/.cirrus.yml
Deleted
@@ -1,24 +0,0 @@ -task: - freebsd_instance: - matrix: - - image_family: freebsd-13-1-snap - env: - # /usr/ports/Mk/Uses/localbase.mk localbase:ldflags - LOCALBASE: /usr/local - CFLAGS: -isystem $LOCALBASE/include - CPPFLAGS: $CFLAGS - CXXFLAGS: $CFLAGS - LDFLAGS: -L$LOCALBASE/lib - deps_script: - - sed -i.bak -e 's/quarterly/latest/' /etc/pkg/FreeBSD.conf - - pkg install -y meson pkgconf git-lite dbus glib libepoll-shim libudev-devd vulkan-loader vulkan-headers v4l_compat gstreamer1 gstreamer1-plugins libinotify gettext libsndfile sdl2 alsa-lib - - sysrc dbus_enable=YES - - service dbus restart - build_script: - - mkdir build - - cd build - - meson setup -Dalsa=enabled -Draop=enabled -Dv4l2=enabled -Dpipewire-alsa=enabled -Dbluez5=disabled -Djack=disabled -Dpipewire-jack=enabled -Dpw-cat=enabled -Dpipewire-v4l2=disabled -Dsdl2=enabled -Dsystemd=disabled -Dsession-managers=media-session .. - - ninja - test_script: - - cd build - - ninja test
View file
pipewire-0.3.67.tar.gz/.gitlab-ci.yml -> pipewire-0.3.68.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: '2023-01-18.0' + FDO_DISTRIBUTION_TAG: '2023-03-09.0' FDO_DISTRIBUTION_VERSION: '35' FDO_DISTRIBUTION_PACKAGES: >- alsa-lib-devel @@ -44,10 +44,12 @@ gstreamer1-devel gstreamer1-plugins-base-devel jack-audio-connection-kit-devel + libasan libcanberra-devel libldac-devel libmysofa-devel libsndfile-devel + libubsan libusb-devel lilv-devel libv4l-devel @@ -172,15 +174,15 @@ - export XDG_RUNTIME_DIR="$(mktemp -p $PWD -d xdg-runtime-XXXXXX)" - | if -n "$FDO_CI_CONCURRENT" ; then - NINJA_ARGS="-j$FDO_CI_CONCURRENT $NINJA_ARGS" - export NINJA_ARGS + COMPILE_ARGS="-j$FDO_CI_CONCURRENT" + export COMPILE_ARGS fi script: - echo "Building with meson options $MESON_OPTIONS" - - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS - - ninja $NINJA_ARGS -C "$BUILD_DIR" - - ninja $NINJA_ARGS -C "$BUILD_DIR" test - - ninja $NINJA_ARGS -C "$BUILD_DIR" install + - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS + - meson compile -C "$BUILD_DIR" $COMPILE_ARGS + - meson test -C "$BUILD_DIR" --no-rebuild + - meson install -C "$BUILD_DIR" --no-rebuild artifacts: name: pipewire-$CI_COMMIT_SHA when: always @@ -312,9 +314,18 @@ MESON_OPTION_VALUE: enabled, disabled script: - echo "Building with -D$MESON_OPTION=$MESON_OPTION_VALUE" - - meson "$BUILD_DIR" . --prefix="$PREFIX" "-D$MESON_OPTION=$MESON_OPTION_VALUE" -Dsession-managers= - - ninja $NINJA_ARGS -C "$BUILD_DIR" - - ninja $NINJA_ARGS -C "$BUILD_DIR" test + - meson setup "$BUILD_DIR" --prefix="$PREFIX" "-D$MESON_OPTION=$MESON_OPTION_VALUE" -Dsession-managers= + - meson compile -C "$BUILD_DIR" $COMPILE_ARGS + - meson test -C "$BUILD_DIR" --no-rebuild + +build_with_asan_ubsan: + extends: + - .build_on_fedora + script: + - echo "Building with ASan and UBSan" + - meson setup "$BUILD_DIR" --prefix="$PREFIX" -D debug=true -D optimization=g -D b_sanitize=address,undefined -D session-managers= + - meson compile -C "$BUILD_DIR" $COMPILE_ARGS + - meson test -C "$BUILD_DIR" --no-rebuild # A release build with NDEBUG, all options on auto() but tests explicitly # enabled. This should show issues with tests failing due to different @@ -333,9 +344,9 @@ - .build_on_fedora script: - echo "Building with meson options $MESON_OPTIONS" - - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS - - ninja $NINJA_ARGS -C "$BUILD_DIR" - - ninja $NINJA_ARGS -C "$BUILD_DIR" install + - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS + - meson compile -C "$BUILD_DIR" $COMPILE_ARGS + - meson install -C "$BUILD_DIR" --no-rebuild variables: MESON_OPTIONS: "-Dsession-managers=$SESSION_MANAGERS" parallel: @@ -349,9 +360,9 @@ script: - pip3 install --upgrade --pre meson - echo "Building with meson options $MESON_OPTIONS" - - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS - - ninja $NINJA_ARGS -C "$BUILD_DIR" - - ninja $NINJA_ARGS -C "$BUILD_DIR" install + - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS + - meson compile -C "$BUILD_DIR" $COMPILE_ARGS + - meson install -C "$BUILD_DIR" --no-rebuild variables: MESON_OPTIONS: "-Dsession-managers=wireplumber,media-session" allow_failure: true @@ -366,9 +377,9 @@ - pip3 uninstall --yes meson - pip3 install "meson==$meson_version" - echo "Building with meson options $MESON_OPTIONS" - - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS - - ninja $NINJA_ARGS -C "$BUILD_DIR" - - ninja $NINJA_ARGS -C "$BUILD_DIR" install + - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS + - meson compile -C "$BUILD_DIR" $COMPILE_ARGS + - meson install -C "$BUILD_DIR" --no-rebuild variables: MESON_OPTIONS: "-Dsession-managers=" @@ -377,7 +388,7 @@ - .build_on_fedora script: - echo "Building with meson options $MESON_OPTIONS" - - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS + - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS - meson test -C "$BUILD_DIR" --setup=valgrind variables: MESON_OPTIONS: "-Dsession-managers=" @@ -391,7 +402,7 @@ stage: analysis script: - export PATH=/opt/coverity/bin:$PATH - - meson "$BUILD_DIR" . --prefix="$PREFIX" + - meson setup "$BUILD_DIR" --prefix="$PREFIX" -Ddocs=disabled -Dbluez5-backend-hsphfpd=enabled -Daudiotestsrc=enabled @@ -410,7 +421,7 @@ --xml-option=append_arg@C:"replace/GLIB_(DEPRECATED|AVAILABLE)_ENUMERATOR_IN_\d_\d\d(_FOR\(\w+\)|)\s+=/ =" --xml-option=append_arg@C:--ppp_translator --xml-option=append_arg@C:"replace/(__has_builtin|_GLIBCXX_HAS_BUILTIN)\(\w+\)/1" - - cov-build --dir cov-int --config coverity_conf.xml ninja $NINJA_ARGS -C "$BUILD_DIR" + - cov-build --dir cov-int --config coverity_conf.xml meson compile -C "$BUILD_DIR" $COMPILE_ARGS - tar czf cov-int.tar.gz cov-int - curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
View file
pipewire-0.3.67.tar.gz/NEWS -> pipewire-0.3.68.tar.gz/NEWS
Changed
@@ -1,3 +1,143 @@ +# PipeWire 0.3.68 (2023-04-06) + +This is a bugfix release that is API and ABI compatible with previous +0.3.x releases. + +This release contains a huge number of changes, some of which might cause +regressions. Please report anything that seems to fail after the upgrade. +UCM devices in particular might have changed names, profiles and ports that +might require changes in custom scripts. + +## Highlights + - Symbolic links to the pipewire binary are now used instead of recompiling + the same binary multiple times. + - Changes to the graph scheduler related to quantum/rate updates and + calculation of the node states. Things should start and switch between + quantums and rates more smoothly now and especially virtual devices should + now only run when required. + - A new RTP session module was added. This uses the Apple MIDI protocol + to configure low-latency bidirectional MIDI (and with a PipeWire specific + extension, also audio) between machines. OPUS encoding was added to the + RTP formats. The SAP module was separated from the rtp-sink/source module + to make it more usable. + - A new runtime debug property was added to all streams and nodes to trigger + a save of the raw samples to a wav file. Support for this has also been + added to the echo-canceler to debug potential issues. + - Module pulse-tunnel has improved rate matching and synchronization + support. It should also not drift anymore for capture devices. + - The link-factory now ignores by default the link.passive property. This means + that tools like pw-link or jack clients and wireplumber can't make passive + links anymore. The reason is that there is now much more advanced logic in + PipeWire itself to handle passive links based on node and port properties. + - The RAOP sink was ported to new OpenSSL functions. Digest passwords are + handled correctly now and support for more devices was added. + - The ACP code was updated with new PulseAudio UCM code: "Create multiple + profiles per verb for conflicting devices". This might change the names + of devices, profiles and ports so scripts might need to be updated. + - Upmixing is disabled again by default. We now ship config files that + distros can install to enable upmixing again. The reason being that PipeWire + should not apply fancy DSP processing to audio by default. + - Many cleanups and bugfixes, including some crashes and memory corruption + bugs. + +## PipeWire + - Various FreeBSD compilation fixes. + - Don't crash when calling _connect twice in stream/filter. (#3091) + - Links are now installed instead of compiling the pipewire binary + multiple times. + - There is now a new core event bound_props that augments the bound_id event + with the global properties. This can be used to get the global.serial among + other global properties. It also makes it possible in the future to let the + server allocate unique names or uuids. + - Fix a bug where the server could go into an infinite reconfigure loop when + the samplerate of a driver would change. + - When a samplerate was forced, restore the previous best samplerate when the + samplerate is no longer forced. (#2133) + - Rework how the states of the nodes in the graph are calculated. A more + refined algorithm is now used that only runs nodes that need to run. + - Rework how the quantum change is applied to the graph. Drivers are now + responsible for using the new updated rate/quantum before starting a new + cycle. This avoids starting a cycle with an old quantum first. + - pw-stream and pw-filter will now ensure that the Trigger event is called + from the main thread. + - node.force-rate=0 will now force the node.rate on the graph, forcefully + switching the hardware into the new rate if possible. (#3026) + - Additional checks were added to the thread-loop to check locking order. + - Additional checks were added to pw-stream and pw-filter to check if methods + are called from the right thread context. + +## modules + - A new RTP session module was added. This uses the Apple MIDI protocol + to configure bidirectional MIDI (or audio) between machines. + - SAP support was removed from module-rtp-source and module-rtp-sink and + moved to a separate module. This makes it possible to use the RTP modules + without SAP support as well. + - The echo-cancel module now has support to save the signals to a wav + file for debugging purposes. + - The RTP modules now have support for the OPUS codec. + - The RAOP module was ported to new openssl encryption functions and handles + digest passwords correctly now. + - module-raop-discover now has match rules to be able to select the streams + and set properties. + - Module pulse-tunnel has improved rate matching and synchronization + support. (#3093) + - Fix potential memory corruption and infinite loops because + module-pulse-tunnel was unloaded from the wrong thread. + - The link-factory now ignores by default the link.passive property. This means + that tools like pw-link or jack clients and wireplumber can't make passive + links anymore. The reason is that there is now much more advanced logic in + PipeWire itself to handle passive links based on node and port properties. + - module-echo-cancel will now clear its buffers after a suspend to avoid + playing stray samples. + - module-raop-sink will now handle 0 timing_port replies. (#3133) + +## SPA + - The adapter module now has support for saving the raw audio to a wav + file for debugging purposes. + - The ACP code was updated with new PulseAudio UCM code: "Create multiple + profiles per verb for conflicting devices". This might change the names + of devices, profiles and ports so scripts might need to be updated. + - Upmixing was disabled again by default. We now ship config files that + distros can install to enable upmixing again. (#3081) + - audioadapter and audioconvert have seen improvements in the experimental + non-DSP/passthrough mode. + - Fix a potential race where the dummy drivers could fail to stop a timer + and cause endless warnings in the logs. + - The ALSA plugin has experimental support for IRQ based scheduling. This + should decrease latency for some (mostly USB) drivers. This should bring + latency within JACK latency. More work on this will be done before the + 1.0 release later this year. + - Audioconvert now has support for volume ramping. (#3046) + - A new loop method was added the check if a thread is currently running the + loop. + - channelmix.disable and resample.disable now generate an error when true + and channelmixing or resampling is required in the converter. + +## Bluetooth + - Fix a crash in some cases when a device was disconnected. + - Support async transport state changes. This avoids some lockups when the + bluetooth backend is having issues. (#3023) + - Align BAP sinks. This improves synchronization between earpieces. + +## ALSA + - Improve properties in pw-top and pavucontrol. + +## pulse-server + - Improve error handling from pulse-tunnel. + - Generate silence correctly for unsigned formats as well. + - Review buffer params. The streams should now just work with 1 or 2 + buffers. + - module-rtp-send and module-rtp-recv now have support for the OPUS codec. + +# JACK + - Make sure we don't call any callbacks anymore when deactivating. (#2781) + +## GStreamer + - Sort the device by priority in deviceprovider. (#3072) + + +Older versions: + # PipeWire 0.3.67 (2023-03-09) This is a bugfix release that is API and ABI compatible with previous @@ -92,9 +232,6 @@ - The metadata plane count is now handled correctly in more cases. - Stream errors are now handled correctly to stop the GStreamer elements. -Older versions: - - # PipeWire 0.3.66 (2023-02-16) This is a bugfix release that is API and ABI compatible with previous
View file
pipewire-0.3.67.tar.gz/doc/index.dox -> pipewire-0.3.68.tar.gz/doc/index.dox
Changed
@@ -41,5 +41,6 @@ - PipeWire Wikipedia(https://en.wikipedia.org/wiki/PipeWire) - Bluetooth, PipeWire and Whatsapp calls(https://gjhenrique.com/pipewire.html) - Intoduction to PipeWire(https://bootlin.com/blog/an-introduction-to-pipewire/) +- A custom PipeWire node(https://bootlin.com/blog/a-custom-pipewire-node/) */
View file
pipewire-0.3.67.tar.gz/doc/pipewire-modules.dox -> pipewire-0.3.68.tar.gz/doc/pipewire-modules.dox
Changed
@@ -74,8 +74,10 @@ - \subpage page_module_raop_discover - \subpage page_module_roc_sink - \subpage page_module_roc_source +- \subpage page_module_rtp_sap - \subpage page_module_rtp_sink - \subpage page_module_rtp_source +- \subpage page_module_rtp_session - \subpage page_module_rt - \subpage page_module_session_manager - \subpage page_module_x11_bell
View file
pipewire-0.3.67.tar.gz/meson.build -> pipewire-0.3.68.tar.gz/meson.build
Changed
@@ -1,7 +1,7 @@ project('pipewire', 'c' , - version : '0.3.67', + version : '0.3.68', license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' , - meson_version : '>= 0.59.0', + meson_version : '>= 0.61.1', default_options : 'warning_level=3', 'c_std=gnu11', 'cpp_std=c++17', @@ -282,6 +282,10 @@ endif cdata.set('HAVE_PW_CAT_FFMPEG_INTEGRATION', pw_cat_ffmpeg.allowed()) +opus_dep = dependency('opus', required : get_option('opus')) +summary({'opus (Bluetooth, RTP)': opus_dep.found()}, bool_yn: true, section: 'Misc dependencies') +cdata.set('HAVE_OPUS', opus_dep.found()) + summary({'readline (for pw-cli)': readline_dep.found()}, bool_yn: true, section: 'Misc dependencies') cdata.set('HAVE_READLINE', readline_dep.found()) ncurses_dep = dependency('ncursesw', required : false)
View file
pipewire-0.3.67.tar.gz/meson_options.txt -> pipewire-0.3.68.tar.gz/meson_options.txt
Changed
@@ -318,3 +318,7 @@ min: -20, max: -1, value: -19) +option('opus', + description: 'Enable code that depends on opus', + type: 'feature', + value: 'auto')
View file
pipewire-0.3.67.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c -> pipewire-0.3.68.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c
Changed
@@ -1121,7 +1121,15 @@ pw_properties_setf(pw->props, PW_KEY_APP_NAME, "PipeWire ALSA %s", pw_get_prgname()); if (pw_properties_get(pw->props, PW_KEY_NODE_NAME) == NULL) - pw_properties_setf(pw->props, PW_KEY_NODE_NAME, "ALSA %s", + pw_properties_setf(pw->props, PW_KEY_NODE_NAME, "alsa_%s.%s", + stream == SND_PCM_STREAM_PLAYBACK ? "playback" : "capture", + pw_get_prgname()); + if (pw_properties_get(pw->props, PW_KEY_NODE_DESCRIPTION) == NULL) + pw_properties_setf(pw->props, PW_KEY_NODE_DESCRIPTION, "ALSA %s %s", + stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture", + pw_get_prgname()); + if (pw_properties_get(pw->props, PW_KEY_MEDIA_NAME) == NULL) + pw_properties_setf(pw->props, PW_KEY_MEDIA_NAME, "ALSA %s", stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); if (pw_properties_get(pw->props, PW_KEY_MEDIA_TYPE) == NULL) pw_properties_set(pw->props, PW_KEY_MEDIA_TYPE, "Audio");
View file
pipewire-0.3.67.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.68.tar.gz/pipewire-jack/src/pipewire-jack.c
Changed
@@ -898,17 +898,19 @@ spa_hook_remove(&client->node_listener); } -static void on_node_bound(void *data, uint32_t global_id) +static void on_node_bound_props(void *data, uint32_t global_id, const struct spa_dict *props) { struct client *client = data; client->node_id = global_id; + if (props) + pw_properties_update(client->props, props); } static const struct pw_proxy_events node_proxy_events = { PW_VERSION_PROXY_EVENTS, .removed = on_node_removed, .destroy = on_node_destroy, - .bound = on_node_bound, + .bound_props = on_node_bound_props, }; static struct link *find_activation(struct spa_list *links, uint32_t node_id) @@ -3798,6 +3800,8 @@ return 0; pw_thread_loop_lock(c->context.loop); + c->active = false; + pw_data_loop_stop(c->loop); pw_client_node_set_active(c->node, false); @@ -3816,12 +3820,7 @@ pw_thread_loop_unlock(c->context.loop); - if (res < 0) - return res; - - c->active = false; - - return 0; + return res; } SPA_EXPORT @@ -5414,6 +5413,8 @@ latency = SPA_LATENCY_INFO(direction); nframes = jack_get_buffer_size((jack_client_t*)c); + if (nframes == 0) + nframes = 1; latency.min_rate = range->min; if (latency.min_rate >= nframes) {
View file
pipewire-0.3.67.tar.gz/pipewire-v4l2/src/pipewire-v4l2.c -> pipewire-0.3.68.tar.gz/pipewire-v4l2/src/pipewire-v4l2.c
Changed
@@ -800,8 +800,10 @@ if ((file = find_file_by_dev(dev_id)) != NULL) { res = do_dup(file->fd, 0); unref_file(file); - if (res >= 0) - fcntl(res, F_SETFL, oflag); + if (res < 0) + return res; + if (fcntl(res, F_SETFL, oflag) < 0) + pw_log_warn("fd:%d failed to set flags: %m", res); return res; }
View file
pipewire-0.3.67.tar.gz/po/be.po -> pipewire-0.3.68.tar.gz/po/be.po
Changed
@@ -2,7 +2,8 @@ # Copyright (C) 2016 PipeWire's COPYRIGHT HOLDER # This file is distributed under the same license as the PipeWire package. # -# Viktar Vaŭčkievič <victorenator@gmail.com>, 2016. +# +# Viktar Vaŭčkievič <victorenator@gmail.com>, 2016, 2023. # msgid "" msgstr "" @@ -10,16 +11,16 @@ "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/" "issues/new\n" "POT-Creation-Date: 2021-04-18 16:54+0800\n" -"PO-Revision-Date: 2016-07-19 11:06+0300\n" +"PO-Revision-Date: 2023-03-11 01:14+0300\n" "Last-Translator: Viktar Vaŭčkievič <victorenator@gmail.com>\n" "Language-Team: Belarusian <>\n" "Language: be\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Lokalize 2.0\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Gtranslator 42.0\n" #: src/daemon/pipewire.c:43 #, c-format @@ -29,14 +30,19 @@ " --version Show version\n" " -c, --config Load config (Default %s)\n" msgstr "" +"%s параметры\n" +" -h, --help Паказаць гэту даведку\n" +" --version Паказаць версію\n" +" -c, --config Загрузіць канфігурацыю (Агаданая " +"%s)\n" #: src/daemon/pipewire.desktop.in:4 msgid "PipeWire Media System" -msgstr "" +msgstr "Медыйная сістэма PipeWire" #: src/daemon/pipewire.desktop.in:5 msgid "Start the PipeWire Media System" -msgstr "" +msgstr "Старт медыйнай сістэмы PipeWire" #: src/examples/media-session/alsa-monitor.c:526 #: spa/plugins/alsa/acp/compat.c:187 @@ -50,7 +56,7 @@ #: src/examples/media-session/alsa-monitor.c:539 msgid "Unknown device" -msgstr "" +msgstr "Невядомая прылада" #: src/tools/pw-cat.c:991 #, c-format @@ -61,6 +67,11 @@ " -v, --verbose Enable verbose operations\n" "\n" msgstr "" +"%s параметры <file>\n" +" -h, --help Паказаць гэту даведку\n" +" --version Паказаць версію\n" +" -v, --verbose Уключыць падрабязнасці\n" +"\n" #: src/tools/pw-cat.c:998 #, c-format @@ -79,6 +90,21 @@ " --list-targets List available targets for --target\n" "\n" msgstr "" +" -R, --remote Назва адаленага сэрвіса\n" +" --media-type Задаць тып медыя (агаданы %s)\n" +" --media-category Задаць катэгорыю медыя (агаданая " +"%s)\n" +" --media-role Задаць ролю медыя (агаданая %s)\n" +" --target Задаць мэтавы вузел (агаданы %s)\n" +" 0 азначае не спасылацца\n" +" --latency Задаць затрымку вузла (агаданая %s)\n" +" Xадзінка (адзінка = s, ms, us, " +"ns)\n" +" ці наўпрост сэмплы (256)\n" +" з частатой аднаго з уваходных " +"файлаў\n" +" --list-targets Ліставаць наяўныя мэты для --target\n" +"\n" #: src/tools/pw-cat.c:1016 #, c-format @@ -99,6 +125,22 @@ "%d)\n" "\n" msgstr "" +" --rate Частата сэмплаў (для запісу) " +"(агаданая %u)\n" +" --channels Колькасць каналаў (для запісу) " +"(агаданая %u)\n" +" --channel-map Мапа каналаў\n" +" адзін з: «stereo», " +"«surround-51»,... ці\n" +" назвы каналаў праз коску: пр. " +"«FL,FR»\n" +" --format Фармат сэмплаў %s (req. for rec) " +"(агаданы %s)\n" +" --volume Гучнасць патоку 0-1.0 (агаданая " +"%.3f)\n" +" -q --quality Якасць перадыскрэтызацыі (0 - 15) " +"(агаданая %d)\n" +"\n" #: src/tools/pw-cat.c:1033 msgid "" @@ -107,6 +149,10 @@ " -m, --midi Midi mode\n" "\n" msgstr "" +" -p, --playback Рэжым прайгравальніка\n" +" -r, --record Рэжым запісу\n" +" -m, --midi Midi-рэжым\n" +"\n" #: src/tools/pw-cli.c:2932 #, c-format @@ -118,10 +164,17 @@ " -r, --remote Remote daemon name\n" "\n" msgstr "" +"%s параметры каманда\n" +" -h, --help Паказаць гэту даведку\n" +" --version Паказаць версію\n" +" -d, --daemon Запусціць як фонавы сэрвіс (Default " +"false)\n" +" -r, --remote Назва адаленага сэрвіса\n" +"\n" #: spa/plugins/alsa/acp/acp.c:290 msgid "Pro Audio" -msgstr "" +msgstr "Pro Audio" #: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704 #: spa/plugins/bluez5/bluez5-device.c:1000 @@ -247,14 +300,12 @@ msgstr "Аналагавы выхад" #: spa/plugins/alsa/acp/alsa-mixer.c:2809 -#, fuzzy msgid "Headphones 2" -msgstr "Навушнікі" +msgstr "Навушнікі 2" #: spa/plugins/alsa/acp/alsa-mixer.c:2810 -#, fuzzy msgid "Headphones Mono Output" -msgstr "Аналагавы монавыхад" +msgstr "Навушнікавы монавыхад" #: spa/plugins/alsa/acp/alsa-mixer.c:2811 msgid "Line Out" @@ -289,39 +340,33 @@ msgstr "Шматканальны выхад" #: spa/plugins/alsa/acp/alsa-mixer.c:2819 -#, fuzzy msgid "Game Output" -msgstr "%s выхад" +msgstr "Гульнявы выхад" #: spa/plugins/alsa/acp/alsa-mixer.c:2820 #: spa/plugins/alsa/acp/alsa-mixer.c:2821 -#, fuzzy msgid "Chat Output" -msgstr "%s выхад" +msgstr "Чатавы выхад" #: spa/plugins/alsa/acp/alsa-mixer.c:2822 -#, fuzzy msgid "Chat Input" -msgstr "%s уваход" +msgstr "Чатавы уваход" #: spa/plugins/alsa/acp/alsa-mixer.c:2823 -#, fuzzy msgid "Virtual Surround 7.1" -msgstr "Віртуальны абʼёмны прыёмнік" +msgstr "Віртуальны абʼёмны 7.1" #: spa/plugins/alsa/acp/alsa-mixer.c:4527 msgid "Analog Mono" msgstr "Аналагавы мона" #: spa/plugins/alsa/acp/alsa-mixer.c:4528 -#, fuzzy
View file
pipewire-0.3.67.tar.gz/po/ru.po -> pipewire-0.3.68.tar.gz/po/ru.po
Changed
@@ -10,19 +10,19 @@ "Project-Id-Version: pipewire\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" "issues\n" -"POT-Creation-Date: 2021-05-16 13:13+0000\n" -"PO-Revision-Date: 2021-06-30 13:26+0300\n" -"Last-Translator: Alexey Rubtsov <rushills@gmail.com>\n" +"POT-Creation-Date: 2023-04-06 03:27+0000\n" +"PO-Revision-Date: 2023-04-06 10:51+0300\n" +"Last-Translator: Aleksandr Melman <Alexmelman88@gmail.com>\n" "Language-Team: ru\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Poedit 3.0\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Poedit 3.2.2\n" -#: src/daemon/pipewire.c:45 +#: src/daemon/pipewire.c:26 #, c-format msgid "" "%s options\n" @@ -33,8 +33,8 @@ "%s опции\n" " -h, --help Показать справку\n" " --version Информация о версии\n" -" -c, --config Указать файл конфигурации (По " -"умолчанию %s)\n" +" -c, --config Загрузить конфигурацию (По умолчанию " +"%s)\n" #: src/daemon/pipewire.desktop.in:4 msgid "PipeWire Media System" @@ -44,72 +44,66 @@ msgid "Start the PipeWire Media System" msgstr "Запустить PipeWire" -#: src/examples/media-session/alsa-monitor.c:585 -#: spa/plugins/alsa/acp/compat.c:187 -msgid "Built-in Audio" -msgstr "Встроенное аудио" - -#: src/examples/media-session/alsa-monitor.c:589 -#: spa/plugins/alsa/acp/compat.c:192 -msgid "Modem" -msgstr "Модем" - -#: src/examples/media-session/alsa-monitor.c:598 -#: src/modules/module-zeroconf-discover.c:290 -msgid "Unknown device" -msgstr "Неизвестное устройство" - -#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:182 -#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:182 +#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159 +#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159 #, c-format msgid "Tunnel to %s/%s" msgstr "Туннель на %s/%s" -#: src/modules/module-pulse-tunnel.c:511 +#: src/modules/module-fallback-sink.c:31 +msgid "Dummy Output" +msgstr "Холостой выход" + +#: src/modules/module-pulse-tunnel.c:688 #, c-format msgid "Tunnel for %s@%s" msgstr "Туннель для %s@%s" -#: src/modules/module-zeroconf-discover.c:302 +#: src/modules/module-zeroconf-discover.c:315 +msgid "Unknown device" +msgstr "Неизвестное устройство" + +#: src/modules/module-zeroconf-discover.c:327 #, c-format msgid "%s on %s@%s" msgstr "%s на %s@%s" -#: src/modules/module-zeroconf-discover.c:306 +#: src/modules/module-zeroconf-discover.c:331 #, c-format msgid "%s on %s" msgstr "%s по %s" -#: src/tools/pw-cat.c:991 +#: src/tools/pw-cat.c:974 #, c-format msgid "" -"%s options <file>\n" +"%s options <file>|-\n" " -h, --help Show this help\n" " --version Show version\n" " -v, --verbose Enable verbose operations\n" "\n" msgstr "" -"%s опции <файл>\n" +"%s опции <file>|-\n" " -h, --help Показать справку\n" " --version Информация о версии\n" " -v, --verbose Включить показ подробной информации\n" "\n" -#: src/tools/pw-cat.c:998 +#: src/tools/pw-cat.c:981 #, c-format msgid "" " -R, --remote Remote daemon name\n" " --media-type Set media type (default %s)\n" " --media-category Set media category (default %s)\n" " --media-role Set media role (default %s)\n" -" --target Set node target (default %s)\n" +" --target Set node target serial or name " +"(default %s)\n" " 0 means don't link\n" " --latency Set node latency (default %s)\n" " Xunit (unit = s, ms, us, ns)\n" " or direct samples (256)\n" " the rate is the one of the source " "file\n" -" --list-targets List available targets for --target\n" +" -P --properties Set node properties\n" "\n" msgstr "" " -R, --remote Имя удаленного фоновой службы\n" @@ -119,7 +113,8 @@ "умолчанию %s)\n" " --media-role Задать роль мультимедиа (по " "умолчанию %s)\n" -" --target Задать цель узла (по умолчанию %s)\n" +" --target Задать серийный номер или имя " +"целевого узла (по умолчанию %s)\n" " 0 значит не связывать\n" " --latency Задать задежку узла (по умолчанию " "%s)\n" @@ -128,11 +123,10 @@ " or direct samples (256)\n" " частота такая же как у источника " "file\n" -" --list-targets Перечислить доступные цели для --" -"target\n" +" -P --properties Задать свойства узла\n" "\n" -#: src/tools/pw-cat.c:1016 +#: src/tools/pw-cat.c:999 #, c-format msgid "" " --rate Sample rate (req. for rec) (default " @@ -167,19 +161,23 @@ "умолчанию %d)\n" "\n" -#: src/tools/pw-cat.c:1033 +#: src/tools/pw-cat.c:1016 msgid "" " -p, --playback Playback mode\n" " -r, --record Recording mode\n" " -m, --midi Midi mode\n" +" -d, --dsd DSD mode\n" +" -o, --encoded Encoded mode\n" "\n" msgstr "" " -p, --playback Режим проигрывания\n" " -r, --record Режим записи\n" " -m, --midi Режим MIDI\n" +" -d, --dsd Режим DSD\n" +" -o, --encoded Режим кодирования\n" "\n" -#: src/tools/pw-cli.c:2959 +#: src/tools/pw-cli.c:2220 #, c-format msgid "" "%s options command\n" @@ -187,6 +185,7 @@ " --version Show version\n" " -d, --daemon Start as daemon (Default false)\n" " -r, --remote Remote daemon name\n" +" -m, --monitor Monitor activity\n" "\n" msgstr "" "%s опции команда\n" @@ -195,197 +194,198 @@ " -d, --daemon Запустить в режиме фоновой службы " "(По умолчанию false)\n" " -r, --remote Имя удаленного фоновой службы\n" +" -m, --monitor Контроль активности\n" "\n" -#: spa/plugins/alsa/acp/acp.c:291 +#: spa/plugins/alsa/acp/acp.c:303 msgid "Pro Audio" msgstr "Pro Audio"
View file
pipewire-0.3.67.tar.gz/spa/examples/adapter-control.c -> pipewire-0.3.68.tar.gz/spa/examples/adapter-control.c
Changed
@@ -24,6 +24,7 @@ #include <errno.h> #include <pthread.h> #include <poll.h> +#include <getopt.h> #include <spa/control/control.h> #include <spa/graph/graph.h> @@ -45,6 +46,22 @@ #define MIN_LATENCY 1024 #define CONTROL_BUFFER_SIZE 32768 +#define DEFAULT_RAMP_SAMPLES (64*1*1024) +#define DEFAULT_RAMP_STEP_SAMPLES 200 + +#define DEFAULT_RAMP_TIME 2000 // 2 seconds +#define DEFAULT_RAMP_STEP_TIME 5000 // 5 milli seconds + +#define DEFAULT_DEVICE "hw:0,0" + +#define LINEAR "linear" +#define CUBIC "cubic" +#define DEFAULT_SCALE SPA_AUDIO_VOLUME_RAMP_LINEAR + +#define NON_NATIVE "non-native" +#define NATIVE "native" +#define DEFAULT_MODE NON_NATIVE + struct buffer { struct spa_buffer buffer; @@ -91,11 +108,22 @@ double volume_accum; uint32_t volume_offs; + const char *alsa_device; + + const char *mode; + enum spa_audio_volume_ramp_scale scale; + + uint32_t volume_ramp_samples; + uint32_t volume_ramp_step_samples; + uint32_t volume_ramp_time; + uint32_t volume_ramp_step_time; + bool running; pthread_t thread; }; -static int load_handle(struct data *data, struct spa_handle **handle, const char *lib, const char *name) +static int load_handle (struct data *data, struct spa_handle **handle, const + char *lib, const char *name, struct spa_dict *info) { int res; void *hnd; @@ -134,7 +162,7 @@ *handle = calloc(1, spa_handle_factory_get_size(factory, NULL)); if ((res = spa_handle_factory_init(factory, *handle, - NULL, data->support, + info, data->support, data->n_support)) < 0) { printf("can't make factory instance: %d\n", res); goto exit_cleanup; @@ -153,6 +181,8 @@ int res; const char *str; struct spa_handle *handle = NULL; + struct spa_dict_item items 2; + struct spa_dict info; void *iface; if ((str = getenv("SPA_PLUGIN_DIR")) == NULL) @@ -167,14 +197,23 @@ /* init the graph */ spa_graph_init(&data->graph, &data->graph_state); - /* set the default log */ - data->log = &default_log.log; + /* enable the debug messages in SPA */ + items 0 = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_TIMESTAMP, "true"); + info = SPA_DICT_INIT(items, 1); + if ((res = load_handle (data, &handle, "support/libspa-support.so", + SPA_NAME_SUPPORT_LOG, &info)) < 0) + return res; + if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Log, &iface)) < 0) { + printf("can't get System interface %d\n", res); + return res; + } + data->log = iface; data->supportdata->n_support++ = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, data->log); /* load and set support system */ if ((res = load_handle(data, &handle, "support/libspa-support.so", - SPA_NAME_SUPPORT_SYSTEM)) < 0) + SPA_NAME_SUPPORT_SYSTEM, NULL)) < 0) return res; if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_System, &iface)) < 0) { printf("can't get System interface %d\n", res); @@ -187,7 +226,7 @@ /* load and set support loop and loop control */ if ((res = load_handle(data, &handle, "support/libspa-support.so", - SPA_NAME_SUPPORT_LOOP)) < 0) + SPA_NAME_SUPPORT_LOOP, NULL)) < 0) return res; if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Loop, &iface)) < 0) { @@ -270,68 +309,159 @@ return res; } -static int fade_in(struct data *data) +static int get_ramp_samples(struct data *data) { - struct spa_pod_builder b; - struct spa_pod_frame f1; - void *buffer = data->control_buffer->datas0.data; - uint32_t buffer_size = data->control_buffer->datas0.maxsize; - data->control_buffer->datas0.chunk0.size = buffer_size; + int samples = -1; + if (data->volume_ramp_samples) + samples = data->volume_ramp_samples; + else if (data->volume_ramp_time) { + samples = (data->volume_ramp_time * 48000) / 1000; + } + if (!samples) + samples = -1; - printf ("fading in\n"); + return samples; +} - spa_pod_builder_init(&b, buffer, buffer_size); - spa_pod_builder_push_sequence(&b, &f0, 0); - data->volume_offs = 0; - do { - spa_pod_builder_control(&b, data->volume_offs, SPA_CONTROL_Properties); +static int get_ramp_step_samples(struct data *data) +{ + int samples = -1; + if (data->volume_ramp_step_samples) + samples = data->volume_ramp_step_samples; + else if (data->volume_ramp_step_time) { + /* convert the step time which is in nano seconds to seconds */ + samples = (data->volume_ramp_step_time / 1000) * (48000 / 1000); + } + if (!samples) + samples = -1; + + return samples; +} + +static double get_volume_at_scale(struct data *data) +{ + if (data->scale == SPA_AUDIO_VOLUME_RAMP_LINEAR) + return data->volume_accum; + else if (data->scale == SPA_AUDIO_VOLUME_RAMP_CUBIC) + return (data->volume_accum * data->volume_accum * data->volume_accum); + + return 0.0; +} + +static int fade_in(struct data *data) +{ + printf("fading in\n"); + if (spa_streq (data->mode, NON_NATIVE)) { + struct spa_pod_builder b; + struct spa_pod_frame f1; + void *buffer = data->control_buffer->datas0.data; + int ramp_samples = get_ramp_samples(data); + int ramp_step_samples = get_ramp_step_samples(data); + double step_size = ((double) ramp_step_samples / (double) ramp_samples); + uint32_t buffer_size = data->control_buffer->datas0.maxsize; + data->control_buffer->datas0.chunk0.size = buffer_size; + + spa_pod_builder_init(&b, buffer, buffer_size); + spa_pod_builder_push_sequence(&b, &f0, 0); + data->volume_offs = 0; + do { + // printf("volume level %f offset %d\n", get_volume_at_scale(data), data->volume_offs); + spa_pod_builder_control(&b, data->volume_offs, SPA_CONTROL_Properties); spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, 0, + SPA_PROP_volume, SPA_POD_Float(get_volume_at_scale(data))); + data->volume_accum += step_size; + data->volume_offs += ramp_step_samples; + } while (data->volume_accum < 1.0); + spa_pod_builder_pop(&b, &f0); + } + else { + struct spa_pod_builder b; + struct spa_pod *props; + int res = 0; + uint8_t buffer1024; + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + props = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_Props, 0, - SPA_PROP_volume, SPA_POD_Float(data->volume_accum));
View file
pipewire-0.3.67.tar.gz/spa/include/spa/node/io.h -> pipewire-0.3.68.tar.gz/spa/include/spa/node/io.h
Changed
@@ -124,7 +124,13 @@ * positive for capture, negative for playback */ double rate_diff; /**< rate difference between clock and monotonic time */ uint64_t next_nsec; /**< estimated next wakeup time in nanoseconds */ - uint32_t padding8; + + struct spa_fraction target_rate; /**< target rate of next cycle */ + uint64_t target_duration; /**< target duration of next cycle */ + uint32_t target_seq; /**< seq counter. must be equal at start and + * end of read and lower bit must be 0 */ + + uint32_t padding3; }; /* the size of the video in this cycle */
View file
pipewire-0.3.67.tar.gz/spa/include/spa/param/audio/format.h -> pipewire-0.3.68.tar.gz/spa/include/spa/param/audio/format.h
Changed
@@ -28,6 +28,7 @@ #include <spa/param/audio/alac.h> #include <spa/param/audio/flac.h> #include <spa/param/audio/ape.h> +#include <spa/param/audio/opus.h> struct spa_audio_info { uint32_t media_type; @@ -46,6 +47,7 @@ struct spa_audio_info_alac alac; struct spa_audio_info_flac flac; struct spa_audio_info_ape ape; + struct spa_audio_info_ape opus; } info; };
View file
pipewire-0.3.68.tar.gz/spa/include/spa/param/audio/opus.h
Added
@@ -0,0 +1,29 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_OPUS_H +#define SPA_AUDIO_OPUS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <spa/param/audio/raw.h> + +struct spa_audio_info_opus { + uint32_t rate; /*< sample rate */ + uint32_t channels; /*< number of channels */ +}; + +#define SPA_AUDIO_INFO_OPUS_INIT(...) ((struct spa_audio_info_opus) { __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_OPUS_H */
View file
pipewire-0.3.67.tar.gz/spa/include/spa/param/audio/raw.h -> pipewire-0.3.68.tar.gz/spa/include/spa/param/audio/raw.h
Changed
@@ -261,6 +261,12 @@ SPA_AUDIO_CHANNEL_START_Custom = 0x10000, }; +enum spa_audio_volume_ramp_scale { + SPA_AUDIO_VOLUME_RAMP_INVALID, + SPA_AUDIO_VOLUME_RAMP_LINEAR, + SPA_AUDIO_VOLUME_RAMP_CUBIC, +}; + /** Extra audio flags */ #define SPA_AUDIO_FLAG_NONE (0) /*< no valid flag */ #define SPA_AUDIO_FLAG_UNPOSITIONED (1 << 0) /*< the position array explicitly
View file
pipewire-0.3.67.tar.gz/spa/include/spa/param/format-types.h -> pipewire-0.3.68.tar.gz/spa/include/spa/param/format-types.h
Changed
@@ -63,6 +63,7 @@ { SPA_MEDIA_SUBTYPE_alac, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "alac", NULL }, { SPA_MEDIA_SUBTYPE_flac, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "flac", NULL }, { SPA_MEDIA_SUBTYPE_ape, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "ape", NULL }, + { SPA_MEDIA_SUBTYPE_opus, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "opus", NULL }, /* video subtypes */ { SPA_MEDIA_SUBTYPE_h264, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "h264", NULL }, { SPA_MEDIA_SUBTYPE_mjpg, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mjpg", NULL },
View file
pipewire-0.3.67.tar.gz/spa/include/spa/param/format.h -> pipewire-0.3.68.tar.gz/spa/include/spa/param/format.h
Changed
@@ -51,6 +51,7 @@ SPA_MEDIA_SUBTYPE_alac, /** since 0.3.65 */ SPA_MEDIA_SUBTYPE_flac, /** since 0.3.65 */ SPA_MEDIA_SUBTYPE_ape, /** since 0.3.65 */ + SPA_MEDIA_SUBTYPE_opus, /** since 0.3.68 */ SPA_MEDIA_SUBTYPE_START_Video = 0x20000, SPA_MEDIA_SUBTYPE_h264,
View file
pipewire-0.3.67.tar.gz/spa/include/spa/param/props-types.h -> pipewire-0.3.68.tar.gz/spa/include/spa/param/props-types.h
Changed
@@ -58,6 +58,11 @@ { SPA_PROP_softMute, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "softMute", NULL }, { SPA_PROP_softVolumes, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "softVolumes", spa_type_prop_float_array }, { SPA_PROP_iec958Codecs, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "iec958Codecs", spa_type_prop_iec958_codec }, + { SPA_PROP_volumeRampSamples, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampSamples", NULL }, + { SPA_PROP_volumeRampStepSamples, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampStepSamples", NULL }, + { SPA_PROP_volumeRampTime, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampTime", NULL }, + { SPA_PROP_volumeRampStepTime, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampStepTime", NULL }, + { SPA_PROP_volumeRampScale, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "volumeRampScale", NULL }, { SPA_PROP_brightness, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "brightness", NULL }, { SPA_PROP_contrast, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "contrast", NULL },
View file
pipewire-0.3.67.tar.gz/spa/include/spa/param/props.h -> pipewire-0.3.68.tar.gz/spa/include/spa/param/props.h
Changed
@@ -59,7 +59,7 @@ SPA_PROP_START_Audio = 0x10000, /**< audio related properties */ SPA_PROP_waveType, SPA_PROP_frequency, - SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 normal */ + SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 normal */ SPA_PROP_mute, /**< mute (Bool) */ SPA_PROP_patternType, SPA_PROP_ditherType, @@ -80,6 +80,14 @@ SPA_PROP_iec958Codecs, /**< enabled IEC958 (S/PDIF) codecs, * (Array (Id enum spa_audio_iec958_codec) */ + SPA_PROP_volumeRampSamples, /**< Samples to ramp the volume over */ + SPA_PROP_volumeRampStepSamples, /**< Step or incremental Samples to ramp + * the volume over */ + SPA_PROP_volumeRampTime, /**< Time in millisec to ramp the volume over */ + SPA_PROP_volumeRampStepTime, /**< Step or incremental Time in nano seconds + * to ramp the */ + SPA_PROP_volumeRampScale, /**< the scale or graph to used to ramp the + * volume */ SPA_PROP_START_Video = 0x20000, /**< video related properties */ SPA_PROP_brightness,
View file
pipewire-0.3.67.tar.gz/spa/include/spa/support/loop.h -> pipewire-0.3.68.tar.gz/spa/include/spa/support/loop.h
Changed
@@ -28,7 +28,7 @@ struct spa_loop { struct spa_interface iface; }; #define SPA_TYPE_INTERFACE_LoopControl SPA_TYPE_INFO_INTERFACE_BASE "LoopControl" -#define SPA_VERSION_LOOP_CONTROL 0 +#define SPA_VERSION_LOOP_CONTROL 1 struct spa_loop_control { struct spa_interface iface; }; #define SPA_TYPE_INTERFACE_LoopUtils SPA_TYPE_INFO_INTERFACE_BASE "LoopUtils" @@ -140,7 +140,7 @@ struct spa_loop_control_methods { /* the version of this structure. This can be used to expand this * structure in the future */ -#define SPA_VERSION_LOOP_CONTROL_METHODS 0 +#define SPA_VERSION_LOOP_CONTROL_METHODS 1 uint32_t version; int (*get_fd) (void *object); @@ -182,6 +182,16 @@ * The number of dispatched fds is returned. */ int (*iterate) (void *object, int timeout); + + /** Check context of the loop + * \param ctrl the control + * + * This function will check if the current thread is currently the + * one that did the enter call. Since version 1:1. + * + * returns 1 on success, 0 or negative errno value on error. + */ + int (*check) (void *object); }; #define spa_loop_control_method_v(o,method,version,...) \ @@ -207,6 +217,7 @@ #define spa_loop_control_enter(l) spa_loop_control_method_v(l,enter,0) #define spa_loop_control_leave(l) spa_loop_control_method_v(l,leave,0) #define spa_loop_control_iterate(l,...) spa_loop_control_method_r(l,iterate,0,__VA_ARGS__) +#define spa_loop_control_check(l) spa_loop_control_method_r(l,check,1) typedef void (*spa_source_io_func_t) (void *data, int fd, uint32_t mask); typedef void (*spa_source_idle_func_t) (void *data);
View file
pipewire-0.3.67.tar.gz/spa/meson.build -> pipewire-0.3.68.tar.gz/spa/meson.build
Changed
@@ -74,10 +74,13 @@ endif endif summary({'LC3plus': lc3plus_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') - opus_dep = dependency('opus', required : get_option('bluez5-codec-opus')) + if get_option('bluez5-codec-opus').enabled() and not opus_dep.found() + error('bluez5-codec-opus enabled, but opus dependency not found') + endif summary({'Opus': opus_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') lc3_dep = dependency('lc3', required : get_option('bluez5-codec-lc3')) summary({'LC3': lc3_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') + cdata.set('HAVE_BLUETOOTH_BAP', get_option('bluez5-codec-lc3').allowed() and lc3_dep.found()) if get_option('bluez5-backend-hsp-native').allowed() or get_option('bluez5-backend-hfp-native').allowed() mm_dep = dependency('ModemManager', version : '>= 1.10.0', required : get_option('bluez5-backend-native-mm')) summary({'ModemManager': mm_dep.found()}, bool_yn: true, section: 'Bluetooth backends')
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/acp/acp.c -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/acp.c
Changed
@@ -362,7 +362,7 @@ devstr, NULL, &m->sample_spec, &m->channel_map, SND_PCM_STREAM_PLAYBACK, &try_period_size, &try_buffer_size, - 0, NULL, NULL, false))) { + 0, NULL, NULL, NULL, NULL, false))) { pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm); pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index); pa_alsa_close(&m->output_pcm); @@ -392,7 +392,7 @@ devstr, NULL, &m->sample_spec, &m->channel_map, SND_PCM_STREAM_CAPTURE, &try_period_size, &try_buffer_size, - 0, NULL, NULL, false))) { + 0, NULL, NULL, NULL, NULL, false))) { pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm); pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index); pa_alsa_close(&m->input_pcm); @@ -449,8 +449,8 @@ pa_dynarray_append(&impl->out.devices, dev); } if (impl->use_ucm) { - if (m->ucm_context.ucm_devices) { - pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context, + if (m->ucm_context.ucm_device) { + pa_alsa_ucm_add_port(NULL, &m->ucm_context, true, impl->ports, ap, NULL); pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context, true, impl, dev->pcm_handle, impl->profile_set->ignore_dB); @@ -473,8 +473,8 @@ } if (impl->use_ucm) { - if (m->ucm_context.ucm_devices) { - pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context, + if (m->ucm_context.ucm_device) { + pa_alsa_ucm_add_port(NULL, &m->ucm_context, false, impl->ports, ap, NULL); pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context, false, impl, dev->pcm_handle, impl->profile_set->ignore_dB); @@ -608,7 +608,7 @@ static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) { pa_card *impl = snd_mixer_elem_get_callback_private(melem); - snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem); + snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem; snd_ctl_elem_value_t *elem_value; bool plugged_in, any_input_port_available; void *state; @@ -618,6 +618,8 @@ enum acp_available active_available = ACP_AVAILABLE_UNKNOWN; size_t size; + pa_assert(_elem); + elem = *_elem; #if 0 /* Changing the jack state may cause a port change, and a port change will * make the sink or source change the mixer settings. If there are multiple @@ -886,13 +888,17 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask) { pa_card *impl = snd_mixer_elem_get_callback_private(melem); - snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem); - int device = snd_hctl_elem_get_device(elem); + snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem; + int device; const char *old_monitor_name; pa_device_port *p; pa_hdmi_eld eld; bool changed = false; + pa_assert(_elem); + elem = *_elem; + device = snd_hctl_elem_get_device(elem); + if (mask == SND_CTL_EVENT_MASK_REMOVE) return 0; @@ -1253,8 +1259,7 @@ * will be NULL, but the UCM device enable sequence will still need to be * executed. */ if (dev->active_port && dev->ucm_context) { - if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port, - dev->direction == PA_ALSA_DIRECTION_OUTPUT)) < 0) + if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port)) < 0) return res; } @@ -1429,8 +1434,7 @@ /* if UCM is available for this card then update the verb */ if (impl->use_ucm && !(np->profile.flags & ACP_PROFILE_PRO)) { if ((res = pa_alsa_ucm_set_profile(&impl->ucm, impl, - np->profile.flags & ACP_PROFILE_OFF ? NULL : np->profile.name, - op ? op->profile.name : NULL)) < 0) { + np->profile.flags & ACP_PROFILE_OFF ? NULL : np, op)) < 0) { return res; } } @@ -1439,8 +1443,8 @@ PA_IDXSET_FOREACH(am, np->output_mappings, idx) { if (impl->use_ucm) { /* Update ports priorities */ - if (am->ucm_context.ucm_devices) { - pa_alsa_ucm_add_ports_combination(am->output.ports, &am->ucm_context, + if (am->ucm_context.ucm_device) { + pa_alsa_ucm_add_port(am->output.ports, &am->ucm_context, true, impl->ports, np, NULL); } } @@ -1452,8 +1456,8 @@ PA_IDXSET_FOREACH(am, np->input_mappings, idx) { if (impl->use_ucm) { /* Update ports priorities */ - if (am->ucm_context.ucm_devices) { - pa_alsa_ucm_add_ports_combination(am->input.ports, &am->ucm_context, + if (am->ucm_context.ucm_device) { + pa_alsa_ucm_add_port(am->input.ports, &am->ucm_context, false, impl->ports, np, NULL); } } @@ -1589,7 +1593,7 @@ res = impl->use_ucm ? pa_alsa_ucm_query_profiles(&impl->ucm, card->index) : -1; if (res == -PA_ALSA_ERR_UCM_LINKED) { - res = -ENOENT; + res = -EEXIST; goto error; } if (res == 0) { @@ -1844,8 +1848,7 @@ mixer_volume_init(impl, d); sync_mixer(d, p); - res = pa_alsa_ucm_set_port(d->ucm_context, p, - dev->direction == ACP_DIRECTION_PLAYBACK); + res = pa_alsa_ucm_set_port(d->ucm_context, p); } else { pa_alsa_port_data *data;
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/acp/alsa-mixer.c -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-mixer.c
Changed
@@ -4734,35 +4734,29 @@ PA_ELEMENTSOF(well_known_descriptions))); if (!p->description) { + pa_strbuf *sb; uint32_t idx; pa_alsa_mapping *m; - char *ptr; - size_t size; - FILE *f; - int count = 0; - - f = open_memstream(&ptr, &size); - if (f == NULL) { - pa_log("failed to open memstream: %m"); - return -1; - } + + sb = pa_strbuf_new(); if (p->output_mappings) PA_IDXSET_FOREACH(m, p->output_mappings, idx) { - if (count++ > 0) - fprintf(f, " + "); - fprintf(f, _("%s Output"), m->description); + if (!pa_strbuf_isempty(sb)) + pa_strbuf_puts(sb, " + "); + + pa_strbuf_printf(sb, _("%s Output"), m->description); } if (p->input_mappings) PA_IDXSET_FOREACH(m, p->input_mappings, idx) { - if (count++ > 0) - fprintf(f, " + "); - fprintf(f, _("%s Input"), m->description); + if (!pa_strbuf_isempty(sb)) + pa_strbuf_puts(sb, " + "); + + pa_strbuf_printf(sb, _("%s Input"), m->description); } - fclose(f); - p->description = ptr; + p->description = pa_strbuf_to_string_free(sb); } return 0; @@ -4814,23 +4808,17 @@ pa_assert(db_fix); if (db_fix->db_values) { + pa_strbuf *buf; unsigned long i, nsteps; - FILE *f; - char *ptr; - size_t size; - - f = open_memstream(&ptr, &size); - if (f == NULL) - return; pa_assert(db_fix->min_step <= db_fix->max_step); nsteps = db_fix->max_step - db_fix->min_step + 1; + buf = pa_strbuf_new(); for (i = 0; i < nsteps; ++i) - fprintf(f, "%li:%0.2f ", i + db_fix->min_step, db_fix->db_valuesi / 100.0); + pa_strbuf_printf(buf, "%li:%0.2f ", i + db_fix->min_step, db_fix->db_valuesi / 100.0); - fclose(f); - db_values = ptr; + db_values = pa_strbuf_to_string_free(buf); } pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s", @@ -5012,7 +5000,7 @@ handle = pa_alsa_open_by_template( m->device_strings, dev_id, NULL, &try_ss, &try_map, mode, &try_period_size, - &try_buffer_size, 0, NULL, NULL, exact_channels); + &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels); if (handle && !exact_channels && m->channel_map.channels != try_map.channels) { char bufPA_CHANNEL_MAP_SNPRINT_MAX; pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/acp/alsa-mixer.h -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-mixer.h
Changed
@@ -354,7 +354,7 @@ pa_alsa_device output; pa_alsa_device input; - /* ucm device context*/ + /* ucm device context */ pa_alsa_ucm_mapping_context ucm_context; }; @@ -381,6 +381,9 @@ pa_idxset *input_mappings; pa_idxset *output_mappings; + /* ucm device context */ + pa_alsa_ucm_profile_context ucm_context; + struct { pa_dynarray devices; } out;
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/acp/alsa-ucm.c -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-ucm.c
Changed
@@ -72,9 +72,8 @@ static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port, - pa_alsa_ucm_device **devices, unsigned n_devices); + pa_alsa_ucm_device *device); static void ucm_port_data_free(pa_device_port *port); -static void ucm_port_update_available(pa_alsa_ucm_port_data *port); static struct ucm_type types = { {"None", PA_DEVICE_PORT_TYPE_UNKNOWN}, @@ -170,17 +169,6 @@ return (char *)value; } -static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) { - pa_alsa_ucm_device *d; - uint32_t idx; - - PA_IDXSET_FOREACH(d, idxset, idx) - if (d == dev) - return 1; - - return 0; -} - static void ucm_add_devices_to_idxset( pa_idxset *idxset, pa_alsa_ucm_device *me, @@ -506,10 +494,10 @@ n_confdev = snd_use_case_get_list(uc_mgr, id, &devices); pa_xfree(id); + device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); if (n_confdev <= 0) pa_log_debug("No %s for device %s", "_conflictingdevs", device_name); else { - device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev); snd_use_case_free_list(devices, n_confdev); } @@ -518,10 +506,10 @@ n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices); pa_xfree(id); + device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); if (n_suppdev <= 0) pa_log_debug("No %s for device %s", "_supporteddevs", device_name); else { - device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev); snd_use_case_free_list(devices, n_suppdev); } @@ -530,10 +518,16 @@ }; /* Create a property list for this ucm modifier */ -static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) { +static int ucm_get_modifier_property( + pa_alsa_ucm_modifier *modifier, + snd_use_case_mgr_t *uc_mgr, + pa_alsa_ucm_verb *verb, + const char *modifier_name) { const char *value; char *id; int i; + const char **devices; + int n_confdev, n_suppdev; for (i = 0; itemi.id; i++) { int err; @@ -550,16 +544,28 @@ } id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name); - modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices); + n_confdev = snd_use_case_get_list(uc_mgr, id, &devices); pa_xfree(id); - if (modifier->n_confdev < 0) + + modifier->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + if (n_confdev <= 0) pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name); + else { + ucm_add_devices_to_idxset(modifier->conflicting_devices, NULL, verb->devices, devices, n_confdev); + snd_use_case_free_list(devices, n_confdev); + } id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name); - modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices); + n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices); pa_xfree(id); - if (modifier->n_suppdev < 0) + + modifier->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + if (n_suppdev <= 0) pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name); + else { + ucm_add_devices_to_idxset(modifier->supported_devices, NULL, verb->devices, devices, n_suppdev); + snd_use_case_free_list(devices, n_suppdev); + } return 0; }; @@ -596,6 +602,59 @@ return 0; }; +static long ucm_device_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) { + const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); + char *devstatus; + long status = 0; + + devstatus = pa_sprintf_malloc("_devstatus/%s", dev_name); + if (snd_use_case_geti(ucm->ucm_mgr, devstatus, &status) < 0) { + pa_log_debug("Failed to get status for UCM device %s", dev_name); + status = -1; + } + pa_xfree(devstatus); + + return status; +} + +static int ucm_device_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) { + const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); + + /* If any of dev's conflicting devices is enabled, trying to disable + * dev gives an error despite the fact that it's already disabled. + * Check that dev is enabled to avoid this error. */ + if (ucm_device_status(ucm, dev) == 0) { + pa_log_debug("UCM device %s is already disabled", dev_name); + return 0; + } + + pa_log_debug("Disabling UCM device %s", dev_name); + if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) < 0) { + pa_log("Failed to disable UCM device %s", dev_name); + return -1; + } + + return 0; +} + +static int ucm_device_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) { + const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); + + /* We don't need to enable devices that are already enabled */ + if (ucm_device_status(ucm, dev) > 0) { + pa_log_debug("UCM device %s is already enabled", dev_name); + return 0; + } + + pa_log_debug("Enabling UCM device %s", dev_name); + if (snd_use_case_set(ucm->ucm_mgr, "_enadev", dev_name) < 0) { + pa_log("Failed to enable UCM device %s", dev_name); + return -1; + } + + return 0; +} + static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) { const char **mod_list; int num_mod, i; @@ -626,6 +685,57 @@ return 0; }; +static long ucm_modifier_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) { + const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); + char *modstatus; + long status = 0; + + modstatus = pa_sprintf_malloc("_modstatus/%s", mod_name); + if (snd_use_case_geti(ucm->ucm_mgr, modstatus, &status) < 0) { + pa_log_debug("Failed to get status for UCM modifier %s", mod_name); + status = -1; + } + pa_xfree(modstatus); + + return status; +} + +static int ucm_modifier_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) { + const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); + + /* We don't need to disable modifiers that are already disabled */ + if (ucm_modifier_status(ucm, mod) == 0) { + pa_log_debug("UCM modifier %s is already disabled", mod_name); + return 0; + } + + pa_log_debug("Disabling UCM modifier %s", mod_name); + if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) { + pa_log("Failed to disable UCM modifier %s", mod_name); + return -1; + } + + return 0; +}
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/acp/alsa-ucm.h -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-ucm.h
Changed
@@ -142,12 +142,13 @@ typedef struct pa_alsa_ucm_device pa_alsa_ucm_device; typedef struct pa_alsa_ucm_config pa_alsa_ucm_config; typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context; +typedef struct pa_alsa_ucm_profile_context pa_alsa_ucm_profile_context; typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data; typedef struct pa_alsa_ucm_volume pa_alsa_ucm_volume; int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index); pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map); -int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile); +int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile); int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb); @@ -159,14 +160,14 @@ pa_card *card, snd_pcm_t *pcm_handle, bool ignore_dB); -void pa_alsa_ucm_add_ports_combination( +void pa_alsa_ucm_add_port( pa_hashmap *hash, pa_alsa_ucm_mapping_context *context, bool is_sink, pa_hashmap *ports, pa_card_profile *cp, pa_core *core); -int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink); +int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port); void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm); void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context); @@ -223,11 +224,8 @@ pa_proplist *proplist; - int n_confdev; - int n_suppdev; - - const char **conflicting_devices; - const char **supported_devices; + pa_idxset *conflicting_devices; + pa_idxset *supported_devices; pa_direction_t action_direction; @@ -270,21 +268,23 @@ pa_alsa_ucm_config *ucm; pa_direction_t direction; - pa_idxset *ucm_devices; - pa_idxset *ucm_modifiers; + pa_alsa_ucm_device *ucm_device; + pa_alsa_ucm_modifier *ucm_modifier; +}; + +struct pa_alsa_ucm_profile_context { + pa_alsa_ucm_verb *verb; }; struct pa_alsa_ucm_port_data { pa_alsa_ucm_config *ucm; pa_device_port *core_port; - /* A single port will be associated with multiple devices if it represents - * a combination of devices. */ - pa_dynarray *devices; /* pa_alsa_ucm_device */ + pa_alsa_ucm_device *device; - /* profile name -> pa_alsa_path for volume control */ + /* verb name -> pa_alsa_path for volume control */ pa_hashmap *paths; - /* Current path, set when activating profile */ + /* Current path, set when activating verb */ pa_alsa_path *path; /* ELD info */
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/acp/alsa-util.c -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-util.c
Changed
@@ -505,6 +505,8 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, + pa_sample_format_t **query_supported_formats, + unsigned int **query_supported_rates, pa_alsa_profile_set *ps, pa_alsa_mapping **mapping) { @@ -543,6 +545,8 @@ tsched_size, use_mmap, use_tsched, + query_supported_formats, + query_supported_rates, m); if (pcm_handle) { @@ -570,6 +574,8 @@ tsched_size, use_mmap, use_tsched, + query_supported_formats, + query_supported_rates, m); if (pcm_handle) { @@ -594,6 +600,8 @@ tsched_size, use_mmap, use_tsched, + query_supported_formats, + query_supported_rates, false); pa_xfree(d); @@ -615,6 +623,8 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, + pa_sample_format_t **query_supported_formats, + unsigned int **query_supported_rates, pa_alsa_mapping *m) { snd_pcm_t *pcm_handle; @@ -644,6 +654,8 @@ tsched_size, use_mmap, use_tsched, + query_supported_formats, + query_supported_rates, pa_channel_map_valid(&m->channel_map) /* Query the channel count if we don't know what we want */); if (!pcm_handle) @@ -681,6 +693,8 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, + pa_sample_format_t **query_supported_formats, + unsigned int **query_supported_rates, bool require_exact_channel_number) { int err; @@ -708,6 +722,12 @@ pa_log_info("ALSA device open '%s' %s: %p", d, mode == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", pcm_handle); + if (query_supported_formats) + *query_supported_formats = pa_alsa_get_supported_formats(pcm_handle, ss->format); + + if (query_supported_rates) + *query_supported_rates = pa_alsa_get_supported_rates(pcm_handle, ss->rate); + if ((err = pa_alsa_set_hw_params( pcm_handle, ss, @@ -781,6 +801,8 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, + pa_sample_format_t **query_supported_formats, + unsigned int **query_supported_rates, bool require_exact_channel_number) { snd_pcm_t *pcm_handle; @@ -802,6 +824,8 @@ tsched_size, use_mmap, use_tsched, + query_supported_formats, + query_supported_rates, require_exact_channel_number); pa_xfree(d); @@ -1411,6 +1435,24 @@ return pa_sprintf_malloc("Audio%i", i); } +#endif + +static void dump_supported_rates(unsigned int* values) +{ + pa_strbuf *buf; + char *str; + int i; + + buf = pa_strbuf_new(); + + for (i = 0; valuesi; i++) { + pa_strbuf_printf(buf, " %u", valuesi); + } + + str = pa_strbuf_to_string_free(buf); + pa_log_debug("Supported rates:%s", str); + pa_xfree(str); +} unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate) { static unsigned int all_rates = { 8000, 11025, 12000, @@ -1418,7 +1460,8 @@ 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000, - 384000 }; + 352800, 384000, + 705600, 768000 }; bool supportedPA_ELEMENTSOF(all_rates) = { false, }; snd_pcm_hw_params_t *hwparams; unsigned int i, j, n, *rates = NULL; @@ -1460,39 +1503,40 @@ rates1 = 0; } + dump_supported_rates(rates); return rates; } pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_format_t fallback_format) { - static const snd_pcm_format_t format_trans_to_pa = { - SND_PCM_FORMAT_U8 = PA_SAMPLE_U8, - SND_PCM_FORMAT_A_LAW = PA_SAMPLE_ALAW, - SND_PCM_FORMAT_MU_LAW = PA_SAMPLE_ULAW, - SND_PCM_FORMAT_S16_LE = PA_SAMPLE_S16LE, - SND_PCM_FORMAT_S16_BE = PA_SAMPLE_S16BE, - SND_PCM_FORMAT_FLOAT_LE = PA_SAMPLE_FLOAT32LE, - SND_PCM_FORMAT_FLOAT_BE = PA_SAMPLE_FLOAT32BE, - SND_PCM_FORMAT_S32_LE = PA_SAMPLE_S32LE, - SND_PCM_FORMAT_S32_BE = PA_SAMPLE_S32BE, - SND_PCM_FORMAT_S24_3LE = PA_SAMPLE_S24LE, - SND_PCM_FORMAT_S24_3BE = PA_SAMPLE_S24BE, - SND_PCM_FORMAT_S24_LE = PA_SAMPLE_S24_32LE, - SND_PCM_FORMAT_S24_BE = PA_SAMPLE_S24_32BE, + static const snd_pcm_format_t format_trans_to_pcm = { + PA_SAMPLE_U8 = SND_PCM_FORMAT_U8, + PA_SAMPLE_ALAW = SND_PCM_FORMAT_A_LAW, + PA_SAMPLE_ULAW = SND_PCM_FORMAT_MU_LAW, + PA_SAMPLE_S16LE = SND_PCM_FORMAT_S16_LE, + PA_SAMPLE_S16BE = SND_PCM_FORMAT_S16_BE, + PA_SAMPLE_FLOAT32LE = SND_PCM_FORMAT_FLOAT_LE, + PA_SAMPLE_FLOAT32BE = SND_PCM_FORMAT_FLOAT_BE, + PA_SAMPLE_S32LE = SND_PCM_FORMAT_S32_LE, + PA_SAMPLE_S32BE = SND_PCM_FORMAT_S32_BE, + PA_SAMPLE_S24LE = SND_PCM_FORMAT_S24_3LE, + PA_SAMPLE_S24BE = SND_PCM_FORMAT_S24_3BE, + PA_SAMPLE_S24_32LE = SND_PCM_FORMAT_S24_LE, + PA_SAMPLE_S24_32BE = SND_PCM_FORMAT_S24_BE, }; - static const snd_pcm_format_t all_formats = { - SND_PCM_FORMAT_U8, - SND_PCM_FORMAT_A_LAW, - SND_PCM_FORMAT_MU_LAW, - SND_PCM_FORMAT_S16_LE, - SND_PCM_FORMAT_S16_BE, - SND_PCM_FORMAT_FLOAT_LE, - SND_PCM_FORMAT_FLOAT_BE, - SND_PCM_FORMAT_S32_LE, - SND_PCM_FORMAT_S32_BE, - SND_PCM_FORMAT_S24_3LE, - SND_PCM_FORMAT_S24_3BE, - SND_PCM_FORMAT_S24_LE, - SND_PCM_FORMAT_S24_BE, + static const pa_sample_format_t all_formats = { + PA_SAMPLE_U8, + PA_SAMPLE_ALAW, + PA_SAMPLE_ULAW, + PA_SAMPLE_S16LE, + PA_SAMPLE_S16BE, + PA_SAMPLE_FLOAT32LE, + PA_SAMPLE_FLOAT32BE, + PA_SAMPLE_S32LE, + PA_SAMPLE_S32BE, + PA_SAMPLE_S24LE, + PA_SAMPLE_S24BE, + PA_SAMPLE_S24_32LE, + PA_SAMPLE_S24_32BE, }; bool supportedPA_ELEMENTSOF(all_formats) = { false, @@ -1510,7 +1554,7 @@ }
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/acp/alsa-util.h -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-util.h
Changed
@@ -64,6 +64,8 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ + pa_sample_format_t **query_supported_formats, /* modified at return */ + unsigned int **query_supported_rates, /* modified at return */ pa_alsa_profile_set *ps, pa_alsa_mapping **mapping); /* modified at return */ #endif @@ -80,6 +82,8 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ + pa_sample_format_t **query_supported_formats, /* modified at return */ + unsigned int **query_supported_rates, /* modified at return */ pa_alsa_mapping *mapping); /* Opens the explicit ALSA device */ @@ -94,6 +98,8 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ + pa_sample_format_t **query_supported_formats, /* modified at return */ + unsigned int **query_supported_rates, /* modified at return */ bool require_exact_channel_number); /* Opens the explicit ALSA device with a fallback list */ @@ -109,6 +115,8 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ + pa_sample_format_t **query_supported_formats, /* modified at return */ + unsigned int **query_supported_rates, /* modified at return */ bool require_exact_channel_number); #if 0
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/acp/compat.h -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/compat.h
Changed
@@ -47,10 +47,12 @@ #define PA_LIKELY(x) (__builtin_expect(!!(x),1)) #define PA_UNLIKELY(x) (__builtin_expect(!!(x),0)) #define PA_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) +#define PA_UNUSED __attribute__ ((unused)) #else #define PA_LIKELY(x) (x) #define PA_UNLIKELY(x) (x) #define PA_PRINTF_FUNC(fmt, arg1) +#define PA_UNUSED #endif #define PA_MIN(a,b) \ @@ -96,7 +98,7 @@ PA_AVAILABLE_YES = 2, } pa_available_t; -#define PA_RATE_MAX (48000U*8U) +#define PA_RATE_MAX (48000U*16U) typedef enum pa_sample_format { PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */ @@ -348,6 +350,48 @@ pa_xfreev((void**)a); } +typedef struct { + size_t size; + char *ptr; + FILE *f; +} pa_strbuf; + +static inline pa_strbuf *pa_strbuf_new(void) +{ + pa_strbuf *s = pa_xnew0(pa_strbuf,1); + s->f = open_memstream(&s->ptr, &s->size); + return s; +} + +static PA_PRINTF_FUNC(2,3) inline size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) +{ + int ret; + va_list args; + va_start(args, format); + ret = vfprintf(sb->f, format, args); + va_end(args); + return ret > 0 ? ret : 0; +} + +static inline void pa_strbuf_puts(pa_strbuf *sb, const char *t) +{ + fputs(t, sb->f); +} + +static inline bool pa_strbuf_isempty(pa_strbuf *sb) +{ + fflush(sb->f); + return sb->size == 0; +} + +static inline char *pa_strbuf_to_string_free(pa_strbuf *sb) +{ + char *ptr; + fclose(sb->f); + ptr = sb->ptr; + free(sb); + return ptr; +} #define pa_cstrerror strerror
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/acp/idxset.h -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/idxset.h
Changed
@@ -74,7 +74,13 @@ { pa_idxset_item *item; pa_array_for_each(item, &s->array) { - if (item->ptr == ptr) + if (item->ptr == NULL) { + if (ptr == NULL) + return item; + else + continue; + } + if (s->compare_func(item->ptr, ptr) == 0) return item; } return NULL; @@ -124,13 +130,25 @@ return count; } -static inline void *pa_idxset_search(pa_idxset *s, uint32_t *idx) +static inline pa_idxset_item *pa_idxset_search(pa_idxset *s, uint32_t *idx) { pa_idxset_item *item; for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item); pa_array_check(&s->array, item); item++, (*idx)++) { if (item->ptr != NULL) - return item->ptr; + return item; + } + *idx = PA_IDXSET_INVALID; + return NULL; +} + +static inline pa_idxset_item *pa_idxset_reverse_search(pa_idxset *s, uint32_t *idx) +{ + pa_idxset_item *item; + for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item); + pa_array_check(&s->array, item); item--, (*idx)--) { + if (item->ptr != NULL) + return item; } *idx = PA_IDXSET_INVALID; return NULL; @@ -138,29 +156,93 @@ static inline void *pa_idxset_next(pa_idxset *s, uint32_t *idx) { + pa_idxset_item *item; (*idx)++;; - return pa_idxset_search(s, idx); + item = pa_idxset_search(s, idx); + return item ? item->ptr : NULL; } static inline void* pa_idxset_first(pa_idxset *s, uint32_t *idx) { uint32_t i = 0; - void *ptr = pa_idxset_search(s, &i); + pa_idxset_item *item = pa_idxset_search(s, &i); if (idx) *idx = i; + return item ? item->ptr : NULL; +} + +static inline void* pa_idxset_last(pa_idxset *s, uint32_t *idx) +{ + uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1; + pa_idxset_item *item = pa_idxset_reverse_search(s, &i); + if (idx) + *idx = i; + return item ? item->ptr : NULL; +} + +static inline void* pa_idxset_steal_last(pa_idxset *s, uint32_t *idx) +{ + uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1; + void *ptr = NULL; + pa_idxset_item *item = pa_idxset_reverse_search(s, &i); + if (idx) + *idx = i; + if (item) { + ptr = item->ptr; + item->ptr = NULL; + pa_array_remove(&s->array, item); + } return ptr; } static inline void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) { pa_idxset_item *item = pa_idxset_find(s, p); - if (item == NULL) + if (item == NULL) { + if (idx) + *idx = PA_IDXSET_INVALID; return NULL; + } if (idx) *idx = item - (pa_idxset_item*)s->array.data; return item->ptr; } +static inline bool pa_idxset_contains(pa_idxset *s, const void *p) +{ + return pa_idxset_get_by_data(s, p, NULL) == p; +} + +static inline bool pa_idxset_isdisjoint(pa_idxset *s, pa_idxset *t) +{ + pa_idxset_item *item; + pa_array_for_each(item, &s->array) { + if (item->ptr && pa_idxset_contains(t, item->ptr)) + return false; + } + return true; +} + +static inline bool pa_idxset_issubset(pa_idxset *s, pa_idxset *t) +{ + pa_idxset_item *item; + pa_array_for_each(item, &s->array) { + if (item->ptr && !pa_idxset_contains(t, item->ptr)) + return false; + } + return true; +} + +static inline bool pa_idxset_issuperset(pa_idxset *s, pa_idxset *t) +{ + return pa_idxset_issubset(t, s); +} + +static inline bool pa_idxset_equals(pa_idxset *s, pa_idxset *t) +{ + return pa_idxset_issubset(s, t) && pa_idxset_issuperset(s, t); +} + static inline void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) { pa_idxset_item *item;
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/alsa-compress-offload-sink.c -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/alsa-compress-offload-sink.c
Changed
@@ -703,14 +703,23 @@ } } - check_position_and_clock_config(this); + if (SPA_LIKELY(this->node_position_io != NULL)) { + this->cycle_duration = this->node_position_io->clock.target_duration; + this->cycle_rate = this->node_position_io->clock.target_rate.denom; + } else { + /* This can happen at the very beginning if node_position_io + * isn't passed to this node in time. */ + this->cycle_duration = 1024; + this->cycle_rate = 48000; + } current_time = this->next_driver_time; this->next_driver_time += ((uint64_t)(this->cycle_duration)) * 1000000000ULL / this->cycle_rate; if (this->node_clock_io != NULL) { this->node_clock_io->nsec = current_time; - this->node_clock_io->position += this->cycle_duration; + this->node_clock_io->rate = this->node_clock_io->target_rate; + this->node_clock_io->position += this->node_clock_io->duration; this->node_clock_io->duration = this->cycle_duration; this->node_clock_io->delay = 0; this->node_clock_io->rate_diff = 1.0;
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/alsa-pcm-source.c -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/alsa-pcm-source.c
Changed
@@ -630,6 +630,7 @@ this->port_info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; this->port_paramsPORT_Latency.user++; emit_port_info(this, false); + res = 0; break; } default:
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -122,6 +122,8 @@ state->disable_mmap = spa_atob(s); } else if (spa_streq(k, "api.alsa.disable-batch")) { state->disable_batch = spa_atob(s); + } else if (spa_streq(k, "api.alsa.disable-tsched")) { + state->disable_tsched = spa_atob(s); } else if (spa_streq(k, "api.alsa.use-chmap")) { state->props.use_chmap = spa_atob(s); } else if (spa_streq(k, "api.alsa.multi-rate")) { @@ -284,12 +286,20 @@ case 11: param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String("api.alsa.disable-tsched"), + SPA_PROP_INFO_description, SPA_POD_String("Disable timer based scheduling"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->disable_tsched), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 12: + param = spa_pod_builder_add_object(b, + 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_CHOICE_Bool(state->props.use_chmap), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; - case 12: + case 13: param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("api.alsa.multi-rate"), @@ -297,7 +307,7 @@ SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->multi_rate), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; - case 13: + case 14: param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("latency.internal.rate"), @@ -306,7 +316,7 @@ 0, 65536), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; - case 14: + case 15: param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("latency.internal.ns"), @@ -315,7 +325,7 @@ 0LL, 2 * SPA_NSEC_PER_SEC), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; - case 15: + case 16: param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, SPA_PROP_INFO_name, SPA_POD_String("clock.name"), @@ -375,6 +385,9 @@ spa_pod_builder_string(b, "api.alsa.disable-batch"); spa_pod_builder_bool(b, state->disable_batch); + spa_pod_builder_string(b, "api.alsa.disable-tsched"); + spa_pod_builder_bool(b, state->disable_tsched); + spa_pod_builder_string(b, "api.alsa.use-chmap"); spa_pod_builder_bool(b, state->props.use_chmap); @@ -557,11 +570,16 @@ device_name, state->stream == SND_PCM_STREAM_CAPTURE ? "capture" : "playback"); - if ((err = spa_system_timerfd_create(state->data_system, - CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0) - goto error_exit_close; + if (!state->disable_tsched) { + if ((err = spa_system_timerfd_create(state->data_system, + CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0) + goto error_exit_close; - state->timerfd = err; + state->timerfd = err; + } else { + /* ALSA pollfds may only be ready after setting swparams, so + * these are initialised in spa_alsa_start() */ + } if (state->clock) spa_scnprintf(state->clock->name, sizeof(state->clock->name), @@ -593,7 +611,10 @@ spa_log_warn(state->log, "%s: close failed: %s", state->props.device, snd_strerror(err)); - spa_system_close(state->data_system, state->timerfd); + if (!state->disable_tsched) + spa_system_close(state->data_system, state->timerfd); + else + state->n_fds = 0; if (state->have_format) state->card->format_ref--; @@ -1555,14 +1576,15 @@ * the period smaller and add one period of headroom. Limit the * period size to our default so that we don't create too much * headroom. */ - period_size = SPA_MIN(period_size, DEFAULT_PERIOD) / 2; + if (!state->disable_tsched) + period_size = SPA_MIN(period_size, DEFAULT_PERIOD) / 2; spa_log_info(state->log, "%s: batch mode, period_size:%ld", state->props.device, period_size); } else { if (period_size == 0) period_size = DEFAULT_PERIOD; - /* disable ALSA wakeups, we use a timer */ - if (snd_pcm_hw_params_can_disable_period_wakeup(params)) + /* disable ALSA wakeups, if we use a timer */ + if (!state->disable_tsched && snd_pcm_hw_params_can_disable_period_wakeup(params)) CHECK(snd_pcm_hw_params_set_period_wakeup(hndl, params, 0), "set_period_wakeup"); } @@ -1594,7 +1616,9 @@ } state->headroom = state->default_headroom; - if (is_batch) + /* If tsched is disabled, we know the pointers are updated when we wake + * up, so we don't need the additional headroom */ + if (is_batch && !state->disable_tsched) state->headroom += period_size; state->max_delay = state->buffer_frames / 2; @@ -1613,14 +1637,15 @@ spa_log_info(state->log, "%s (%s): format:%s access:%s-%s rate:%d channels:%d " "buffer frames %lu, period frames %lu, periods %u, frame_size %zd " - "headroom %u start-delay:%u", + "headroom %u start-delay:%u tsched:%u", state->props.device, state->stream == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", snd_pcm_format_name(state->format), state->use_mmap ? "mmap" : "rw", planar ? "planar" : "interleaved", state->rate, state->channels, state->buffer_frames, state->period_frames, - periods, state->frame_size, state->headroom, state->start_delay); + periods, state->frame_size, state->headroom, state->start_delay, + !state->disable_tsched); /* write the parameters to device */ CHECK(snd_pcm_hw_params(hndl, params), "set_hw_params"); @@ -1653,7 +1678,21 @@ /* start the transfer */ CHECK(snd_pcm_sw_params_set_start_threshold(hndl, params, LONG_MAX), "set_start_threshold"); - CHECK(snd_pcm_sw_params_set_period_event(hndl, params, 0), "set_period_event"); + CHECK(snd_pcm_sw_params_set_period_event(hndl, params, state->disable_tsched), "set_period_event"); + + if (state->disable_tsched) { + snd_pcm_uframes_t avail_min; + + if (state->stream == SND_PCM_STREAM_PLAYBACK) { + /* wake up when buffer has target frames or less data (will underrun soon) */ + avail_min = state->buffer_frames - state->threshold; + } else { + /* wake up when there's target frames or more (enough for us to read and push a buffer) */ + avail_min = state->threshold; + } + + CHECK(snd_pcm_sw_params_set_avail_min(hndl, params, avail_min), "set_avail_min"); + } /* write the parameters to the playback device */ CHECK(snd_pcm_sw_params(hndl, params), "sw_params"); @@ -1671,6 +1710,9 @@ { struct itimerspec ts; + if (state->disable_tsched) + return 0; + ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC; ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC; ts.it_interval.tv_sec = 0; @@ -1952,7 +1994,8 @@ if (SPA_LIKELY(!follower && state->clock)) { state->clock->nsec = current_time; - state->clock->position += state->duration; + state->clock->rate = state->clock->target_rate; + state->clock->position += state->clock->duration; state->clock->duration = state->duration; state->clock->delay = delay + state->delay; state->clock->rate_diff = corr; @@ -1993,20 +2036,39 @@ return 0; } -static inline void check_position_config(struct state *state) +static void update_sources(struct state *state, bool active) { + if (state->disable_tsched && state->source0.data != NULL) {
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/alsa-pcm.h -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/alsa-pcm.h
Changed
@@ -47,6 +47,7 @@ }; #define MAX_BUFFERS 32 +#define MAX_POLL 16 struct buffer { uint32_t id; @@ -131,6 +132,7 @@ struct channel_map default_pos; unsigned int disable_mmap; unsigned int disable_batch; + unsigned int disable_tsched; char clock_name64; uint32_t quantum_limit; @@ -171,8 +173,11 @@ size_t ready_offset; bool started; - struct spa_source source; + /* Either a single source for tsched, or a set of pollfds from ALSA */ + struct spa_source sourceMAX_POLL; int timerfd; + struct pollfd pfdsMAX_POLL; + int n_fds; uint32_t threshold; uint32_t last_threshold; uint32_t headroom;
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/alsa-seq.c -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/alsa-seq.c
Changed
@@ -684,7 +684,7 @@ static void update_position(struct seq_state *state) { - if (state->position) { + if (SPA_LIKELY(state->position)) { struct spa_io_clock *clock = &state->position->clock; state->rate = clock->rate; if (state->rate.num == 0 || state->rate.denom == 0) @@ -744,7 +744,8 @@ if (!follower && state->clock) { state->clock->nsec = nsec; - state->clock->position += state->duration; + state->clock->rate = state->rate; + state->clock->position += state->clock->duration; state->clock->duration = state->duration; state->clock->delay = state->duration * corr; state->clock->rate_diff = corr; @@ -793,7 +794,17 @@ spa_log_trace(state->log, "timeout %"PRIu64, state->current_time); - update_position(state); + if (SPA_LIKELY(state->position)) { + struct spa_io_clock *clock = &state->position->clock; + state->rate = clock->target_rate; + if (state->rate.num == 0 || state->rate.denom == 0) + state->rate = SPA_FRACTION(1, 48000); + state->duration = clock->target_duration; + } else { + state->rate = SPA_FRACTION(1, 48000); + state->duration = 1024; + } + state->threshold = state->duration; update_time(state, state->current_time, false);
View file
pipewire-0.3.67.tar.gz/spa/plugins/alsa/mixer/profile-sets/asus-xonar-se.conf -> pipewire-0.3.68.tar.gz/spa/plugins/alsa/mixer/profile-sets/asus-xonar-se.conf
Changed
@@ -21,16 +21,14 @@ General auto-profiles = yes -Mapping analog-stereo-front -description = Analog Stereo Front +Mapping analog-stereo-headset device-strings = hw:%f,1 channel-map = left,right paths-output = analog-output analog-output-headphones paths-input = analog-input-mic analog-input-headphone-mic analog-input-headset-mic priority = 15 -Mapping analog-stereo-rear -description = Analog Stereo Rear +Mapping analog-stereo device-strings = hw:%f,0 channel-map = left,right paths-output = analog-output analog-output-speaker
View file
pipewire-0.3.67.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/audioadapter.c
Changed
@@ -177,6 +177,7 @@ SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id( SPA_PARAM_PORT_CONFIG_MODE_passthrough)); result.next++; + res = 1; break; default: return 0; @@ -226,30 +227,40 @@ static int link_io(struct impl *this) { int res; - - if (this->convert == NULL) - return 0; + struct spa_io_rate_match *rate_match; + size_t rate_match_size; spa_log_debug(this->log, "%p: controls", this); spa_zero(this->io_rate_match); this->io_rate_match.rate = 1.0; + if (this->follower == this->target) { + rate_match = NULL; + rate_match_size = 0; + } else { + rate_match = &this->io_rate_match; + rate_match_size = sizeof(this->io_rate_match); + } + if ((res = spa_node_port_set_io(this->follower, this->direction, 0, SPA_IO_RateMatch, - &this->io_rate_match, sizeof(this->io_rate_match))) < 0) { + rate_match, rate_match_size)) < 0) { spa_log_debug(this->log, "%p: set RateMatch on follower disabled %d %s", this, res, spa_strerror(res)); } else if ((res = spa_node_port_set_io(this->convert, SPA_DIRECTION_REVERSE(this->direction), 0, SPA_IO_RateMatch, - &this->io_rate_match, sizeof(this->io_rate_match))) < 0) { + rate_match, rate_match_size)) < 0) { spa_log_warn(this->log, "%p: set RateMatch on convert failed %d %s", this, res, spa_strerror(res)); } + if (this->follower == this->target) + return 0; + this->io_buffers = SPA_IO_BUFFERS_INIT; if ((res = spa_node_port_set_io(this->follower, @@ -355,11 +366,11 @@ struct spa_data *datas; uint64_t follower_flags, conv_flags; - spa_log_debug(this->log, "%p: n_buffers:%d", this, this->n_buffers); - if (this->target == this->follower) return 0; + spa_log_debug(this->log, "%p: n_buffers:%d", this, this->n_buffers); + if (this->n_buffers > 0) return 0; @@ -487,7 +498,7 @@ format = fmt; } - if (this->target != this->follower && this->convert) { + if (this->target != this->follower) { if ((res = spa_node_port_set_param(this->convert, SPA_DIRECTION_REVERSE(this->direction), 0, SPA_PARAM_Format, flags, @@ -565,12 +576,13 @@ } else { /* add converter ports */ configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_dsp); - link_io(this); } + link_io(this); } - this->info.change_mask |= SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PARAMS; - this->info.flags &= ~SPA_NODE_FLAG_NEED_CONFIGURE; + SPA_FLAG_CLEAR(this->info.flags, SPA_NODE_FLAG_NEED_CONFIGURE); + SPA_FLAG_UPDATE(this->info.flags, SPA_NODE_FLAG_ASYNC, + this->async && this->follower == this->target); this->paramsIDX_Props.user++; emit_node_info(this, false); @@ -746,17 +758,16 @@ struct spa_pod_builder b = { 0 }; int res; + if (this->target == this->follower) + return 0; + spa_log_debug(this->log, "%p: have_format:%d", this, this->have_format); if (this->have_format) return 0; - if (this->target == this->follower) - return 0; - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - spa_node_send_command(this->follower, &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_ParamBegin)); @@ -774,18 +785,16 @@ goto done; } } - if (this->convert) { - state = 0; - if ((res = spa_node_port_enum_params_sync(this->convert, - SPA_DIRECTION_REVERSE(this->direction), 0, - SPA_PARAM_EnumFormat, &state, - format, &format, &b)) != 1) { - debug_params(this, this->convert, - SPA_DIRECTION_REVERSE(this->direction), 0, - SPA_PARAM_EnumFormat, format, "convert format", res); - res = -ENOTSUP; - goto done; - } + state = 0; + if ((res = spa_node_port_enum_params_sync(this->convert, + SPA_DIRECTION_REVERSE(this->direction), 0, + SPA_PARAM_EnumFormat, &state, + format, &format, &b)) != 1) { + debug_params(this, this->convert, + SPA_DIRECTION_REVERSE(this->direction), 0, + SPA_PARAM_EnumFormat, format, "convert format", res); + res = -ENOTSUP; + goto done; } if (format == NULL) { res = -ENOTSUP; @@ -998,6 +1007,8 @@ this->info.flags |= SPA_NODE_FLAG_OUT_PORT_CONFIG; this->info.max_output_ports = MAX_PORTS; } + SPA_FLAG_UPDATE(this->info.flags, SPA_NODE_FLAG_ASYNC, + this->async && this->follower == this->target); spa_log_debug(this->log, "%p: follower info %s", this, this->direction == SPA_DIRECTION_INPUT ? @@ -1229,7 +1240,7 @@ int res; struct impl *this = data; - if (this->target != this->follower && this->convert) + if (this->target != this->follower) res = spa_node_port_reuse_buffer(this->convert, port_id, buffer_id); else res = spa_node_call_reuse_buffer(&this->callbacks, port_id, buffer_id); @@ -1272,11 +1283,10 @@ spa_node_add_listener(this->follower, &l, &follower_node_events, this); spa_hook_remove(&l); - if (this->convert) { - spa_zero(l); - spa_node_add_listener(this->convert, &l, &convert_node_events, this); - spa_hook_remove(&l); - } + spa_zero(l); + spa_node_add_listener(this->convert, &l, &convert_node_events, this); + spa_hook_remove(&l); + this->add_listener = false; emit_node_info(this, true); @@ -1466,7 +1476,7 @@ * First we run the converter to process the input for the follower * then if it produced data, we run the follower. */ while (retry--) { - status = this->convert ? spa_node_process(this->convert) : 0; + status = spa_node_process(this->convert); /* schedule the follower when the converter needed * a recycled buffer */ if (status == -EPIPE || status == 0) @@ -1498,7 +1508,7 @@ /* output node (source). First run the converter to make * sure we push out any queued data. Then when it needs * more data, schedule the follower. */ - status = this->convert ? spa_node_process(this->convert) : 0; + status = spa_node_process(this->convert); if (status == 0) status = SPA_STATUS_NEED_DATA; else if (status < 0) @@ -1660,6 +1670,9 @@ info, support, n_support); spa_handle_get_interface(this->hnd_convert, SPA_TYPE_INTERFACE_Node, &iface);
View file
pipewire-0.3.67.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/audioconvert.c
Changed
@@ -23,12 +23,14 @@ #include <spa/param/param.h> #include <spa/param/latency-utils.h> #include <spa/pod/filter.h> +#include <spa/pod/dynamic.h> #include <spa/debug/types.h> #include "volume-ops.h" #include "fmt-ops.h" #include "channelmix-ops.h" #include "resample.h" +#include "wavfile.h" #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT log_topic @@ -60,18 +62,29 @@ vol->volumesi = DEFAULT_VOLUME; } +struct volume_ramp_params { + unsigned int volume_ramp_samples; + unsigned int volume_ramp_step_samples; + unsigned int volume_ramp_time; + unsigned int volume_ramp_step_time; + enum spa_audio_volume_ramp_scale scale; +}; + struct props { float volume; + float prev_volume; uint32_t n_channels; uint32_t channel_mapSPA_AUDIO_MAX_CHANNELS; struct volumes channel; struct volumes soft; struct volumes monitor; + struct volume_ramp_params vrp; unsigned int have_soft_volume:1; unsigned int mix_disabled:1; unsigned int resample_disabled:1; unsigned int resample_quality; double rate; + char wav_path512; }; static void props_reset(struct props *props) @@ -89,6 +102,7 @@ props->resample_disabled = false; props->resample_quality = RESAMPLE_DEFAULT_QUALITY; props->rate = 1.0; + spa_zero(props->wav_path); } struct buffer { @@ -192,6 +206,8 @@ struct resample resample; struct volume volume; double rate_scale; + struct spa_pod_sequence *vol_ramp_sequence; + uint32_t vol_ramp_offset; uint32_t in_offset; uint32_t out_offset; @@ -199,6 +215,7 @@ unsigned int setup:1; unsigned int resample_peaks:1; unsigned int is_passthrough:1; + unsigned int ramp_volume:1; unsigned int drained:1; unsigned int rate_adjust:1; @@ -207,6 +224,8 @@ float *scratch; float *tmp2; float *tmp_datas2MAX_PORTS; + + struct wav_file *wav_file; }; #define CHECK_PORT(this,d,p) ((p) < this->dird.n_ports) @@ -637,6 +656,14 @@ spa_pod_builder_pop(&b, &f1); param = spa_pod_builder_pop(&b, &f0); break; + case 24: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_PropInfo, id, + SPA_PROP_INFO_name, SPA_POD_String("debug.wav-path"), + SPA_PROP_INFO_description, SPA_POD_String("Path to WAV file"), + SPA_PROP_INFO_type, SPA_POD_String(p->wav_path), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; default: return 0; } @@ -709,6 +736,8 @@ spa_pod_builder_int(&b, this->dir1.conv.noise_bits); spa_pod_builder_string(&b, "dither.method"); spa_pod_builder_string(&b, dither_method_infothis->dir1.conv.method.label); + spa_pod_builder_string(&b, "debug.wav-path"); + spa_pod_builder_string(&b, p->wav_path); spa_pod_builder_pop(&b, &f1); param = spa_pod_builder_pop(&b, &f0); break; @@ -782,6 +811,10 @@ spa_atou32(s, &this->dir1.conv.noise_bits, 0); else if (spa_streq(k, "dither.method")) this->dir1.conv.method = dither_method_from_label(s); + else if (spa_streq(k, "debug.wav-path")) { + spa_scnprintf(this->props.wav_path, + sizeof(this->props.wav_path), "%s", s ? s : ""); + } else return 0; return 1; @@ -837,6 +870,127 @@ return changed; } +static int get_ramp_samples(struct impl *this) +{ + struct volume_ramp_params *vrp = &this->props.vrp; + int samples = -1; + + if (vrp->volume_ramp_samples) + samples = vrp->volume_ramp_samples; + else if (vrp->volume_ramp_time) { + struct dir *d = &this->dirSPA_DIRECTION_OUTPUT; + unsigned int sample_rate = d->format.info.raw.rate; + samples = (vrp->volume_ramp_time * sample_rate) / 1000; + spa_log_info(this->log, "volume ramp samples calculated from time is %d", samples); + } + if (!samples) + samples = -1; + + return samples; + +} + +static int get_ramp_step_samples(struct impl *this) +{ + struct volume_ramp_params *vrp = &this->props.vrp; + int samples = -1; + + if (vrp->volume_ramp_step_samples) + samples = vrp->volume_ramp_step_samples; + else if (vrp->volume_ramp_step_time) { + struct dir *d = &this->dirSPA_DIRECTION_OUTPUT; + int sample_rate = d->format.info.raw.rate; + /* convert the step time which is in nano seconds to seconds */ + samples = (vrp->volume_ramp_step_time/1000) * (sample_rate/1000); + spa_log_debug(this->log, "volume ramp step samples calculated from time is %d", samples); + } + if (!samples) + samples = -1; + + return samples; + +} + +static double get_volume_at_scale(struct impl *this, double value) +{ + struct volume_ramp_params *vrp = &this->props.vrp; + if (vrp->scale == SPA_AUDIO_VOLUME_RAMP_LINEAR || vrp->scale == SPA_AUDIO_VOLUME_RAMP_INVALID) + return value; + else if (vrp->scale == SPA_AUDIO_VOLUME_RAMP_CUBIC) + return (value * value * value); + + return 0.0; +} + +static struct spa_pod *generate_ramp_up_seq(struct impl *this) +{ + struct spa_pod_dynamic_builder b; + struct spa_pod_frame f1; + struct props *p = &this->props; + double volume_accum = p->prev_volume; + int ramp_samples = get_ramp_samples(this); + int ramp_step_samples = get_ramp_step_samples(this); + double volume_step = ((p->volume - p->prev_volume) / (ramp_samples / ramp_step_samples)); + uint32_t volume_offs = 0; + + spa_pod_dynamic_builder_init(&b, NULL, 0, 4096); + + spa_pod_builder_push_sequence(&b.b, &f0, 0); + spa_log_info(this->log, "generating ramp up sequence from %f to %f with a" + " step value %f at scale %d", p->prev_volume, p->volume, volume_step, p->vrp.scale); + do { + // spa_log_debug(this->log, "volume accum %f", get_volume_at_scale(this, volume_accum)); + spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties); + spa_pod_builder_add_object(&b.b, + SPA_TYPE_OBJECT_Props, 0, + SPA_PROP_volume, + SPA_POD_Float(get_volume_at_scale(this, volume_accum))); + volume_accum += volume_step; + volume_offs += ramp_step_samples; + } while (volume_accum < p->volume); + return spa_pod_builder_pop(&b.b, &f0); +} + +static struct spa_pod *generate_ramp_down_seq(struct impl *this)
View file
pipewire-0.3.67.tar.gz/spa/plugins/audioconvert/channelmix-ops.c -> pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/channelmix-ops.c
Changed
@@ -629,8 +629,12 @@ for (jc = 0, ic = 0, i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { float sum = 0.0f; - char str1024, str21024; - int idx = 0, idx2 = 0; + char str11024, str21024; + struct spa_strbuf sb1, sb2; + + spa_strbuf_init(&sb1, str1, sizeof(str1)); + spa_strbuf_init(&sb2, str2, sizeof(str2)); + if ((dst_paired & (1UL << i)) == 0) continue; for (jc = 0, j = 0; j < SPA_AUDIO_MAX_CHANNELS; j++) { @@ -640,7 +644,7 @@ continue; if (ic == 0) - idx2 += snprintf(str2 + idx2, sizeof(str2) - idx2, "%-4.4s ", + spa_strbuf_append(&sb2, "%-4.4s ", src_mask == 0 ? "UNK" : spa_debug_type_find_short_name(spa_type_audio_channel, j + _SH)); @@ -648,17 +652,17 @@ sum += fabs(matrixij); if (matrixij == 0.0f) - idx += snprintf(str + idx, sizeof(str) - idx, " "); + spa_strbuf_append(&sb1, " "); else - idx += snprintf(str + idx, sizeof(str) - idx, "%1.3f ", matrixij); + spa_strbuf_append(&sb1, "%1.3f ", matrixij); } - if (idx2 > 0) + if (sb2.pos > 0) spa_log_info(mix->log, " %s", str2); - if (idx > 0) { + if (sb1.pos > 0) { spa_log_info(mix->log, "%-4.4s %s %f", dst_mask == 0 ? "UNK" : spa_debug_type_find_short_name(spa_type_audio_channel, i + _SH), - str, sum); + str1, sum); } maxsum = SPA_MAX(maxsum, sum);
View file
pipewire-0.3.67.tar.gz/spa/plugins/audioconvert/meson.build -> pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/meson.build
Changed
@@ -104,6 +104,7 @@ 'peaks-ops.c', 'resample-native.c', 'resample-peaks.c', + 'wavfile.c', 'volume-ops.c' , c_args : simd_cargs, '-O3', link_with : simd_dependencies,
View file
pipewire-0.3.67.tar.gz/spa/plugins/audioconvert/test-audioconvert.c -> pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/test-audioconvert.c
Changed
@@ -53,7 +53,7 @@ size_t size; int res; struct spa_support support1; - struct spa_dict_item items2; + struct spa_dict_item items6; const struct spa_handle_factory *factory; void *iface; @@ -70,10 +70,15 @@ spa_assert_se(ctx->convert_handle != NULL); items0 = SPA_DICT_ITEM_INIT("clock.quantum-limit", "8192"); + items1 = SPA_DICT_ITEM_INIT("channelmix.upmix", "true"); + items2 = SPA_DICT_ITEM_INIT("channelmix.upmix-method", "psd"); + items3 = SPA_DICT_ITEM_INIT("channelmix.lfe-cutoff", "150"); + items4 = SPA_DICT_ITEM_INIT("channelmix.fc-cutoff", "12000"); + items5 = SPA_DICT_ITEM_INIT("channelmix.rear-delay", "12.0"); res = spa_handle_factory_init(factory, ctx->convert_handle, - &SPA_DICT_INIT(items, 1), + &SPA_DICT_INIT(items, 6), support, 1); spa_assert_se(res >= 0);
View file
pipewire-0.3.67.tar.gz/spa/plugins/audioconvert/test-fmt-ops.c -> pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/test-fmt-ops.c
Changed
@@ -546,13 +546,17 @@ } static void test_lossless_s16(void) { - int16_t i; + int32_t i; fprintf(stderr, "test %s:\n", __func__); - for (i = S16_MIN; i < S16_MAX; i+=3) { - float v = S16_TO_F32(i); + for (i = S16_MIN; i <= S16_MAX; i+=3) { + float v = S16_TO_F32((int16_t)i); int16_t t = F32_TO_S16(v); spa_assert_se(i == t); + + int32_t t2 = F32_TO_S32(v); + spa_assert_se(i<<16 == t2); + spa_assert_se(i == t2>>16); } } @@ -561,10 +565,14 @@ uint32_t i; fprintf(stderr, "test %s:\n", __func__); - for (i = U16_MIN; i < U16_MAX; i+=3) { - float v = U16_TO_F32(i); + for (i = U16_MIN; i <= U16_MAX; i+=3) { + float v = U16_TO_F32((uint16_t)i); uint16_t t = F32_TO_U16(v); spa_assert_se(i == t); + + uint32_t t2 = F32_TO_U32(v); + spa_assert_se(i<<16 == t2); + spa_assert_se(i == t2>>16); } }
View file
pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/wavfile.c
Added
@@ -0,0 +1,250 @@ +/* PipeWire + * + * 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"), + * 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 <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <spa/utils/string.h> + +#include "wavfile.h" + +#define BLOCK_SIZE 4096 + +struct wav_file { + struct spa_audio_info info; + int fd; + const struct format_info *fi; + + uint32_t length; + + uint32_t stride; + uint32_t blocks; +}; + +static inline ssize_t write_data(struct wav_file *wf, const void *data, size_t size) +{ + ssize_t len; + len = write(wf->fd, data, size); + if (len > 0) + wf->length += len; + return len; +} + +static ssize_t writei(struct wav_file *wf, const void **data, size_t samples) +{ + return write_data(wf, data0, samples * wf->stride); +} + +typedef struct { + uint8_t v3; +} __attribute__ ((packed)) uint24_t; + +#define MAKE_WRITEN_FUNC(name, type) \ +static ssize_t name (struct wav_file *wf, const void **data, size_t samples) \ +{ \ + uint32_t b, n, k, blocks = wf->blocks, chunk; \ + uint8_t bufBLOCK_SIZE; \ + ssize_t res = 0; \ + type **d = (type**)data; \ + uint32_t chunk_size = sizeof(buf) / (blocks * sizeof(type)); \ + for (n = 0; n < samples; ) { \ + type *p = (type*)buf; \ + chunk = SPA_MIN(samples - n, chunk_size); \ + for (k = 0; k < chunk; k++, n++) { \ + for (b = 0; b < blocks; b++) \ + *p++ = dbn; \ + } \ + res += write_data(wf, buf, \ + chunk * blocks * sizeof(type)); \ + } \ + return res; \ +} + +MAKE_WRITEN_FUNC(writen_8, uint8_t); +MAKE_WRITEN_FUNC(writen_16, uint16_t); +MAKE_WRITEN_FUNC(writen_24, uint24_t); +MAKE_WRITEN_FUNC(writen_32, uint32_t); +MAKE_WRITEN_FUNC(writen_64, uint64_t); + +static inline int write_n(int fd, const void *buf, int count) +{ + return write(fd, buf, count) == (ssize_t)count ? count : -errno; +} + +static inline int write_le16(int fd, uint16_t val) +{ + uint8_t buf2 = { val, val >> 8 }; + return write_n(fd, buf, 2); +} + +static inline int write_le32(int fd, uint32_t val) +{ + uint8_t buf4 = { val, val >> 8, val >> 16, val >> 24 }; + return write_n(fd, buf, 4); +} + +#define MAKE_AUDIO_RAW(format,bits,planar,fmt,...) \ + { SPA_MEDIA_TYPE_audio, SPA_MEDIA_SUBTYPE_raw, format, bits, planar, fmt, __VA_ARGS__ } + +static struct format_info { + uint32_t media_type; + uint32_t media_subtype; + uint32_t format; + uint32_t bits; + bool planar; + uint32_t fmt; + ssize_t (*write) (struct wav_file *wf, const void **data, size_t samples); +} format_info = { + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_U8P, 8, true, 1, writen_8), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_U8, 8, false, 1, writei), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S16P, 16, true, 1, writen_16), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S16_LE, 16, false, 1, writei), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S24P, 24, true, 1, writen_24), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S24_LE, 24, false, 1, writei), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S24_32P, 32, true, 1, writen_32), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S32P, 32, true, 1, writen_32), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S24_32_LE, 32, false, 1, writei), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S32_LE, 32, false, 1, writei), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_F32P, 32, true, 3, writen_32), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_F32_LE, 32, false, 3, writei), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_F64P, 64, true, 3, writen_64), + MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_F64_LE, 32, false, 3, writei), +}; + +#define CHECK_RES(expr) if ((res = (expr)) < 0) return res + +static int write_headers(struct wav_file *wf) +{ + int res; + uint32_t channels, rate, bps, bits; + const struct format_info *fi = wf->fi; + + lseek(wf->fd, 0, SEEK_SET); + + rate = wf->info.info.raw.rate; + channels = wf->info.info.raw.channels; + bits = fi->bits; + bps = channels * bits / 8; + + CHECK_RES(write_n(wf->fd, "RIFF", 4)); + CHECK_RES(write_le32(wf->fd, wf->length == 0 ? (uint32_t)-1 : wf->length + 12 + 8 + 16)); + CHECK_RES(write_n(wf->fd, "WAVE", 4)); + CHECK_RES(write_n(wf->fd, "fmt ", 4)); + CHECK_RES(write_le32(wf->fd, 16)); + CHECK_RES(write_le16(wf->fd, fi->fmt)); /* format */ + CHECK_RES(write_le16(wf->fd, channels)); /* channels */ + CHECK_RES(write_le32(wf->fd, rate)); /* rate */ + CHECK_RES(write_le32(wf->fd, bps * rate)); /* bytes per sec */ + CHECK_RES(write_le16(wf->fd, bps)); /* bytes per samples */ + CHECK_RES(write_le16(wf->fd, bits)); /* bits per sample */ + CHECK_RES(write_n(wf->fd, "data", 4)); + CHECK_RES(write_le32(wf->fd, wf->length == 0 ? (uint32_t)-1 : wf->length)); + + return 0; +} + +static const struct format_info *find_info(struct wav_file_info *info) +{ + uint32_t i; + + if (info->info.media_type != SPA_MEDIA_TYPE_audio || + info->info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return NULL; + + for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) { + if (format_infoi.format == info->info.info.raw.format) + return &format_infoi; + } + return NULL; +} + +static int open_write(struct wav_file *wf, const char *filename, struct wav_file_info *info) +{ + int res; + const struct format_info *fi; + + fi = find_info(info); + if (fi == NULL) + return -ENOTSUP; + + if ((wf->fd = open(filename, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0660)) < 0) { + res = -errno; + goto exit; + } + wf->info = info->info; + wf->fi = fi; + if (fi->planar) { + wf->stride = fi->bits / 8;
View file
pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/wavfile.h
Added
@@ -0,0 +1,39 @@ +/* PipeWire + * + * 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"), + * 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 <spa/utils/defs.h> +#include <spa/param/audio/format.h> + +struct wav_file; + +struct wav_file_info { + struct spa_audio_info info; +}; + +struct wav_file * +wav_file_open(const char *filename, const char *mode, struct wav_file_info *info); + +int wav_file_close(struct wav_file *wf); + +ssize_t wav_file_write(struct wav_file *wf, const void **data, size_t size);
View file
pipewire-0.3.67.tar.gz/spa/plugins/audiomixer/audiomixer.c -> pipewire-0.3.68.tar.gz/spa/plugins/audiomixer/audiomixer.c
Changed
@@ -770,9 +770,9 @@ size = SPA_MIN(bd->maxsize - offs, bd->chunk->size); maxsize = SPA_MIN(size, maxsize); - spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d", this, + spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d/%d", this, i, inio, outio, inio->status, inio->buffer_id, - offs, size); + offs, size, this->stride); if (!SPA_FLAG_IS_SET(bd->chunk->flags, SPA_CHUNK_FLAG_EMPTY)) { datasn_buffers = SPA_PTROFF(bd->data, offs, void); @@ -783,7 +783,8 @@ outb = dequeue_buffer(this, outport); if (SPA_UNLIKELY(outb == NULL)) { - spa_log_trace(this->log, "%p: out of buffers", this); + spa_log_trace(this->log, "%p: out of buffers (%d)", this, + outport->n_buffers); return -EPIPE; }
View file
pipewire-0.3.67.tar.gz/spa/plugins/audiomixer/mixer-dsp.c -> pipewire-0.3.68.tar.gz/spa/plugins/audiomixer/mixer-dsp.c
Changed
@@ -706,9 +706,9 @@ size = SPA_MIN(bd->maxsize - offs, bd->chunk->size); maxsize = SPA_MIN(maxsize, size); - spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d", this, + spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d/%d", this, i, inio, outio, inio->status, inio->buffer_id, - offs, size); + offs, size, (int)sizeof(float)); if (!SPA_FLAG_IS_SET(bd->chunk->flags, SPA_CHUNK_FLAG_EMPTY)) { datasn_buffers = SPA_PTROFF(bd->data, offs, void);
View file
pipewire-0.3.67.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c -> pipewire-0.3.68.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c
Changed
@@ -345,6 +345,14 @@ return 0; } +static void update_target(struct impl *this) +{ + if (this->position) { + this->position->clock.duration = this->position->clock.target_duration; + this->position->clock.rate = this->position->clock.target_rate; + } +} + static int make_buffer(struct impl *this) { struct buffer *b; @@ -424,6 +432,8 @@ struct impl *this = source->data; int res; + update_target(this); + res = make_buffer(this); if (res == SPA_STATUS_HAVE_DATA)
View file
pipewire-0.3.67.tar.gz/spa/plugins/avb/avb-pcm.c -> pipewire-0.3.68.tar.gz/spa/plugins/avb/avb-pcm.c
Changed
@@ -832,6 +832,17 @@ state->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL); } +static void update_position(struct state *state) +{ + if (state->position) { + state->duration = state->position->clock.duration; + state->rate_denom = state->position->clock.rate.denom; + } else { + state->duration = 1024; + state->rate_denom = state->rate; + } +} + static int flush_write(struct state *state, uint64_t current_time) { int32_t avail, wanted; @@ -884,6 +895,8 @@ uint32_t index, to_write; struct port *port = &state->ports0; + update_position(state); + filled = spa_ringbuffer_get_write_index(&state->ring, &index); if (filled < 0) { spa_log_warn(state->log, "underrun %d", filled); @@ -933,14 +946,16 @@ } spa_ringbuffer_write_update(&state->ring, index); - if (state->following) { + if (state->following) flush_write(state, state->position->clock.nsec); - } + return 0; } static int handle_play(struct state *state, uint64_t current_time) { + update_position(state); + flush_write(state, current_time); spa_node_call_ready(&state->callbacks, SPA_STATUS_NEED_DATA); return 0; @@ -955,8 +970,7 @@ struct spa_data *d; uint32_t n_bytes; - if (state->position) - state->duration = state->position->clock.duration; + update_position(state); avail = spa_ringbuffer_get_read_index(&state->ring, &index); wanted = state->duration * state->stride; @@ -1028,7 +1042,7 @@ { struct state *state = source->data; uint64_t expirations, current_time, duration; - uint32_t rate; + struct spa_fraction rate; int res; spa_log_trace(state->log, "timeout"); @@ -1042,24 +1056,24 @@ current_time = state->next_time; if (SPA_LIKELY(state->position)) { - duration = state->position->clock.duration; - rate = state->position->clock.rate.denom; + duration = state->position->clock.target_duration; + rate = state->position->clock.target_rate; } else { duration = 1024; - rate = 48000; + rate = SPA_FRACTION(1, 48000); } - state->duration = duration; + + state->next_time = current_time + duration * SPA_NSEC_PER_SEC / rate.denom; if (state->ports0.direction == SPA_DIRECTION_INPUT) handle_play(state, current_time); else handle_capture(state, current_time); - state->next_time = current_time + duration * SPA_NSEC_PER_SEC / rate; - if (SPA_LIKELY(state->clock)) { state->clock->nsec = current_time; - state->clock->position += duration; + state->clock->rate = rate; + state->clock->position += state->clock->duration; state->clock->duration = duration; state->clock->delay = 0; state->clock->rate_diff = 1.0; @@ -1134,13 +1148,7 @@ if (state->started) return 0; - if (state->position) { - state->duration = state->position->clock.duration; - state->rate_denom = state->position->clock.rate.denom; - } else { - state->duration = 1024; - state->rate_denom = state->rate; - } + update_position(state); spa_dll_init(&state->dll); state->max_error = (256.0 * state->rate) / state->rate_denom; @@ -1184,10 +1192,11 @@ struct state *state = user_data; spa_loop_remove_source(state->data_loop, &state->timer_source); + set_timeout(state, 0); - if (state->ports0.direction == SPA_DIRECTION_OUTPUT) { + if (state->ports0.direction == SPA_DIRECTION_OUTPUT) spa_loop_remove_source(state->data_loop, &state->sock_source); - } + return 0; } @@ -1201,7 +1210,6 @@ spa_loop_invoke(state->data_loop, do_remove_source, 0, NULL, 0, true, state); state->started = false; - set_timeout(state, 0); return 0; }
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/backend-hsphfpd.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/backend-hsphfpd.c
Changed
@@ -835,12 +835,14 @@ static void hsphfpd_audio_acquire_reply(DBusPendingCall *pending, void *user_data) { - struct impl *backend = user_data; + struct spa_bt_transport *transport = user_data; + struct impl *backend = SPA_CONTAINER_OF(transport->backend, struct impl, this); DBusMessage *r; const char *transport_path; const char *service_id; const char *agent_path; DBusError error; + int ret = 0; dbus_error_init(&error); @@ -853,16 +855,19 @@ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { spa_log_error(backend->log, "RegisterApplication() failed: %s", dbus_message_get_error_name(r)); + ret = -EIO; goto finish; } if (!spa_streq(dbus_message_get_sender(r), backend->hsphfpd_service_id)) { spa_log_error(backend->log, "Reply for " HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio() from invalid sender"); + ret = -EIO; goto finish; } if (!check_signature(r, "oso")) { spa_log_error(backend->log, "Invalid reply signature for " HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio()"); + ret = -EIO; goto finish; } @@ -872,11 +877,13 @@ DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID) == FALSE) { spa_log_error(backend->log, "Failed to parse " HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio() reply: %s", error.message); + ret = -EIO; goto finish; } if (!spa_streq(service_id, dbus_bus_get_unique_name(backend->conn))) { spa_log_warn(backend->log, HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio() failed: Other audio application took audio socket"); + ret = -EIO; goto finish; } @@ -885,6 +892,11 @@ finish: dbus_message_unref(r); dbus_pending_call_unref(pending); + + if (ret < 0) + spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ERROR); + else + spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ACTIVE); } static int hsphfpd_audio_acquire(void *data, bool optional) @@ -919,15 +931,10 @@ dbus_error_init(&err); dbus_connection_send_with_reply(backend->conn, m, &call, -1); - dbus_pending_call_set_notify(call, hsphfpd_audio_acquire_reply, backend, NULL); + dbus_pending_call_set_notify(call, hsphfpd_audio_acquire_reply, transport, NULL); dbus_message_unref(m); - /* The ConnectAudio method triggers Introspect and NewConnection calls, - which will set the fd to use for the SCO data. - We need to run the DBus loop to be able to reply to those method calls */ backend->acquire_in_progress = true; - while (backend->acquire_in_progress && dbus_connection_read_write_dispatch(backend->conn, -1)) - ; // empty loop body return 0; } @@ -941,6 +948,8 @@ spa_log_debug(backend->log, "transport %p: Release %s", transport, transport->path); + spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_IDLE); + if (transport->sco_io) { spa_bt_sco_io_destroy(transport->sco_io); transport->sco_io = NULL;
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/backend-native.c
Changed
@@ -40,6 +40,7 @@ #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic +#define PROP_KEY_ROLES "bluez5.roles" #define PROP_KEY_HEADSET_ROLES "bluez5.headset-roles" #define HFP_CODEC_SWITCH_INITIAL_TIMEOUT_MSEC 5000 @@ -111,6 +112,8 @@ struct transport_data { struct rfcomm *rfcomm; struct spa_source sco; + int err; + bool requesting; }; enum hfp_hf_state { @@ -1361,7 +1364,7 @@ bdaddr_t src; int sock = -1; - sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); + sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK, BTPROTO_SCO); if (sock < 0) { spa_log_error(backend->log, "socket(SEQPACKET, SCO) %s", strerror(errno)); goto fail; @@ -1414,6 +1417,8 @@ spa_log_debug(backend->log, "transport %p: enter sco_do_connect, codec=%u", t, t->codec); + td->err = -EIO; + if (d->adapter == NULL) return -EIO; @@ -1456,6 +1461,8 @@ goto fail_close; } + td->err = -EINPROGRESS; + return sock; fail_close: @@ -1465,37 +1472,76 @@ static int rfcomm_ag_sync_volume(struct rfcomm *rfcomm, bool later); -static void wait_for_socket(int fd) -{ - struct pollfd fds1; - const int timeout_ms = 500; - - fds0.fd = fd; - fds0.events = POLLIN | POLLERR | POLLHUP; - poll(fds, 1, timeout_ms); -} - -static int sco_acquire_cb(void *data, bool optional) +static void sco_ready(struct spa_bt_transport *t) { - struct spa_bt_transport *t = data; - struct transport_data *td = t->user_data; struct impl *backend = SPA_CONTAINER_OF(t->backend, struct impl, this); - int sock; + struct transport_data *td = t->user_data; + struct sco_options sco_opt; socklen_t len; + int err; - spa_log_debug(backend->log, "transport %p: enter sco_acquire_cb", t); + spa_log_debug(backend->log, "transport %p: ready", t); - if (optional || t->fd > 0) - sock = t->fd; - else - sock = sco_do_connect(t); + /* Read socket error status */ + if (t->fd >= 0) { + if (td->err == -EINPROGRESS) { + len = sizeof(err); + memset(&err, 0, len); + if (getsockopt(t->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) + td->err = -errno; + else + td->err = -err; + } + } else { + td->err = -EIO; + } - if (sock < 0) - goto fail; + if (!td->requesting) + return; -#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE - rfcomm_hfp_ag_set_cind(td->rfcomm, true); -#endif + td->requesting = false; + + if (td->err) + goto done; + + /* XXX: The MTU as currently reported by kernel (6.2) here is not a valid packet size, + * XXX: for USB adapters, see sco-io. + */ + len = sizeof(sco_opt); + memset(&sco_opt, 0, len); + if (getsockopt(t->fd, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) { + spa_log_warn(backend->log, "getsockopt(SCO_OPTIONS) failed, using defaults"); + t->read_mtu = 48; + t->write_mtu = 48; + } else { + spa_log_debug(backend->log, "autodetected mtu = %u", sco_opt.mtu); + t->read_mtu = sco_opt.mtu; + t->write_mtu = sco_opt.mtu; + } + + /* Clear nonblocking flag we set for connect() */ + err = fcntl(t->fd, F_GETFL, O_NONBLOCK); + if (err < 0) { + td->err = -errno; + goto done; + } + err &= ~O_NONBLOCK; + err = fcntl(t->fd, F_SETFL, O_NONBLOCK, err); + if (err < 0) { + td->err = -errno; + goto done; + } + +done: + if (td->err) { + spa_log_debug(backend->log, "transport %p: acquire failed: %s (%d)", + t, strerror(-td->err), td->err); + spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_ERROR); + return; + } + + spa_log_debug(backend->log, "transport %p: acquire complete, read_mtu=%u, write_mtu=%u", + t, t->read_mtu, t->write_mtu); /* * Send RFCOMM volume after connection is ready, and also after @@ -1512,55 +1558,65 @@ * volume buttons, which is updated by an +VGS event only after * sufficient time is elapsed from the connection. */ - wait_for_socket(sock); rfcomm_ag_sync_volume(td->rfcomm, false); rfcomm_ag_sync_volume(td->rfcomm, true); - t->fd = sock; + spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_ACTIVE); +} + +static void sco_start_source(struct spa_bt_transport *t); + +static int sco_acquire_cb(void *data, bool optional) +{ + struct spa_bt_transport *t = data; + struct transport_data *td = t->user_data; + struct impl *backend = SPA_CONTAINER_OF(t->backend, struct impl, this); + int sock; - /* Fallback value */ - t->read_mtu = 48; - t->write_mtu = 48; + spa_log_debug(backend->log, "transport %p: enter sco_acquire_cb", t); - if (true) { - struct sco_options sco_opt; + if (optional || t->fd > 0) + sock = t->fd; + else + sock = sco_do_connect(t); - len = sizeof(sco_opt); - memset(&sco_opt, 0, len); + if (sock < 0) + goto fail; - if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) - spa_log_warn(backend->log, "getsockopt(SCO_OPTIONS) failed, loading defaults"); - else { - spa_log_debug(backend->log, "autodetected mtu = %u", sco_opt.mtu); - t->read_mtu = sco_opt.mtu; - t->write_mtu = sco_opt.mtu; - } - } - spa_log_debug(backend->log, "transport %p: read_mtu=%u, write_mtu=%u", t, t->read_mtu, t->write_mtu); +#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE + rfcomm_hfp_ag_set_cind(td->rfcomm, true); +#endif + + t->fd = sock; + + td->requesting = true; +
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/backend-ofono.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/backend-ofono.c
Changed
@@ -231,7 +231,9 @@ ts.tv_nsec = 1; spa_loop_utils_update_timer(backend->loop_utils, backend->timer, &ts, NULL, false); - return -EIO; + + ret = -EIO; + goto finish; } td->broken = false; @@ -243,6 +245,11 @@ ret = 0; finish: + if (ret < 0) + spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ERROR); + else + spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ACTIVE); + return ret; } @@ -254,6 +261,8 @@ spa_log_debug(backend->log, "transport %p: Release %s", transport, transport->path); + spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_IDLE); + if (transport->sco_io) { spa_bt_sco_io_destroy(transport->sco_io); transport->sco_io = NULL;
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/bluez5-dbus.c
Changed
@@ -35,6 +35,7 @@ #include "config.h" #include "codec-loader.h" #include "player.h" +#include "iso-io.h" #include "defs.h" static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5"); @@ -65,6 +66,12 @@ #define CODEC_SWITCH_RETRIES 1 +/* How many times to retry acquire on errors, and how long delay to require before we can + * try again. + */ +#define TRANSPORT_ERROR_MAX_RETRY 3 +#define TRANSPORT_ERROR_TIMEOUT (2*BLUEZ_ACTION_RATE_MSEC*SPA_NSEC_PER_MSEC) + struct spa_bt_monitor { struct spa_handle handle; @@ -72,7 +79,9 @@ struct spa_log *log; struct spa_loop *main_loop; + struct spa_loop *data_loop; struct spa_system *main_system; + struct spa_system *data_system; struct spa_plugin_loader *plugin_loader; struct spa_dbus *dbus; struct spa_dbus_connection *dbus_connection; @@ -103,6 +112,8 @@ struct spa_dict enabled_codecs; + enum spa_bt_profile enabled_profiles; + unsigned int connection_info_supported:1; unsigned int dummy_avrcp_player:1; @@ -178,11 +189,12 @@ * SCO socket connect may fail with ECONNABORTED if it is done too soon after * previous close. To avoid this in cases where nodes are toggled between * stopped/started rapidly, postpone release until the transport has remained - * unused for a time. Since this appears common to multiple SCO backends, we do - * it for all SCO backends here. + * unused for a time. + * + * Avoiding unnecessary release+reacquire also makes sense for other transports, + * so we use the release timeout for all of them. */ -#define SCO_TRANSPORT_RELEASE_TIMEOUT_MSEC 1000 -#define SPA_BT_TRANSPORT_IS_SCO(transport) (transport->backend != NULL) +#define TRANSPORT_RELEASE_TIMEOUT_MSEC 1000 #define TRANSPORT_VOLUME_TIMEOUT_MSEC 200 @@ -508,6 +520,19 @@ } } +static enum spa_bt_profile get_codec_profile(const struct media_codec *codec, + enum spa_bt_media_direction direction) +{ + switch (direction) { + case SPA_BT_MEDIA_SOURCE: + return codec->bap ? SPA_BT_PROFILE_BAP_SOURCE : SPA_BT_PROFILE_A2DP_SOURCE; + case SPA_BT_MEDIA_SINK: + return codec->bap ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_A2DP_SINK; + default: + spa_assert_not_reached(); + } +} + static bool endpoint_should_be_registered(struct spa_bt_monitor *monitor, const struct media_codec *codec, enum spa_bt_media_direction direction) @@ -517,7 +542,8 @@ */ return is_media_codec_enabled(monitor, codec) && codec_has_direction(codec, direction) && - codec->fill_caps; + codec->fill_caps && + (get_codec_profile(codec, direction) & monitor->enabled_profiles); } static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) @@ -1209,11 +1235,17 @@ return NULL; } -void spa_bt_device_update_last_bluez_action_time(struct spa_bt_device *device) +static uint64_t get_time_now(struct spa_bt_monitor *monitor) { struct timespec ts; - spa_system_clock_gettime(device->monitor->main_system, CLOCK_MONOTONIC, &ts); - device->last_bluez_action_time = SPA_TIMESPEC_TO_NSEC(&ts); + + spa_system_clock_gettime(monitor->main_system, CLOCK_MONOTONIC, &ts); + return SPA_TIMESPEC_TO_NSEC(&ts); +} + +void spa_bt_device_update_last_bluez_action_time(struct spa_bt_device *device) +{ + device->last_bluez_action_time = get_time_now(device->monitor); } static struct spa_bt_device *device_create(struct spa_bt_monitor *monitor, const char *path) @@ -2199,6 +2231,8 @@ t->sco_io = NULL; t->delay_us = SPA_BT_UNKNOWN_DELAY; t->latency_us = SPA_BT_UNKNOWN_DELAY; + t->bap_cig = 0xff; + t->bap_cis = 0xff; t->user_data = SPA_PTROFF(t, sizeof(struct spa_bt_transport), void); spa_hook_list_init(&t->listener_list); spa_list_init(&t->bap_transport_linked); @@ -2236,6 +2270,18 @@ spa_bt_transport_emit_state_changed(transport, old, state); if (state >= SPA_BT_TRANSPORT_STATE_PENDING && old < SPA_BT_TRANSPORT_STATE_PENDING) transport_sync_volume(transport); + + if (state == SPA_BT_TRANSPORT_STATE_ERROR) { + uint64_t now = get_time_now(monitor); + + if (now > transport->last_error_time + TRANSPORT_ERROR_TIMEOUT) { + spa_log_error(monitor->log, "Failure in Bluetooth audio transport %s", + transport->path); + } + + transport->last_error_time = now; + ++transport->error_count; + } } } @@ -2261,8 +2307,16 @@ transport->sco_io = NULL; } + if (transport->iso_io) + spa_bt_iso_io_destroy(transport->iso_io); + spa_bt_transport_destroy(transport); + if (transport->acquire_call) { + dbus_pending_call_cancel(transport->acquire_call); + transport->acquire_call = NULL; + } + if (transport->fd >= 0) { spa_bt_player_set_state(transport->device->adapter->dummy_player, SPA_BT_PLAYER_STOPPED); @@ -2313,10 +2367,17 @@ if (transport->acquire_refcount > 0) { spa_log_debug(monitor->log, "transport %p: incref %s", transport, transport->path); transport->acquire_refcount += 1; + spa_bt_transport_emit_state_changed(transport, transport->state, transport->state); return 0; } spa_assert(transport->acquire_refcount == 0); + /* If we are getting into error state too often, stop trying */ + if (get_time_now(monitor) > transport->last_error_time + TRANSPORT_ERROR_TIMEOUT) + transport->error_count = 0; + if (transport->error_count >= TRANSPORT_ERROR_MAX_RETRY) + return -EIO; + if (!transport->acquired) res = spa_bt_transport_impl(transport, acquire, 0, optional); else @@ -2333,11 +2394,11 @@ int spa_bt_transport_release(struct spa_bt_transport *transport) { struct spa_bt_monitor *monitor = transport->monitor; - int res; if (transport->acquire_refcount > 1) { spa_log_debug(monitor->log, "transport %p: decref %s", transport, transport->path); transport->acquire_refcount -= 1; + spa_bt_transport_emit_state_changed(transport, transport->state, transport->state); return 0; } else if (transport->acquire_refcount == 0) { @@ -2347,23 +2408,8 @@ spa_assert(transport->acquire_refcount == 1); spa_assert(transport->acquired); - if (SPA_BT_TRANSPORT_IS_SCO(transport)) { - /* Postpone SCO transport releases, since we might need it again soon */ - res = spa_bt_transport_start_release_timer(transport); - } else if (transport->keepalive) { - res = 0; - transport->acquire_refcount = 0; - spa_log_debug(monitor->log, "transport %p: keepalive %s on release", - transport, transport->path); - } else { - res = spa_bt_transport_impl(transport, release, 0); - if (res >= 0) { - transport->acquire_refcount = 0;
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/bluez5-device.c
Changed
@@ -1038,13 +1038,16 @@ static void set_initial_profile(struct impl *this); -static void device_connected(void *userdata, bool connected) { +static void device_connected(void *userdata, bool connected) +{ struct impl *this = userdata; spa_log_debug(this->log, "connected: %d", connected); - if (connected ^ (this->profile != DEVICE_PROFILE_OFF)) + if (connected ^ (this->profile != DEVICE_PROFILE_OFF)) { + emit_remove_nodes(this); set_initial_profile(this); + } } static const struct spa_bt_device_events bt_dev_events = {
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/decode-buffer.h -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/decode-buffer.h
Changed
@@ -38,10 +38,13 @@ #include <spa/utils/defs.h> #include <spa/support/log.h> +#include "rate-control.h" + #define BUFFERING_LONG_MSEC (2*60000) #define BUFFERING_SHORT_MSEC 1000 #define BUFFERING_RATE_DIFF_MAX 0.005 + /** * Safety margin. * @@ -51,131 +54,6 @@ #define BUFFERING_TARGET(spike,packet_size,max_buf) \ SPA_CLAMP((spike)*3/2, (packet_size), (max_buf) - 2*(packet_size)) -/** - * Rate controller. - * - * It's here in a form, where it operates on the running average - * so it's compatible with the level spike determination, and - * clamping the rate to a range is easy. The impulse response - * is similar to spa_dll, and step response does not have sign changes. - * - * The controller iterates as - * - * avg(j+1) = (1 - beta) avg(j) + beta level(j) - * corr(j+1) = corr(j) + a avg(j+1) - avg(j) / duration - * + b avg(j) - target / duration - * - * with beta = duration/avg_period < 0.5 is the moving average parameter, - * and a = beta/3 + ..., b = beta^2/27 + .... - * - * This choice results to c(j) being low-pass filtered, and buffer level(j) - * converging towards target with stable damped evolution with eigenvalues - * real and close to each other around (1 - beta)^(1/3). - * - * Derivation: - * - * The deviation from the buffer level target evolves as - * - * delta(j) = level(j) - target - * delta(j+1) = delta(j) + r(j) - c(j+1) - * - * where r is samples received in one duration, and c corrected rate - * (samples per duration). - * - * The rate correction is in general determined by linear filter f - * - * c(j+1) = c(j) + \sum_{k=0}^\infty delta(j - k) f(k) - * - * If \sum_k f(k) is not zero, the only fixed point is c=r, delta=0, - * so this structure (if the filter is stable) rate matches and - * drives buffer level to target. - * - * The z-transform then is - * - * delta(z) = G(z) r(z) - * c(z) = F(z) delta(z) - * G(z) = (z - 1) / (z - 1)^2 + z f(z) - * F(z) = f(z) / (z - 1) - * - * We now want: poles of G(z) must be in |z|<1 for stability, F(z) - * should damp high frequencies, and f(z) is causal. - * - * To satisfy the conditions, take - * - * (z - 1)^2 + z f(z) = p(z) / q(z) - * - * where p(z) is polynomial with leading term z^n with wanted root - * structure, and q(z) is any polynomial with leading term z^{n-2}. - * This guarantees f(z) is causal, and G(z) = (z-1) q(z) / p(z). - * We can choose p(z) and q(z) to improve low-pass properties of F(z). - * - * Simplest choice is p(z)=(z-x)^2 and q(z)=1, but that gives flat - * high frequency response in F(z). Better choice is p(z) = (z-u)*(z-v)*(z-w) - * and q(z) = z - r. To make F(z) better lowpass, one can cancel - * a resulting 1/z pole in F(z) by setting r=u*v*w. Then, - * - * G(z) = (z - u*v*w)*(z - 1) / (z - u)*(z - v)*(z - w) - * F(z) = (a z + b - a) / (z - 1) * H(z) - * H(z) = beta / (z - 1 + beta) - * beta = 1 - u*v*w - * a = (1-u) + (1-v) + (1-w) - beta / beta - * b = (1-u)*(1-v)*(1-w) / beta - * - * which corresponds to iteration for c(j): - * - * avg(j+1) = (1 - beta) avg(j) + beta delta(j) - * c(j+1) = c(j) + a avg(j+1) - avg(j) + b avg(j) - * - * So the controller operates on the running average, - * which gives the low-pass property for c(j). - * - * The simplest filter is obtained by putting the poles at - * u=v=w=(1-beta)**(1/3). Since beta << 1, computing the root - * can be avoided by expanding in series. - * - * Overshoot in impulse response could be reduced by moving one of the - * poles closer to z=1, but this increases the step response time. - */ -struct spa_bt_rate_control -{ - double avg; - double corr; -}; - -static void spa_bt_rate_control_init(struct spa_bt_rate_control *this, double level) -{ - this->avg = level; - this->corr = 1.0; -} - -static double spa_bt_rate_control_update(struct spa_bt_rate_control *this, double level, - double target, double duration, double period) -{ - /* - * u = (1 - beta)^(1/3) - * x = a / beta - * y = b / beta - * a = (2 + u) * (1 - u)^2 / beta - * b = (1 - u)^3 / beta - * beta -> 0 - */ - const double beta = SPA_CLAMP(duration / period, 0, 0.5); - const double x = 1.0/3; - const double y = beta/27; - double avg; - - avg = beta * level + (1 - beta) * this->avg; - this->corr += x * (avg - this->avg) / period - + y * (this->avg - target) / period; - this->avg = avg; - - this->corr = SPA_CLAMP(this->corr, - 1 - BUFFERING_RATE_DIFF_MAX, - 1 + BUFFERING_RATE_DIFF_MAX); - - return this->corr; -} - /** Windowed min/max */ struct spa_bt_ptp @@ -463,7 +341,8 @@ } this->corr = spa_bt_rate_control_update(&this->ctl, - level, target, this->prev_consumed, avg_period); + level, target, this->prev_consumed, avg_period, + BUFFERING_RATE_DIFF_MAX); spa_bt_decode_buffer_get_read(this, &avail);
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/defs.h
Changed
@@ -522,6 +522,8 @@ #define spa_bt_device_add_listener(d,listener,events,data) \ spa_hook_list_append(&(d)->listener_list, listener, events, data) +struct spa_bt_iso_io; + struct spa_bt_sco_io; struct spa_bt_sco_io *spa_bt_sco_io_create(struct spa_loop *data_loop, int fd, uint16_t read_mtu, uint16_t write_mtu); @@ -539,9 +541,10 @@ #define SPA_BT_VOLUME_A2DP_MAX 127 enum spa_bt_transport_state { - SPA_BT_TRANSPORT_STATE_IDLE, - SPA_BT_TRANSPORT_STATE_PENDING, - SPA_BT_TRANSPORT_STATE_ACTIVE, + SPA_BT_TRANSPORT_STATE_ERROR = -1, + SPA_BT_TRANSPORT_STATE_IDLE = 0, + SPA_BT_TRANSPORT_STATE_PENDING = 1, + SPA_BT_TRANSPORT_STATE_ACTIVE = 2, }; struct spa_bt_transport_events { @@ -600,16 +603,22 @@ int acquire_refcount; bool acquired; bool keepalive; + int error_count; + uint64_t last_error_time; int fd; uint16_t read_mtu; uint16_t write_mtu; unsigned int delay_us; unsigned int latency_us; + uint8_t bap_cig; + uint8_t bap_cis; + struct spa_bt_iso_io *iso_io; struct spa_bt_sco_io *sco_io; struct spa_source volume_timer; struct spa_source release_timer; + DBusPendingCall *acquire_call; struct spa_hook_list listener_list; struct spa_callbacks impl;
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/iso-io.c
Added
@@ -0,0 +1,377 @@ +/* Spa ISO I/O */ +/* SPDX-FileCopyrightText: Copyright © 2023 Pauli Virtanen. */ +/* SPDX-License-Identifier: MIT */ + +#include <unistd.h> +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <limits.h> +#include <sys/socket.h> + +#include <spa/support/loop.h> +#include <spa/support/log.h> +#include <spa/utils/list.h> +#include <spa/utils/string.h> +#include <spa/utils/result.h> +#include <spa/node/io.h> + +#include <bluetooth/bluetooth.h> + +#include "config.h" +#include "iso-io.h" + +static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.iso"); +#undef SPA_LOG_TOPIC_DEFAULT +#define SPA_LOG_TOPIC_DEFAULT &log_topic + +#define IDLE_TIME (100 * SPA_NSEC_PER_MSEC) + +struct group { + struct spa_log *log; + struct spa_log_topic log_topic; + struct spa_loop *data_loop; + struct spa_system *data_system; + struct spa_source source; + struct spa_list streams; + int timerfd; + uint8_t cig; + uint64_t next; + uint64_t duration; + uint32_t paused; +}; + +struct stream { + struct spa_bt_iso_io this; + struct spa_list link; + struct group *group; + int fd; + bool sink; + bool idle; + + spa_bt_iso_io_pull_t pull; +}; + +struct modify_info +{ + struct stream *stream; + struct spa_list *streams; +}; + +static int do_modify(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct modify_info *info = user_data; + + if (info->streams) + spa_list_append(info->streams, &info->stream->link); + else + spa_list_remove(&info->stream->link); + + return 0; +} + +static void stream_link(struct group *group, struct stream *stream) +{ + struct modify_info info = { .stream = stream, .streams = &group->streams }; + int res; + + res = spa_loop_invoke(group->data_loop, do_modify, 0, NULL, 0, true, &info); + spa_assert_se(res == 0); +} + +static void stream_unlink(struct stream *stream) +{ + struct modify_info info = { .stream = stream, .streams = NULL }; + int res; + + res = spa_loop_invoke(stream->group->data_loop, do_modify, 0, NULL, 0, true, &info); + spa_assert_se(res == 0); +} + +static int set_timeout(struct group *group, uint64_t time) +{ + struct itimerspec ts; + ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC; + ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + return spa_system_timerfd_settime(group->data_system, + group->timerfd, SPA_FD_TIMER_ABSTIME, &ts, NULL); +} + +static int set_timers(struct group *group) +{ + struct timespec now; + + spa_system_clock_gettime(group->data_system, CLOCK_MONOTONIC, &now); + group->next = SPA_ROUND_UP(SPA_TIMESPEC_TO_NSEC(&now) + group->duration, + group->duration); + + return set_timeout(group, group->next); +} + +static void group_on_timeout(struct spa_source *source) +{ + struct group *group = source->data; + struct stream *stream; + uint64_t exp; + int res; + bool active = false; + + if ((res = spa_system_timerfd_read(group->data_system, group->timerfd, &exp)) < 0) { + if (res != -EAGAIN) + spa_log_warn(group->log, "%p: ISO group:%u error reading timerfd: %s", + group, group->cig, spa_strerror(res)); + return; + } + + /* + * If an idle stream activates when another stream is already active, + * pause output of all streams for a while to avoid desynchronization. + */ + spa_list_for_each(stream, &group->streams, link) { + if (!stream->sink) + continue; + if (!stream->idle) { + active = true; + break; + } + } + + spa_list_for_each(stream, &group->streams, link) { + if (!stream->sink) + continue; + + if (stream->idle && stream->this.size > 0 && active && !group->paused) + group->paused = 1u + IDLE_TIME / group->duration; + + stream->idle = (stream->this.size == 0); + } + + if (group->paused) { + --group->paused; + spa_log_debug(group->log, "%p: ISO group:%d paused:%u", group, group->cig, group->paused); + } + + /* Produce output */ + spa_list_for_each(stream, &group->streams, link) { + int res; + + if (!stream->sink) + continue; + if (stream->idle) + continue; + if (group->paused) { + stream->this.size = 0; + continue; + } + + res = send(stream->fd, stream->this.buf, stream->this.size, MSG_DONTWAIT | MSG_NOSIGNAL); + if (res < 0) + res = -errno; + + spa_log_trace(group->log, "%p: ISO group:%u sent fd:%d size:%u ts:%u res:%d", + group, group->cig, stream->fd, (unsigned)stream->this.size, + (unsigned)stream->this.timestamp, res); + + stream->this.size = 0; + } + + /* Pull data for the next interval */ + group->next += exp * group->duration; + + spa_list_for_each(stream, &group->streams, link) { + if (!stream->sink) + continue; + + if (stream->pull) { + stream->this.now = group->next; + stream->pull(&stream->this); + } else { + stream->this.size = 0; + } + } + + set_timeout(group, group->next); +} + +static struct group *group_create(int fd, struct spa_log *log, struct spa_loop *data_loop, + struct spa_system *data_system)
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/iso-io.h
Added
@@ -0,0 +1,39 @@ +/* Spa Bluez5 ISO I/O */ +/* SPDX-FileCopyrightText: Copyright © 2023 Pauli Virtanen */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_BLUEZ5_ISO_IO_H +#define SPA_BLUEZ5_ISO_IO_H + +#include <spa/utils/defs.h> +#include <spa/support/loop.h> +#include <spa/support/log.h> +#include <spa/node/io.h> + +/** + * ISO I/O. + * + * Synchronizes related writes from different streams in the same group + * to occur at same real time instant (or not at all). + */ +struct spa_bt_iso_io +{ + uint64_t now; + uint64_t duration; + + uint32_t timestamp; + uint8_t buf4096; + size_t size; + + void *user_data; +}; + +typedef void (*spa_bt_iso_io_pull_t)(struct spa_bt_iso_io *io); + +struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, struct spa_log *log, + struct spa_loop *data_loop, struct spa_system *data_system); +struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *io, int fd, bool sink); +void spa_bt_iso_io_destroy(struct spa_bt_iso_io *io); +void spa_bt_iso_io_set_cb(struct spa_bt_iso_io *io, spa_bt_iso_io_pull_t pull, void *user_data); + +#endif
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/media-sink.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/media-sink.c
Changed
@@ -31,11 +31,15 @@ #include <spa/debug/mem.h> #include <spa/debug/log.h> +#include <bluetooth/bluetooth.h> + #include <sbc/sbc.h> #include "defs.h" #include "rtp.h" #include "media-codecs.h" +#include "rate-control.h" +#include "iso-io.h" static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.sink.media"); #undef SPA_LOG_TOPIC_DEFAULT @@ -52,6 +56,7 @@ #define MIN_BUFFERS 2 #define MAX_BUFFERS 32 #define BUFFER_SIZE (8192*8) +#define RATE_CTL_DIFF_MAX 0.005 struct buffer { uint32_t id; @@ -70,6 +75,7 @@ uint64_t info_all; struct spa_port_info info; struct spa_io_buffers *io; + struct spa_io_rate_match *rate_match; struct spa_latency_info latency; #define IDX_EnumFormat 0 #define IDX_Meta 1 @@ -87,6 +93,8 @@ struct spa_list ready; size_t ready_offset; + + struct spa_bt_rate_control ratectl; }; struct impl { @@ -116,6 +124,8 @@ struct port port; unsigned int started:1; + unsigned int start_ready:1; + unsigned int transport_started:1; unsigned int following:1; unsigned int is_output:1; unsigned int flush_pending:1; @@ -135,6 +145,8 @@ uint64_t next_time; uint64_t last_error; uint64_t process_time; + uint64_t process_duration; + uint64_t process_rate; uint64_t prev_flush_time; uint64_t next_flush_time; @@ -147,6 +159,8 @@ int need_flush; bool fragment; + bool resync; + bool have_iso_packet; uint32_t block_size; uint8_t bufferBUFFER_SIZE; uint32_t buffer_used; @@ -270,52 +284,75 @@ return set_timeout(this, this->following ? 0 : this->next_time); } -static int do_reassign_follower(struct spa_loop *loop, +static inline bool is_following(struct impl *this) +{ + return this->position && this->clock && this->position->clock.id != this->clock->id; +} + +struct reassign_io_info { + struct impl *this; + struct spa_io_position *position; + struct spa_io_clock *clock; +}; + +static int do_reassign_io(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) { - struct impl *this = user_data; - set_timers(this); - return 0; -} + struct reassign_io_info *info = user_data; + struct impl *this = info->this; + bool following; -static inline bool is_following(struct impl *this) -{ - return this->position && this->clock && this->position->clock.id != this->clock->id; + if (this->position != info->position || this->clock != info->clock) + this->resync = true; + + this->position = info->position; + this->clock = info->clock; + + following = is_following(this); + + if (following != this->following) { + spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following); + this->following = following; + set_timers(this); + } + + return 0; } static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) { struct impl *this = object; - bool following; + struct reassign_io_info info = { .this = this, .position = this->position, .clock = this->clock }; spa_return_val_if_fail(this != NULL, -EINVAL); switch (id) { case SPA_IO_Clock: - this->clock = data; - if (this->clock != NULL) { - spa_scnprintf(this->clock->name, - sizeof(this->clock->name), + info.clock = data; + if (info.clock != NULL) { + spa_scnprintf(info.clock->name, + sizeof(info.clock->name), "%s", this->props.clock_name); } break; case SPA_IO_Position: - this->position = data; + info.position = data; break; default: return -ENOENT; } - following = is_following(this); - if (this->started && following != this->following) { - spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following); - this->following = following; - spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this); + if (this->started) { + spa_loop_invoke(this->data_loop, do_reassign_io, 0, NULL, 0, true, &info); + } else { + this->clock = info.clock; + this->position = info.position; } + return 0; } @@ -395,6 +432,62 @@ return 0; } +static uint32_t get_queued_frames(struct impl *this) +{ + struct port *port = &this->port; + uint32_t bytes = 0; + struct buffer *b; + + spa_list_for_each(b, &port->ready, link) { + struct spa_data *d = b->buf->datas; + + bytes += d0.chunk->size; + } + + if (bytes > port->ready_offset) + bytes -= port->ready_offset; + else + bytes = 0; + + /* Count (partially) encoded packet */ + bytes += this->tmp_buffer_used; + bytes += this->block_count * this->block_size; + + return bytes / port->frame_size; +} + +static uint64_t get_reference_time(struct impl *this, uint64_t *duration_ns_ret) +{ + struct port *port = &this->port; + uint64_t t, duration_ns; + + if (!this->process_rate || !this->process_duration) { + if (this->position) { + this->process_duration = this->position->clock.duration; + this->process_rate = this->position->clock.rate.denom;
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/media-source.c
Changed
@@ -114,7 +114,8 @@ struct port port; unsigned int started:1; - unsigned int transport_acquired:1; + unsigned int start_ready:1; + unsigned int transport_started:1; unsigned int following:1; unsigned int matching:1; unsigned int resampling:1; @@ -258,7 +259,8 @@ struct port *port = &this->port; set_timers(this); - spa_bt_decode_buffer_recover(&port->buffer); + if (this->transport_started) + spa_bt_decode_buffer_recover(&port->buffer); return 0; } @@ -561,12 +563,15 @@ { struct port *port = &this->port; + if (!this->transport_started) + port->buffer.corr = 1.0; + if (this->position && port->rate_match) { port->rate_match->rate = 1 / port->buffer.corr; this->matching = this->following; this->resampling = this->matching || - (port->current_format.info.raw.rate != this->position->clock.rate.denom); + (port->current_format.info.raw.rate != this->position->clock.target_rate.denom); } else { this->matching = false; this->resampling = false; @@ -607,8 +612,8 @@ now_time, now_time - prev_time); if (SPA_LIKELY(this->position)) { - duration = this->position->clock.duration; - rate = this->position->clock.rate.denom; + duration = this->position->clock.target_duration; + rate = this->position->clock.target_rate.denom; } else { duration = 1024; rate = 48000; @@ -620,7 +625,8 @@ if (SPA_LIKELY(this->clock)) { this->clock->nsec = now_time; - this->clock->position += duration; + this->clock->rate = this->clock->target_rate; + this->clock->position += this->clock->duration; this->clock->duration = duration; this->clock->rate_diff = port->buffer.corr; this->clock->next_nsec = this->next_time; @@ -643,15 +649,15 @@ struct port *port = &this->port; uint32_t flags; - if (this->transport_acquired) + if (this->transport_started) return 0; + if (!this->start_ready) + return -EIO; - spa_log_debug(this->log, "%p: transport %p acquire", this, - this->transport); - if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) - return res; + spa_return_val_if_fail(this->transport != NULL, -EIO); - this->transport_acquired = true; + spa_log_debug(this->log, "%p: start transport state:%d", + this, this->transport->state); flags = this->is_duplex ? 0 : MEDIA_CODEC_FLAG_SINK; @@ -724,25 +730,16 @@ set_duplex_timeout(this, this->duplex_timeout); } - this->timer_source.data = this; - this->timer_source.fd = this->timerfd; - this->timer_source.func = media_on_timeout; - this->timer_source.mask = SPA_IO_IN; - this->timer_source.rmask = 0; - spa_loop_add_source(this->data_loop, &this->timer_source); - this->sample_count = 0; - setup_matching(this); - - set_timers(this); + this->transport_started = true; return 0; } static int do_start(struct impl *this) { - int res = 0; + int res; if (this->started) return 0; @@ -751,16 +748,31 @@ this->following = is_following(this); - spa_log_debug(this->log, "%p: start state:%d following:%d", - this, this->transport->state, this->following); + this->start_ready = true; - if (this->transport->state >= SPA_BT_TRANSPORT_STATE_PENDING || - this->is_duplex || this->codec->bap) - res = transport_start(this); + spa_log_debug(this->log, "%p: start following:%d", this, this->following); + + spa_log_debug(this->log, "%p: transport %p acquire", this, + this->transport); + if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) { + this->start_ready = false; + return res; + } + + this->timer_source.data = this; + this->timer_source.fd = this->timerfd; + this->timer_source.func = media_on_timeout; + this->timer_source.mask = SPA_IO_IN; + this->timer_source.rmask = 0; + spa_loop_add_source(this->data_loop, &this->timer_source); + + setup_matching(this); + + set_timers(this); this->started = true; - return res; + return 0; } static int do_remove_source(struct spa_loop *loop, @@ -771,54 +783,58 @@ void *user_data) { struct impl *this = user_data; - struct itimerspec ts; spa_log_debug(this->log, "%p: remove source", this); + if (this->timer_source.loop) + spa_loop_remove_source(this->data_loop, &this->timer_source); + set_timeout(this, 0); + + return 0; +} + +static int do_remove_transport_source(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *this = user_data; + + spa_log_debug(this->log, "%p: remove transport source", this); + + this->transport_started = false; + set_duplex_timeout(this, 0); if (this->source.loop) spa_loop_remove_source(this->data_loop, &this->source); - if (this->timer_source.loop) - spa_loop_remove_source(this->data_loop, &this->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->timerfd, 0, &ts, NULL); - return 0; } -static int transport_stop(struct impl *this) +static void transport_stop(struct impl *this) { struct port *port = &this->port; - int res; + + if (!this->transport_started) + return; spa_log_debug(this->log, "%p: transport stop", this);
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/meson.build -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/meson.build
Changed
@@ -19,6 +19,7 @@ 'sco-sink.c', 'sco-source.c', 'sco-io.c', + 'iso-io.c', 'quirks.c', 'player.c', 'bluez5-device.c',
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/midi-node.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/midi-node.c
Changed
@@ -868,13 +868,20 @@ spa_log_trace(this->log, "%p: timer %"PRIu64" %"PRIu64"", this, now_time, now_time - prev_time); - update_position(this); + if (SPA_LIKELY(this->position)) { + this->duration = this->position->clock.target_duration; + this->rate = this->position->clock.target_rate.denom; + } else { + this->duration = 1024; + this->rate = 48000; + } this->next_time = now_time + this->duration * SPA_NSEC_PER_SEC / this->rate; if (SPA_LIKELY(this->clock)) { this->clock->nsec = now_time; - this->clock->position += this->duration; + this->clock->rate = this->clock->target_rate; + this->clock->position += this->clock->duration; this->clock->duration = this->duration; this->clock->rate_diff = 1.0f; this->clock->next_nsec = this->next_time; @@ -1103,17 +1110,10 @@ void *user_data) { struct impl *this = user_data; - struct itimerspec ts; if (this->timer_source.loop) spa_loop_remove_source(this->data_loop, &this->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->timerfd, 0, &ts, NULL); - + set_timeout(this, 0); return 0; } @@ -1217,7 +1217,6 @@ void *user_data) { struct impl *this = user_data; - set_timers(this); return 0; }
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/rate-control.h
Added
@@ -0,0 +1,133 @@ +/* Spa Bluez5 rate control */ +/* SPDX-FileCopyrightText: Copyright © 2022 Pauli Virtanen */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_BLUEZ5_RATE_CONTROL_H +#define SPA_BLUEZ5_RATE_CONTROL_H + +#include <spa/utils/defs.h> + +/** + * Rate controller. + * + * It's here in a form, where it operates on the running average + * so it's compatible with the level spike determination, and + * clamping the rate to a range is easy. The impulse response + * is similar to spa_dll, and step response does not have sign changes. + * + * The controller iterates as + * + * avg(j+1) = (1 - beta) avg(j) + beta level(j) + * corr(j+1) = corr(j) + a avg(j+1) - avg(j) / duration + * + b avg(j) - target / duration + * + * with beta = duration/avg_period < 0.5 is the moving average parameter, + * and a = beta/3 + ..., b = beta^2/27 + .... + * + * This choice results to c(j) being low-pass filtered, and buffer level(j) + * converging towards target with stable damped evolution with eigenvalues + * real and close to each other around (1 - beta)^(1/3). + * + * Derivation: + * + * The deviation from the buffer level target evolves as + * + * delta(j) = level(j) - target + * delta(j+1) = delta(j) + r(j) - c(j+1) + * + * where r is samples received in one duration, and c corrected rate + * (samples per duration). + * + * The rate correction is in general determined by linear filter f + * + * c(j+1) = c(j) + \sum_{k=0}^\infty delta(j - k) f(k) + * + * If \sum_k f(k) is not zero, the only fixed point is c=r, delta=0, + * so this structure (if the filter is stable) rate matches and + * drives buffer level to target. + * + * The z-transform then is + * + * delta(z) = G(z) r(z) + * c(z) = F(z) delta(z) + * G(z) = (z - 1) / (z - 1)^2 + z f(z) + * F(z) = f(z) / (z - 1) + * + * We now want: poles of G(z) must be in |z|<1 for stability, F(z) + * should damp high frequencies, and f(z) is causal. + * + * To satisfy the conditions, take + * + * (z - 1)^2 + z f(z) = p(z) / q(z) + * + * where p(z) is polynomial with leading term z^n with wanted root + * structure, and q(z) is any polynomial with leading term z^{n-2}. + * This guarantees f(z) is causal, and G(z) = (z-1) q(z) / p(z). + * We can choose p(z) and q(z) to improve low-pass properties of F(z). + * + * Simplest choice is p(z)=(z-x)^2 and q(z)=1, but that gives flat + * high frequency response in F(z). Better choice is p(z) = (z-u)*(z-v)*(z-w) + * and q(z) = z - r. To make F(z) better lowpass, one can cancel + * a resulting 1/z pole in F(z) by setting r=u*v*w. Then, + * + * G(z) = (z - u*v*w)*(z - 1) / (z - u)*(z - v)*(z - w) + * F(z) = (a z + b - a) / (z - 1) * H(z) + * H(z) = beta / (z - 1 + beta) + * beta = 1 - u*v*w + * a = (1-u) + (1-v) + (1-w) - beta / beta + * b = (1-u)*(1-v)*(1-w) / beta + * + * which corresponds to iteration for c(j): + * + * avg(j+1) = (1 - beta) avg(j) + beta delta(j) + * c(j+1) = c(j) + a avg(j+1) - avg(j) + b avg(j) + * + * So the controller operates on the running average, + * which gives the low-pass property for c(j). + * + * The simplest filter is obtained by putting the poles at + * u=v=w=(1-beta)**(1/3). Since beta << 1, computing the root + * can be avoided by expanding in series. + * + * Overshoot in impulse response could be reduced by moving one of the + * poles closer to z=1, but this increases the step response time. + */ +struct spa_bt_rate_control +{ + double avg; + double corr; +}; + +static void spa_bt_rate_control_init(struct spa_bt_rate_control *this, double level) +{ + this->avg = level; + this->corr = 1.0; +} + +static double spa_bt_rate_control_update(struct spa_bt_rate_control *this, double level, + double target, double duration, double period, double rate_diff_max) +{ + /* + * u = (1 - beta)^(1/3) + * x = a / beta + * y = b / beta + * a = (2 + u) * (1 - u)^2 / beta + * b = (1 - u)^3 / beta + * beta -> 0 + */ + const double beta = SPA_CLAMP(duration / period, 0, 0.5); + const double x = 1.0/3; + const double y = beta/27; + double avg; + + avg = beta * level + (1 - beta) * this->avg; + this->corr += x * (avg - this->avg) / period + + y * (this->avg - target) / period; + this->avg = avg; + + this->corr = SPA_CLAMP(this->corr, 1 - rate_diff_max, 1 + rate_diff_max); + + return this->corr; +} + +#endif
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/sco-io.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/sco-io.c
Changed
@@ -89,7 +89,7 @@ int res; read_again: - res = read(io->fd, io->read_buffer, SPA_MIN(io->read_mtu, MAX_MTU)); + res = recv(io->fd, io->read_buffer, SPA_MIN(io->read_mtu, MAX_MTU), MSG_DONTWAIT); if (res <= 0) { if (errno == EINTR) { /* retry if interrupted */ @@ -165,7 +165,7 @@ do { int written; - written = write(io->fd, buf, packet_size); + written = send(io->fd, buf, packet_size, MSG_DONTWAIT | MSG_NOSIGNAL); if (written < 0) { if (errno == EINTR) { /* retry if interrupted */
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/sco-sink.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/sco-sink.c
Changed
@@ -116,6 +116,8 @@ /* Flags */ unsigned int started:1; + unsigned int start_ready:1; + unsigned int transport_started:1; unsigned int following:1; unsigned int flush_pending:1; @@ -360,9 +362,17 @@ return bytes / port->frame_size; } -static void flush_data(struct impl *this) +static int flush_data(struct impl *this) { struct port *port = &this->port; + int processed = 0; + int written; + + spa_assert(this->transport_started); + + if (this->transport == NULL || this->transport->sco_io == NULL || !this->flush_timer_source.loop) + return -EIO; + const uint32_t min_in_size = (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ? MSBC_DECODED_SIZE : this->transport->write_mtu; @@ -372,18 +382,13 @@ const uint32_t packet_samples = min_in_size / port->frame_size; const uint64_t packet_time = (uint64_t)packet_samples * SPA_NSEC_PER_SEC / port->current_format.info.raw.rate; - int processed = 0; - int written; - - if (this->transport == NULL || this->transport->sco_io == NULL) - return; while (!spa_list_is_empty(&port->ready) && port->write_buffer_size < min_in_size) { struct spa_data *datas; /* get buffer */ if (!port->current_buffer) { - spa_return_if_fail(!spa_list_is_empty(&port->ready)); + spa_return_val_if_fail(!spa_list_is_empty(&port->ready), -EIO); port->current_buffer = spa_list_first(&port->ready, struct buffer, link); port->ready_offset = 0; } @@ -418,14 +423,14 @@ if (this->flush_pending) { spa_log_trace(this->log, "%p: wait for flush timer", this); - return; + return 0; } if (port->write_buffer_size < min_in_size) { /* wait for more data */ spa_log_trace(this->log, "%p: skip flush", this); enable_flush_timer(this, false); - return; + return 0; } if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) { @@ -445,7 +450,7 @@ this->buffer_next + 2, MSBC_ENCODED_SIZE - 3, &out_encoded); if (processed < 0) { spa_log_warn(this->log, "sbc_encode failed: %d", processed); - return; + return -EINVAL; } this->buffer_next += out_encoded + 3; port->write_buffer_size = 0; @@ -539,18 +544,19 @@ } enable_flush_timer(this, true); - return; + return 0; stop: - if (this->source.loop) - spa_loop_remove_source(this->data_loop, &this->source); enable_flush_timer(this, false); + if (this->flush_timer_source.loop) + spa_loop_remove_source(this->data_loop, &this->flush_timer_source); + return -EIO; } static void sco_on_flush_timeout(struct spa_source *source) { struct impl *this = source->data; - uint64_t exp; + uint64_t exp = 0; int res; spa_log_trace(this->log, "%p: flush on timeout", this); @@ -582,9 +588,6 @@ uint64_t prev_time, now_time; int res; - if (this->transport == NULL) - return; - if (this->started) { if ((res = spa_system_timerfd_read(this->data_system, this->timerfd, &exp)) < 0) { if (res != -EAGAIN) @@ -600,8 +603,8 @@ now_time, now_time - prev_time); if (SPA_LIKELY(this->position)) { - duration = this->position->clock.duration; - rate = this->position->clock.rate.denom; + duration = this->position->clock.target_duration; + rate = this->position->clock.target_rate.denom; } else { duration = 1024; rate = 48000; @@ -611,7 +614,8 @@ if (SPA_LIKELY(this->clock)) { this->clock->nsec = now_time; - this->clock->position += duration; + this->clock->rate = this->clock->target_rate; + this->clock->position += this->clock->duration; this->clock->duration = duration; this->clock->rate_diff = 1.0f; this->clock->next_nsec = this->next_time; @@ -639,28 +643,22 @@ return (a*b)/gcd(a,b); } -static int do_start(struct impl *this) +static int transport_start(struct impl *this) { - bool do_accept; int res; /* Don't do anything if the node has already started */ - if (this->started) + if (this->transport_started) return 0; + if (!this->start_ready) + return -EIO; /* Make sure the transport is valid */ spa_return_val_if_fail(this->transport != NULL, -EIO); this->following = is_following(this); - spa_log_debug(this->log, "%p: start following:%d", this, this->following); - - /* Do accept if Gateway; otherwise do connect for Head Unit */ - do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY; - - /* acquire the socket fd (false -> connect | true -> accept) */ - if ((res = spa_bt_transport_acquire(this->transport, do_accept)) < 0) - return res; + spa_log_debug(this->log, "%p: start transport", this); /* Init mSBC if needed */ if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) { @@ -689,14 +687,6 @@ if ((res = spa_bt_transport_ensure_sco_io(this->transport, this->data_loop)) < 0) goto fail; - /* Add the timeout callback */ - this->source.data = this; - this->source.fd = this->timerfd; - this->source.func = sco_on_timeout; - this->source.mask = SPA_IO_IN; - 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 = sco_on_flush_timeout; @@ -706,20 +696,58 @@ /* start processing */ this->flush_pending = false; - set_timers(this); /* Set the started flag */ - this->started = true; + this->transport_started = true; return 0; fail: free(this->buffer); this->buffer = NULL; - spa_bt_transport_release(this->transport); return res; } +static int do_start(struct impl *this) +{ + bool do_accept;
View file
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/sco-source.c -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/sco-source.c
Changed
@@ -113,9 +113,12 @@ struct port port; unsigned int started:1; + unsigned int start_ready:1; + unsigned int transport_started:1; unsigned int following:1; unsigned int matching:1; unsigned int resampling:1; + unsigned int io_error:1; struct spa_source timer_source; int timerfd; @@ -230,7 +233,8 @@ struct port *port = &this->port; set_timers(this); - spa_bt_decode_buffer_recover(&port->buffer); + if (this->transport_started) + spa_bt_decode_buffer_recover(&port->buffer); return 0; } @@ -495,6 +499,10 @@ uint32_t decoded; uint64_t dt; + /* Drop data when not started */ + if (!this->started) + return 0; + if (this->transport == NULL) { spa_log_debug(this->log, "no transport, stop reading"); goto stop; @@ -548,6 +556,7 @@ return 0; stop: + this->io_error = true; return 1; } @@ -555,12 +564,15 @@ { struct port *port = &this->port; + if (!this->transport_started) + port->buffer.corr = 1.0; + if (this->position && port->rate_match) { port->rate_match->rate = 1 / port->buffer.corr; this->matching = this->following; this->resampling = this->matching || - (port->current_format.info.raw.rate != this->position->clock.rate.denom); + (port->current_format.info.raw.rate != this->position->clock.target_rate.denom); } else { this->matching = false; this->resampling = false; @@ -583,9 +595,6 @@ uint64_t prev_time, now_time; int res; - if (this->transport == NULL) - return; - if (this->started) { if ((res = spa_system_timerfd_read(this->data_system, this->timerfd, &exp)) < 0) { if (res != -EAGAIN) @@ -602,8 +611,8 @@ now_time, now_time - prev_time); if (SPA_LIKELY(this->position)) { - duration = this->position->clock.duration; - rate = this->position->clock.rate.denom; + duration = this->position->clock.target_duration; + rate = this->position->clock.target_rate.denom; } else { duration = 1024; rate = 48000; @@ -615,7 +624,8 @@ if (SPA_LIKELY(this->clock)) { this->clock->nsec = now_time; - this->clock->position += duration; + this->clock->rate = this->clock->target_rate; + this->clock->position += this->clock->duration; this->clock->duration = duration; this->clock->rate_diff = port->buffer.corr; this->clock->next_nsec = this->next_time; @@ -646,31 +656,22 @@ return 0; } -static int do_start(struct impl *this) +static int transport_start(struct impl *this) { struct port *port = &this->port; - bool do_accept; int res; /* Don't do anything if the node has already started */ - if (this->started) + if (this->transport_started) return 0; + if (!this->start_ready) + return -EIO; - this->following = is_following(this); - - spa_log_debug(this->log, "%p: start following:%d", - this, this->following); + spa_log_debug(this->log, "%p: start transport", this); /* Make sure the transport is valid */ spa_return_val_if_fail (this->transport != NULL, -EIO); - /* Do accept if Gateway; otherwise do connect for Head Unit */ - do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY; - - /* acquire the socket fd (false -> connect | true -> accept) */ - if ((res = spa_bt_transport_acquire(this->transport, do_accept)) < 0) - return res; - /* Reset the buffers and sample count */ reset_buffers(port); @@ -694,11 +695,47 @@ this->msbc_buffer_pos = 0; } + this->io_error = false; + /* Start socket i/o */ if ((res = spa_bt_transport_ensure_sco_io(this->transport, this->data_loop)) < 0) goto fail; spa_loop_invoke(this->data_loop, do_add_source, 0, NULL, 0, true, this); + /* Set the started flag */ + this->transport_started = true; + + return 0; + +fail: + return res; +} + +static int do_start(struct impl *this) +{ + bool do_accept; + int res; + + if (this->started) + return 0; + + spa_return_val_if_fail(this->transport, -EIO); + + this->following = is_following(this); + + this->start_ready = true; + + spa_log_debug(this->log, "%p: start following:%d", this, this->following); + + /* Do accept if Gateway; otherwise do connect for Head Unit */ + do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY; + + /* acquire the socket fd (false -> connect | true -> accept) */ + if ((res = spa_bt_transport_acquire(this->transport, do_accept)) < 0) { + this->start_ready = false; + return res; + } + /* Start timer */ this->timer_source.data = this; this->timer_source.fd = this->timerfd; @@ -708,16 +745,12 @@ spa_loop_add_source(this->data_loop, &this->timer_source); setup_matching(this); + set_timers(this); - /* Set the started flag */ this->started = true; return 0; - -fail: - spa_bt_transport_release(this->transport); - return res; } static int do_remove_source(struct spa_loop *loop, @@ -728,42 +761,66 @@ void *user_data) { struct impl *this = user_data; - struct itimerspec ts; - - if (this->transport && this->transport->sco_io)
View file
pipewire-0.3.67.tar.gz/spa/plugins/libcamera/libcamera-utils.cpp -> pipewire-0.3.68.tar.gz/spa/plugins/libcamera/libcamera-utils.cpp
Changed
@@ -885,9 +885,12 @@ } const FrameMetadata &fmd = buffer->metadata(); - - if (impl->clock) { + /* FIXME, we should follow the driver clock and target_ values. + * for now we ignore and use our own. */ + impl->clock->target_rate = port->rate; + impl->clock->target_duration = 1; + impl->clock->nsec = fmd.timestamp; impl->clock->rate = port->rate; impl->clock->position = fmd.sequence;
View file
pipewire-0.3.67.tar.gz/spa/plugins/meson.build -> pipewire-0.3.68.tar.gz/spa/plugins/meson.build
Changed
@@ -1,4 +1,4 @@ -if alsa_dep.found() +if alsa_dep.found() and host_machine.system() == 'linux' subdir('alsa') endif if get_option('avb').require(host_machine.system() == 'linux', error_message: 'AVB support is only available on Linux').allowed()
View file
pipewire-0.3.67.tar.gz/spa/plugins/support/loop.c -> pipewire-0.3.68.tar.gz/spa/plugins/support/loop.c
Changed
@@ -333,7 +333,7 @@ impl->enter_count = 1; } else { spa_return_if_fail(impl->enter_count > 0); - spa_return_if_fail(impl->thread == thread_id); + spa_return_if_fail(pthread_equal(impl->thread, thread_id)); impl->enter_count++; } spa_log_trace(impl->log, "%p: enter %p", impl, (void *) impl->thread); @@ -345,7 +345,7 @@ pthread_t thread_id = pthread_self(); spa_return_if_fail(impl->enter_count > 0); - spa_return_if_fail(impl->thread == thread_id); + spa_return_if_fail(pthread_equal(impl->thread, thread_id)); spa_log_trace(impl->log, "%p: leave %p", impl, (void *) impl->thread); @@ -356,6 +356,13 @@ } } +static int loop_check(void *object) +{ + struct impl *impl = object; + pthread_t thread_id = pthread_self(); + return (impl->thread == 0 || pthread_equal(impl->thread, thread_id)) ? 1 : 0; +} + static inline void free_source(struct source_impl *s) { detach_source(&s->source); @@ -826,6 +833,7 @@ .enter = loop_enter, .leave = loop_leave, .iterate = loop_iterate, + .check = loop_check, }; static const struct spa_loop_utils_methods impl_loop_utils = {
View file
pipewire-0.3.67.tar.gz/spa/plugins/support/node-driver.c -> pipewire-0.3.68.tar.gz/spa/plugins/support/node-driver.c
Changed
@@ -88,9 +88,13 @@ clockid_t id; } clock_info = { { "realtime", CLOCK_REALTIME }, +#ifdef CLOCK_TAI { "tai", CLOCK_TAI }, +#endif { "monotonic", CLOCK_MONOTONIC }, +#ifdef CLOCK_MONOTONIC_RAW { "monotonic-raw", CLOCK_MONOTONIC_RAW }, +#endif { "boottime", CLOCK_BOOTTIME }, }; @@ -145,7 +149,7 @@ spa_log_debug(this->log, "%p now:%"PRIu64, this, this->next_time); - if (this->following) { + if (this->following || !this->started) { set_timeout(this, 0); } else { set_timeout(this, this->next_time); @@ -158,7 +162,7 @@ return this->position && this->clock && this->position->clock.id != this->clock->id; } -static int do_reassign_follower(struct spa_loop *loop, +static int do_set_timers(struct spa_loop *loop, bool async, uint32_t seq, const void *data, @@ -185,7 +189,7 @@ if (following != this->following) { spa_log_debug(this->log, NAME" %p: reassign follower %d->%d", this, this->following, following); this->following = following; - spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this); + spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this); } return 0; } @@ -243,8 +247,8 @@ return; } if (SPA_LIKELY(this->position)) { - duration = this->position->clock.duration; - rate = this->position->clock.rate.denom; + duration = this->position->clock.target_duration; + rate = this->position->clock.target_rate.denom; } else { duration = 1024; rate = 48000; @@ -259,15 +263,14 @@ current_position = scale_u64(current_time, rate, SPA_NSEC_PER_SEC); - if (SPA_LIKELY(this->clock)) - position = this->clock->position; - else - position = current_position; - if (this->last_time == 0) { spa_dll_set_bw(&this->dll, SPA_DLL_BW_MIN, duration, rate); this->max_error = rate * MAX_ERROR_MS / 1000; position = current_position; + } else if (SPA_LIKELY(this->clock)) { + position = this->clock->position + this->clock->duration; + } else { + position = current_position; } /* check the elapsed time of the other clock against @@ -279,7 +282,6 @@ else if (err < -this->max_error) err = -this->max_error; - position += duration; this->last_time = current_time; if (this->tracking) { @@ -287,7 +289,7 @@ this->next_time = nsec + duration / corr * 1e9 / rate; } else { corr = 1.0; - this->next_time = scale_u64(position, SPA_NSEC_PER_SEC, rate); + this->next_time = scale_u64(position + duration, SPA_NSEC_PER_SEC, rate); } if (SPA_UNLIKELY((this->next_time - this->base_time) > BW_PERIOD)) { @@ -300,6 +302,7 @@ if (SPA_LIKELY(this->clock)) { this->clock->nsec = nsec; + this->clock->rate = this->clock->target_rate; this->clock->position = position; this->clock->duration = duration; this->clock->delay = 0; @@ -319,9 +322,9 @@ return 0; this->following = is_following(this); - set_timers(this); this->started = true; this->last_time = 0; + spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this); return 0; } @@ -330,7 +333,7 @@ if (!this->started) return 0; this->started = false; - set_timeout(this, 0); + spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this); return 0; }
View file
pipewire-0.3.67.tar.gz/spa/plugins/support/null-audio-sink.c -> pipewire-0.3.68.tar.gz/spa/plugins/support/null-audio-sink.c
Changed
@@ -191,7 +191,7 @@ return res; this->next_time = SPA_TIMESPEC_TO_NSEC(&now); - if (this->following) { + if (this->following || !this->started) { set_timeout(this, 0); } else { set_timeout(this, this->next_time); @@ -204,7 +204,7 @@ return this->position && this->clock && this->position->clock.id != this->clock->id; } -static int do_reassign_follower(struct spa_loop *loop, +static int do_set_timers(struct spa_loop *loop, bool async, uint32_t seq, const void *data, @@ -227,7 +227,7 @@ if (following != this->following) { spa_log_debug(this->log, NAME" %p: reassign follower %d->%d", this, this->following, following); this->following = following; - spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this); + spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this); } return 0; } @@ -280,8 +280,8 @@ nsec = this->next_time; if (SPA_LIKELY(this->position)) { - duration = this->position->clock.duration; - rate = this->position->clock.rate.denom; + duration = this->position->clock.target_duration; + rate = this->position->clock.target_rate.denom; } else { duration = 1024; rate = 48000; @@ -291,7 +291,8 @@ if (SPA_LIKELY(this->clock)) { this->clock->nsec = nsec; - this->clock->position += duration; + this->clock->rate = this->clock->target_rate; + this->clock->position += this->clock->duration; this->clock->duration = duration; this->clock->delay = 0; this->clock->rate_diff = 1.0; @@ -309,8 +310,8 @@ return 0; this->following = is_following(this); - set_timers(this); this->started = true; + spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this); return 0; } @@ -319,7 +320,7 @@ if (!this->started) return 0; this->started = false; - set_timeout(this, 0); + spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this); return 0; }
View file
pipewire-0.3.67.tar.gz/spa/plugins/v4l2/v4l2-utils.c -> pipewire-0.3.68.tar.gz/spa/plugins/v4l2/v4l2-utils.c
Changed
@@ -1289,6 +1289,11 @@ spa_log_trace(this->log, "v4l2 %p: have output %d", this, buf.index); if (this->clock) { + /* FIXME, we should follow the driver clock and target_ values. + * for now we ignore and use our own. */ + this->clock->target_rate = port->rate; + this->clock->target_duration = 1; + this->clock->nsec = pts; this->clock->rate = port->rate; this->clock->position = buf.sequence;
View file
pipewire-0.3.67.tar.gz/spa/plugins/volume/volume.c -> pipewire-0.3.68.tar.gz/spa/plugins/volume/volume.c
Changed
@@ -525,7 +525,7 @@ b->flags = direction == SPA_DIRECTION_INPUT ? BUFFER_FLAG_OUT : 0; b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h)); - if (d0.data == NULL) { + if (d0.data != NULL) { b->ptr = d0.data; b->size = d0.maxsize; } else {
View file
pipewire-0.3.68.tar.gz/src/daemon/client-rt.conf.avail
Added
+(directory)
View file
pipewire-0.3.68.tar.gz/src/daemon/client-rt.conf.avail/20-upmix.conf.in
Added
@@ -0,0 +1,8 @@ +# Enables upmixing +stream.properties = { + channelmix.upmix = true + channelmix.upmix-method = psd # none, simple + channelmix.lfe-cutoff = 150 + channelmix.fc-cutoff = 12000 + channelmix.rear-delay = 12.0 +}
View file
pipewire-0.3.68.tar.gz/src/daemon/client-rt.conf.avail/meson.build
Added
@@ -0,0 +1,11 @@ +conf_files = + '20-upmix.conf', + + +foreach c : conf_files + configure_file(input : '@0@.in'.format(c), + output : c, + configuration : conf_config, + install_dir : pipewire_confdatadir / 'client-rt.conf.avail') +endforeach +
View file
pipewire-0.3.68.tar.gz/src/daemon/client.conf.avail
Added
+(directory)
View file
pipewire-0.3.68.tar.gz/src/daemon/client.conf.avail/20-upmix.conf.in
Added
@@ -0,0 +1,8 @@ +# Enables upmixing +stream.properties = { + channelmix.upmix = true + channelmix.upmix-method = psd # none, simple + channelmix.lfe-cutoff = 150 + channelmix.fc-cutoff = 12000 + channelmix.rear-delay = 12.0 +}
View file
pipewire-0.3.68.tar.gz/src/daemon/client.conf.avail/meson.build
Added
@@ -0,0 +1,11 @@ +conf_files = + '20-upmix.conf', + + +foreach c : conf_files + configure_file(input : '@0@.in'.format(c), + output : c, + configuration : conf_config, + install_dir : pipewire_confdatadir / 'client.conf.avail') +endforeach +
View file
pipewire-0.3.67.tar.gz/src/daemon/meson.build -> pipewire-0.3.68.tar.gz/src/daemon/meson.build
Changed
@@ -86,31 +86,18 @@ output : 'pipewire-uninstalled.conf', configuration : conf_config_uninstalled) -pipewire_exec = executable('pipewire', - pipewire_daemon_sources, - install: true, - c_args : pipewire_c_args, - include_directories : configinc , - dependencies : spa_dep, pipewire_dep, , -) - -executable('pipewire-pulse', - pipewire_daemon_sources, - install: true, - c_args : pipewire_c_args, - include_directories : configinc , - dependencies : spa_dep, pipewire_dep, , -) +conf_avail_folders = + 'pipewire.conf.avail', + 'client.conf.avail', + 'client-rt.conf.avail', + 'pipewire-pulse.conf.avail', + -executable('pipewire-avb', - pipewire_daemon_sources, - install: true, - c_args : pipewire_c_args, - include_directories : configinc , - dependencies : spa_dep, pipewire_dep, , -) +foreach c : conf_avail_folders + subdir(c) +endforeach -executable('pipewire-aes67', +pipewire_exec = executable('pipewire', pipewire_daemon_sources, install: true, c_args : pipewire_c_args, @@ -120,6 +107,22 @@ ln = find_program('ln') +foreach alias : 'pipewire-pulse', 'pipewire-avb', 'pipewire-aes67' + custom_target( + alias, + build_by_default: true, + install: false, + command: ln, '-sf', meson.project_build_root() + '/@INPUT@', '@OUTPUT@', + input: pipewire_exec, + output: alias, + ) + install_symlink( + alias, + pointing_to: pipewire_exec.name(), + install_dir: pipewire_bindir, + ) +endforeach + custom_target('pipewire-uninstalled', build_by_default: true, install: false,
View file
pipewire-0.3.67.tar.gz/src/daemon/minimal.conf.in -> pipewire-0.3.68.tar.gz/src/daemon/minimal.conf.in
Changed
@@ -326,7 +326,6 @@ # link.output.port = capture_1 # link.input.node = my-mic # link.input.port = input_FL - # link.passive = true # } #} #{ factory = link-factory @@ -335,7 +334,6 @@ # link.output.port = capture_2 # link.input.node = my-mic # link.input.port = input_FR - # link.passive = true # } #}
View file
pipewire-0.3.68.tar.gz/src/daemon/pipewire-pulse.conf.avail
Added
+(directory)
View file
pipewire-0.3.68.tar.gz/src/daemon/pipewire-pulse.conf.avail/20-upmix.conf.in
Added
@@ -0,0 +1,8 @@ +# Enables upmixing +stream.properties = { + channelmix.upmix = true + channelmix.upmix-method = psd # none, simple + channelmix.lfe-cutoff = 150 + channelmix.fc-cutoff = 12000 + channelmix.rear-delay = 12.0 +}
View file
pipewire-0.3.68.tar.gz/src/daemon/pipewire-pulse.conf.avail/meson.build
Added
@@ -0,0 +1,11 @@ +conf_files = + '20-upmix.conf', + + +foreach c : conf_files + configure_file(input : '@0@.in'.format(c), + output : c, + configuration : conf_config, + install_dir : pipewire_confdatadir / 'pipewire-pulse.conf.avail') +endforeach +
View file
pipewire-0.3.68.tar.gz/src/daemon/pipewire.conf.avail
Added
+(directory)
View file
pipewire-0.3.68.tar.gz/src/daemon/pipewire.conf.avail/10-rates.conf.in
Added
@@ -0,0 +1,4 @@ +# Adds more common rates +context.properties = { + default.clock.allowed-rates = 44100 48000 88200 96000 +}
View file
pipewire-0.3.68.tar.gz/src/daemon/pipewire.conf.avail/20-upmix.conf.in
Added
@@ -0,0 +1,8 @@ +# Enables upmixing +stream.properties = { + channelmix.upmix = true + channelmix.upmix-method = psd # none, simple + channelmix.lfe-cutoff = 150 + channelmix.fc-cutoff = 12000 + channelmix.rear-delay = 12.0 +}
View file
pipewire-0.3.68.tar.gz/src/daemon/pipewire.conf.avail/meson.build
Added
@@ -0,0 +1,12 @@ +conf_files = + '10-rates.conf', + '20-upmix.conf', + + +foreach c : conf_files + configure_file(input : '@0@.in'.format(c), + output : c, + configuration : conf_config, + install_dir : pipewire_confdatadir / 'pipewire.conf.avail') +endforeach +
View file
pipewire-0.3.67.tar.gz/src/examples/export-spa.c -> pipewire-0.3.68.tar.gz/src/examples/export-spa.c
Changed
@@ -36,7 +36,7 @@ uint32_t id; }; -static void proxy_event_bound(void *_data, uint32_t global_id) +static void proxy_event_bound_props(void *_data, uint32_t global_id, const struct spa_dict *props) { struct data *data = _data; if (data->id != global_id) { @@ -47,7 +47,7 @@ static const struct pw_proxy_events proxy_events = { PW_VERSION_PROXY_EVENTS, - .bound = proxy_event_bound, + .bound_props = proxy_event_bound_props, }; static int make_node(struct data *data)
View file
pipewire-0.3.67.tar.gz/src/gst/gstpipewiredeviceprovider.c -> pipewire-0.3.68.tar.gz/src/gst/gstpipewiredeviceprovider.c
Changed
@@ -210,6 +210,7 @@ const struct pw_node_info *info = data->info; const gchar *element = NULL; GstPipeWireDevice *gstdev; + int priority = 0; if (info->max_input_ports > 0 && info->max_output_ports == 0) { type = GST_PIPEWIRE_DEVICE_TYPE_SINK; @@ -224,11 +225,16 @@ props = gst_structure_new_empty ("pipewire-proplist"); if (info->props) { const struct spa_dict_item *item; + const char *str; + spa_dict_for_each (item, info->props) gst_structure_set (props, item->key, G_TYPE_STRING, item->value, NULL); klass = spa_dict_lookup (info->props, PW_KEY_MEDIA_CLASS); name = spa_dict_lookup (info->props, PW_KEY_NODE_DESCRIPTION); + + if ((str = spa_dict_lookup(info->props, PW_KEY_PRIORITY_SESSION))) + priority = atoi(str); } if (klass == NULL) klass = "unknown/unknown"; @@ -244,26 +250,56 @@ gstdev->serial = data->serial; gstdev->type = type; gstdev->element = element; + gstdev->priority = priority; if (props) gst_structure_free (props); return GST_DEVICE (gstdev); } +static int +compare_device_session_priority (const void *a, + const void *b) +{ + const GstPipeWireDevice *dev_a = a; + const GstPipeWireDevice *dev_b = b; + + if (dev_a->priority < dev_b->priority) + return 1; + else if (dev_a->priority > dev_b->priority) + return -1; + else + return 0; +} + static void do_add_nodes(GstPipeWireDeviceProvider *self) { struct node_data *nd; + GList *new_devices = NULL; + GList *l; spa_list_for_each(nd, &self->nodes, link) { if (nd->dev != NULL) continue; pw_log_info("add node %d", nd->id); nd->dev = new_node (self, nd); - if (nd->dev) { - if(self->list_only) - self->devices = g_list_prepend (self->devices, gst_object_ref_sink (nd->dev)); - else - gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), nd->dev); + if (nd->dev) + new_devices = g_list_prepend (new_devices, nd->dev); + } + if (!new_devices) + return; + + new_devices = g_list_sort (new_devices, + compare_device_session_priority); + for (l = new_devices; l != NULL; l = l->next) { + GstDevice *device = l->data; + + if(self->list_only) { + self->devices = g_list_insert_sorted (self->devices, + gst_object_ref_sink (device), + compare_device_session_priority); + } else { + gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), device); } } }
View file
pipewire-0.3.67.tar.gz/src/gst/gstpipewiredeviceprovider.h -> pipewire-0.3.68.tar.gz/src/gst/gstpipewiredeviceprovider.h
Changed
@@ -39,6 +39,7 @@ uint64_t serial; int fd; const gchar *element; + int priority; }; struct _GstPipeWireDeviceClass {
View file
pipewire-0.3.67.tar.gz/src/modules/meson.build -> pipewire-0.3.68.tar.gz/src/modules/meson.build
Changed
@@ -28,6 +28,8 @@ 'module-rt.c', 'module-raop-discover.c', 'module-raop-sink.c', + 'module-rtp-sap.c', + 'module-rtp-session.c', 'module-rtp-source.c', 'module-rtp-sink.c', 'module-session-manager.c', @@ -153,7 +155,7 @@ install : true, install_dir : modules_install_dir, install_rpath: modules_install_dir, - dependencies : mathlib, dl_lib, pipewire_dep, + dependencies : mathlib, dl_lib, pipewire_dep, audioconvert_dep, ) pipewire_module_profiler = shared_library('pipewire-module-profiler', @@ -519,16 +521,41 @@ summary({'ROC': roc_dep.found()}, bool_yn: true, section: 'Streaming between daemons') pipewire_module_rtp_source = shared_library('pipewire-module-rtp-source', - 'module-rtp-source.c' , + 'module-rtp-source.c', + 'module-rtp/stream.c' , include_directories : configinc, install : true, install_dir : modules_install_dir, install_rpath: modules_install_dir, - dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, + dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep, ) pipewire_module_rtp_sink = shared_library('pipewire-module-rtp-sink', - 'module-rtp-sink.c' , + 'module-rtp-sink.c', + 'module-rtp/stream.c' , + include_directories : configinc, + install : true, + install_dir : modules_install_dir, + install_rpath: modules_install_dir, + dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep, +) + +build_module_rtp_session = avahi_dep.found() +if build_module_rtp_session + pipewire_module_rtp_session = shared_library('pipewire-module-rtp-session', + 'module-rtp/stream.c', + 'module-zeroconf-discover/avahi-poll.c', + 'module-rtp-session.c' , + include_directories : configinc, + install : true, + install_dir : modules_install_dir, + install_rpath: modules_install_dir, + dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep, opus_dep, + ) +endif + +pipewire_module_rtp_sap = shared_library('pipewire-module-rtp-sap', + 'module-rtp-sap.c' , include_directories : configinc, install : true, install_dir : modules_install_dir,
View file
pipewire-0.3.67.tar.gz/src/modules/module-access.c -> pipewire-0.3.68.tar.gz/src/modules/module-access.c
Changed
@@ -111,10 +111,10 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic -#define MODULE_USAGE " access.force=flatpak " \ - " access.allowed=<cmd-line> " \ - " access.rejected=<cmd-line> " \ - " access.restricted=<cmd-line> " \ +#define MODULE_USAGE "( access.force=flatpak ) " \ + "( access.allowed= <cmd-line>,.. ) " \ + "( access.rejected= <cmd-line>,.. ) " \ + "( access.restricted= <cmd-line>,.. ) " \ static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
View file
pipewire-0.3.67.tar.gz/src/modules/module-adapter.c -> pipewire-0.3.68.tar.gz/src/modules/module-adapter.c
Changed
@@ -26,7 +26,7 @@ #define PW_LOG_TOPIC_DEFAULT mod_topic #define FACTORY_USAGE SPA_KEY_FACTORY_NAME"=<factory-name> " \ - ""SPA_KEY_LIBRARY_NAME"=<library-name> " \ + "("SPA_KEY_LIBRARY_NAME"=<library-name>) " \ ADAPTER_USAGE static const struct spa_dict_item module_props = { @@ -203,7 +203,7 @@ handle = pw_context_load_spa_handle(d->context, factory_name, - properties ? &properties->dict : NULL); + &properties->dict); if (handle == NULL) goto error_errno;
View file
pipewire-0.3.67.tar.gz/src/modules/module-avb/avdecc.c -> pipewire-0.3.68.tar.gz/src/modules/module-avb/avdecc.c
Changed
@@ -240,6 +240,7 @@ struct server *avdecc_server_new(struct impl *impl, struct spa_dict *props) { struct server *server; + const char *str; int res = 0; server = calloc(1, sizeof(*server)); @@ -248,7 +249,8 @@ server->impl = impl; spa_list_append(&impl->servers, &server->link); - server->ifname = strdup(spa_dict_lookup(props, "ifname")); + str = spa_dict_lookup(props, "ifname"); + server->ifname = str ? strdup(str) : NULL; spa_hook_list_init(&server->listener_list); spa_list_init(&server->descriptors); spa_list_init(&server->streams); @@ -309,7 +311,7 @@ if (server->source) pw_loop_destroy_source(impl->loop, server->source); if (server->timer) - pw_loop_destroy_source(impl->loop, server->source); + pw_loop_destroy_source(impl->loop, server->timer); spa_hook_list_clean(&server->listener_list); free(server); }
View file
pipewire-0.3.67.tar.gz/src/modules/module-client-node/remote-node.c -> pipewire-0.3.68.tar.gz/src/modules/module-client-node/remote-node.c
Changed
@@ -1143,18 +1143,20 @@ client_node_removed(_data); } -static void client_node_bound(void *_data, uint32_t global_id) +static void client_node_bound_props(void *_data, uint32_t global_id, const struct spa_dict *props) { struct node_data *data = _data; pw_log_debug("%p: bound %u", data, global_id); data->remote_id = global_id; + if (props) + pw_properties_update(data->node->properties, props); } static const struct pw_proxy_events proxy_client_node_events = { PW_VERSION_PROXY_EVENTS, .removed = client_node_removed, .destroy = client_node_destroy, - .bound = client_node_bound, + .bound_props = client_node_bound_props, }; static int node_ready(void *d, int status)
View file
pipewire-0.3.67.tar.gz/src/modules/module-combine-stream.c -> pipewire-0.3.68.tar.gz/src/modules/module-combine-stream.c
Changed
@@ -35,6 +35,10 @@ * - a new virtual sink that forwards audio to other sinks * - a new virtual source that combines audio from other sources * + * The sources and sink that need to be combined can be selected using generic match + * rules. This makes it possible to combine static nodes or nodes based on certain + * properties. + * * ## Module Options * * - `node.name`: a unique name for the stream @@ -42,6 +46,7 @@ * - `combine.mode` = capture | playback | sink | source, default sink * - `combine.props = {}`: properties to be passed to the sink/source * - `stream.props = {}`: properties to be passed to the streams + * - `stream.rules = {}`: rules for matching streams, use create-stream actions * * ## General options * @@ -198,15 +203,15 @@ #define DEFAULT_CHANNELS 2 #define DEFAULT_POSITION " FL FR " -#define MODULE_USAGE " node.latency=<latency as fraction> " \ - " combine.mode=<mode of stream, playback|capture|sink|source>, default:sink " \ - " node.name=<name of the stream> " \ - " node.description=<description of the stream> " \ - " audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) "> " \ - " audio.position=<channel map, default:"DEFAULT_POSITION"> " \ - " combine.props=<properties> " \ - " stream.props=<properties> " \ - " stream.rules=<properties> " +#define MODULE_USAGE "( node.latency=<latency as fraction> ) " \ + "( combine.mode=<mode of stream, playback|capture|sink|source>, default:sink ) " \ + "( node.name=<name of the stream> ) " \ + "( node.description=<description of the stream> ) " \ + "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) "> ) " \ + "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) " \ + "( combine.props=<properties> ) " \ + "( stream.props=<properties> ) " \ + "( stream.rules=<properties> ) " static const struct spa_dict_item module_props = { @@ -576,7 +581,7 @@ if (impl->mode == MODE_CAPTURE || impl->mode == MODE_SINK) str = " { matches = { media.class = \"Audio/Sink\" } " " actions = { create-stream = {} } } "; - else if (impl->mode == MODE_PLAYBACK || impl->mode == MODE_SOURCE) + else str = " { matches = { media.class = \"Audio/Source\" } " " actions = { create-stream = {} } } "; }
View file
pipewire-0.3.67.tar.gz/src/modules/module-echo-cancel.c -> pipewire-0.3.68.tar.gz/src/modules/module-echo-cancel.c
Changed
@@ -33,6 +33,8 @@ #include <spa/support/plugin-loader.h> #include <spa/interfaces/audio/aec.h> +#include <spa/plugins/audioconvert/wavfile.h> + #include <pipewire/impl.h> #include <pipewire/pipewire.h> @@ -155,19 +157,19 @@ static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, { PW_KEY_MODULE_DESCRIPTION, "Echo Cancellation" }, - { PW_KEY_MODULE_USAGE, " remote.name=<remote> " - " node.latency=<latency as fraction> " - " audio.rate=<sample rate> " - " audio.channels=<number of channels> " - " audio.position=<channel map> " - " buffer.max_size=<max buffer size in ms> " - " buffer.play_delay=<delay as fraction> " - " library.name =<library name> " - " aec.args=<aec arguments> " - " capture.props=<properties> " - " source.props=<properties> " - " sink.props=<properties> " - " playback.props=<properties> " }, + { PW_KEY_MODULE_USAGE, " ( remote.name=<remote> ) " + "( node.latency=<latency as fraction> ) " + "( audio.rate=<sample rate> ) " + "( audio.channels=<number of channels> ) " + "( audio.position=<channel map> ) " + "( buffer.max_size=<max buffer size in ms> ) " + "( buffer.play_delay=<delay as fraction> ) " + "( library.name =<library name> ) " + "( aec.args=<aec arguments> ) " + "( capture.props=<properties> ) " + "( source.props=<properties> ) " + "( sink.props=<properties> ) " + "( playback.props=<properties> ) " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; @@ -231,8 +233,50 @@ struct spa_plugin_loader *loader; bool monitor_mode; + + char wav_path512; + struct wav_file *wav_file; }; +static inline void aec_run(struct impl *impl, const float *rec, const float *play, + float *out, uint32_t n_samples) +{ + spa_audio_aec_run(impl->aec, rec, play, out, n_samples); + + if (SPA_UNLIKELY(impl->wav_path0)) { + if (impl->wav_file == NULL) { + struct wav_file_info info; + + info.info.info.raw = impl->info; + info.info.info.raw.channels *= 3; + info.info.media_type = SPA_MEDIA_TYPE_audio; + info.info.media_subtype = SPA_MEDIA_SUBTYPE_raw; + + impl->wav_file = wav_file_open(impl->wav_path, + "w", &info); + if (impl->wav_file == NULL) + pw_log_warn("can't open wav path '%s': %m", + impl->wav_path); + } + if (impl->wav_file) { + uint32_t i, c = impl->info.channels; + const float *datac * 3; + + for (i = 0; i < c; i++) { + datai = playi; + datai + c = reci; + datai + 2*c = outi; + } + wav_file_write(impl->wav_file, (void*)data, n_samples); + } else { + spa_zero(impl->wav_path); + } + } else if (impl->wav_file != NULL) { + wav_file_close(impl->wav_file); + impl->wav_file = NULL; + } +} + static void process(struct impl *impl) { struct pw_buffer *cout; @@ -326,11 +370,11 @@ pdi = play_delayedi + delay_left; oi = outi + delay_left; } - spa_audio_aec_run(impl->aec, rec, pd, o, size / sizeof(float) - delay_left); + aec_run(impl, rec, pd, o, size / sizeof(float) - delay_left); } } else { /* run the canceller */ - spa_audio_aec_run(impl->aec, rec, play_delayed, out, size / sizeof(float)); + aec_run(impl, rec, play_delayed, out, size / sizeof(float)); } /* Next, copy over the output to the output ringbuffer */ @@ -520,6 +564,27 @@ } } +static void reset_buffers(struct impl *impl) +{ + uint32_t index, i; + + spa_ringbuffer_init(&impl->rec_ring); + spa_ringbuffer_init(&impl->play_ring); + spa_ringbuffer_init(&impl->play_delayed_ring); + spa_ringbuffer_init(&impl->out_ring); + + for (i = 0; i < impl->info.channels; i++) { + memset(impl->rec_bufferi, 0, impl->rec_ringsize); + memset(impl->play_bufferi, 0, impl->play_ringsize); + memset(impl->out_bufferi, 0, impl->out_ringsize); + } + + spa_ringbuffer_get_write_index(&impl->play_ring, &index); + 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))); +} + static void input_param_latency_changed(struct impl *impl, const struct spa_pod *param) { struct spa_latency_info latency; @@ -538,21 +603,64 @@ else pw_stream_update_params(impl->capture, params, 1); } + static struct spa_pod* get_props_param(struct impl* impl, struct spa_pod_builder* b) { - if (spa_audio_aec_get_params(impl->aec, NULL) > 0) { - struct spa_pod_frame f2; - spa_pod_builder_push_object( - b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); - spa_pod_builder_prop(b, SPA_PROP_params, 0); - spa_pod_builder_push_struct(b, &f1); + struct spa_pod_frame f2; + spa_pod_builder_push_object( + b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); + spa_pod_builder_prop(b, SPA_PROP_params, 0); + spa_pod_builder_push_struct(b, &f1); + + spa_pod_builder_string(b, "debug.aec.wav-path"); + spa_pod_builder_string(b, impl->wav_path); + + if (spa_audio_aec_get_params(impl->aec, NULL) > 0) spa_audio_aec_get_params(impl->aec, b); - spa_pod_builder_pop(b, &f1); - return spa_pod_builder_pop(b, &f0); + spa_pod_builder_pop(b, &f1); + return spa_pod_builder_pop(b, &f0); +} + +static int set_params(struct impl* impl, const struct spa_pod *params) +{ + struct spa_pod_parser prs; + struct spa_pod_frame f; + int changed = 0; + + spa_pod_parser_pod(&prs, params); + if (spa_pod_parser_push_struct(&prs, &f) < 0) + return 0; + + while (true) { + const char *name; + struct spa_pod *pod; + char value512; + + if (spa_pod_parser_get_string(&prs, &name) < 0) + break; + + if (spa_pod_parser_get_pod(&prs, &pod) < 0) + break; + + if (spa_pod_is_string(pod)) { + spa_pod_copy_string(pod, sizeof(value), value); + } else if (spa_pod_is_none(pod)) { + spa_zero(value); + } else + continue; + + pw_log_info("key:'%s' val:'%s'", name, value); + + if (spa_streq(name, "debug.aec.wav-path")) { + spa_scnprintf(impl->wav_path, + sizeof(impl->wav_path), "%s", value);
View file
pipewire-0.3.67.tar.gz/src/modules/module-example-sink.c -> pipewire-0.3.68.tar.gz/src/modules/module-example-sink.c
Changed
@@ -83,14 +83,14 @@ #define DEFAULT_CHANNELS 2 #define DEFAULT_POSITION " FL FR " -#define MODULE_USAGE " node.latency=<latency as fraction> " \ - " node.name=<name of the nodes> " \ - " node.description=<description of the nodes> " \ - " audio.format=<format, default:"DEFAULT_FORMAT"> " \ - " audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)"> " \ - " audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) "> " \ - " audio.position=<channel map, default:"DEFAULT_POSITION"> " \ - " stream.props=<properties> " +#define MODULE_USAGE "( node.latency=<latency as fraction> ) " \ + "( node.name=<name of the nodes> ) " \ + "( node.description=<description of the nodes> ) " \ + "( audio.format=<format, default:"DEFAULT_FORMAT"> ) " \ + "( audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)"> ) " \ + "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) "> ) " \ + "( audio.position=<channel map, default:"DEFAULT_POSITION"> " \ + "( stream.props=<properties> ) " static const struct spa_dict_item module_props = {
View file
pipewire-0.3.67.tar.gz/src/modules/module-example-source.c -> pipewire-0.3.68.tar.gz/src/modules/module-example-source.c
Changed
@@ -83,14 +83,14 @@ #define DEFAULT_CHANNELS 2 #define DEFAULT_POSITION " FL FR " -#define MODULE_USAGE " node.latency=<latency as fraction> " \ - " node.name=<name of the nodes> " \ - " node.description=<description of the nodes> " \ - " audio.format=<format, default:"DEFAULT_FORMAT"> " \ - " audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)"> " \ - " audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) "> " \ - " audio.position=<channel map, default:"DEFAULT_POSITION"> " \ - " stream.props=<properties> " +#define MODULE_USAGE "( node.latency=<latency as fraction> ) " \ + "( node.name=<name of the nodes> ) " \ + "( node.description=<description of the nodes> ) " \ + "( audio.format=<format, default:"DEFAULT_FORMAT"> ) " \ + "( audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)"> ) " \ + "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) "> ) " \ + "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) " \ + "( stream.props=<properties> ) " static const struct spa_dict_item module_props = {
View file
pipewire-0.3.67.tar.gz/src/modules/module-fallback-sink.c -> pipewire-0.3.68.tar.gz/src/modules/module-fallback-sink.c
Changed
@@ -33,8 +33,8 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic -#define MODULE_USAGE (" sink.name=<str> " \ - " sink.description=<str> ") +#define MODULE_USAGE ("( sink.name=<str> ) " \ + "( sink.description=<str> ) ") static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Pauli Virtanen <pav@iki.fi>" }, @@ -162,7 +162,7 @@ pw_proxy_destroy(impl->sink); } -static void sink_proxy_bound(void *data, uint32_t id) +static void sink_proxy_bound_props(void *data, uint32_t id, const struct spa_dict *props) { struct impl *impl = data; @@ -186,7 +186,7 @@ static const struct pw_proxy_events sink_proxy_events = { PW_VERSION_PROXY_EVENTS, .removed = sink_proxy_removed, - .bound = sink_proxy_bound, + .bound_props = sink_proxy_bound_props, .destroy = sink_proxy_destroy, };
View file
pipewire-0.3.67.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.68.tar.gz/src/modules/module-filter-chain.c
Changed
@@ -62,7 +62,7 @@ * filter.graph = { * nodes = * { - * type = <ladspa | lv2 | builtin> + * type = <ladspa | lv2 | builtin | sofa> * name = <name> * plugin = <plugin> * label = <label> @@ -89,22 +89,24 @@ * Nodes describe the processing filters in the graph. Use a tool like lv2ls * or listplugins to get a list of available plugins, labels and the port names. * - * - `type` is one of `ladspa`, `lv2` or `builtin` + * - `type` is one of `ladspa`, `lv2`, `builtin` or `sofa`. * - `name` is the name for this node, you might need this later to refer to this node * and its ports when setting controls or making links. * - `plugin` is the type specific plugin name. * - For LADSPA plugins it will append `.so` to find the shared object with that * name in the LADSPA plugin path. * - For LV2, this is the plugin URI obtained with lv2ls. - * - For builtin this is ignored + * - For builtin and sofa this is ignored * - `label` is the type specific filter inside the plugin. * - For LADSPA this is the label * - For LV2 this is unused - * - For builtin this is the name of the filter to use + * - For builtin and sofa this is the name of the filter to use * - * - `config` contains a filter specific configuration section. The convolver - * plugin needs this. + * - `config` contains a filter specific configuration section. Some plugins need + * this. (convolver, sofa, delay, ...) * - `control` contains the initial values for the control ports of the filter. + * normally these are given with the port name but it is also possible + * to give the control index as the key. * * ### Links * @@ -196,6 +198,7 @@ * offset = ... * length = ... * channel = ... + * resample_quality = ... * } * ... * } @@ -218,9 +221,13 @@ * can be used as gain. * - A filename to load as the IR. This needs to be a file format supported * by sndfile. + * - filename, ... an array of filenames. The file with the closest samplerate match + * with the graph samplerate will be used. * - `offset` The sample offset in the file as the start of the IR. * - `length` The number of samples to use as the IR. * - `channel` The channel to use from the file as the IR. + * - `resample_quality` The resample quality in case the IR does not match the graph + * samplerate. * * ### Delay * @@ -259,6 +266,61 @@ * * It has an input port "In" and an output port "Out". * + * ## SOFA filter + * + * There is an optional builtin SOFA filter available. + * + * ### Spatializer + * + * The spatializer can be used to place the sound in a 3D space. + * + * The spatializer has an input port "In" and a stereo pair of output ports + * called "Out L" and "Out R". It requires a config section in the node + * declaration in this format: + * + * The control can be changed at runtime to move the sounds around in the + * 3D space. + * + *\code{.unparsed} + * filter.graph = { + * nodes = + * { + * type = sofa + * name = ... + * label = spatializer + * config = { + * blocksize = ... + * tailsize = ... + * filename = ... + * } + * control = { + * "Azimuth" = ... + * "Elevation" = ... + * "Radius" = ... + * } + * ... + * } + * } + * ... + * } + *\endcode + * + * - `blocksize` specifies the size of the blocks to use in the FFT. It is a value + * between 64 and 256. When not specified, this value is + * computed automatically from the number of samples in the file. + * - `tailsize` specifies the size of the tail blocks to use in the FFT. + * - `filename` The SOFA file to load. SOFA files usually end in the .sofa extension + * and contain the HRTF for the various spatial positions. + * + * - `Azimuth` controls the azimuth, this is the direction the sound is coming from + * in degrees between 0 and 360. 0 is straight ahead. 90 is left, 180 + * behind, 270 right. + * - `Elevation` controls the elevation, this is how high/low the signal is in degrees + * between -90 and 90. 0 is straight in front, 90 is directly above + * and -90 directly below. + * - `Radius` controls how far away the signal is as a value between 0 and 100. + * default is 1.0. + * * ## General options * * Options with well-known behavior. Most options can be added to the global @@ -375,16 +437,16 @@ static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, { PW_KEY_MODULE_DESCRIPTION, "Create filter chain streams" }, - { PW_KEY_MODULE_USAGE, " remote.name=<remote> " - " node.latency=<latency as fraction> " - " node.description=<description of the nodes> " - " audio.rate=<sample rate> " - " audio.channels=<number of channels> " - " audio.position=<channel map> " + { PW_KEY_MODULE_USAGE, " ( remote.name=<remote> ) " + "( node.latency=<latency as fraction> ) " + "( node.description=<description of the nodes> ) " + "( audio.rate=<sample rate> ) " + "( audio.channels=<number of channels> ) " + "( audio.position=<channel map> ) " "filter.graph = " " nodes = " " { " - " type = <ladspa | lv2 | builtin> " + " type = <ladspa | lv2 | builtin | sofa> " " name = <name> " " plugin = <plugin> " " label = <label> " @@ -402,8 +464,8 @@ " inputs = <portname> ... " " outputs = <portname> ... " " " - " capture.props=<properties> " - " playback.props=<properties> " }, + "( capture.props=<properties> ) " + "( playback.props=<properties> ) " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, };
View file
pipewire-0.3.67.tar.gz/src/modules/module-filter-chain/ladspa_plugin.c -> pipewire-0.3.68.tar.gz/src/modules/module-filter-chain/ladspa_plugin.c
Changed
@@ -214,7 +214,7 @@ struct fc_plugin *pl = NULL; if (plugin0 != '/') { - const char *search_dirs, *p; + const char *search_dirs, *p, *state = NULL; char pathPATH_MAX; size_t len; @@ -229,7 +229,7 @@ */ errno = ENAMETOOLONG; - while ((p = pw_split_walk(NULL, ":", &len, &search_dirs))) { + while ((p = pw_split_walk(search_dirs, ":", &len, &state))) { int pathlen; if (len >= sizeof(path))
View file
pipewire-0.3.67.tar.gz/src/modules/module-link-factory.c -> pipewire-0.3.68.tar.gz/src/modules/module-link-factory.c
Changed
@@ -22,22 +22,29 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic -#define FACTORY_USAGE PW_KEY_LINK_OUTPUT_NODE"=<output-node> " \ - ""PW_KEY_LINK_OUTPUT_PORT"=<output-port> " \ - PW_KEY_LINK_INPUT_NODE"=<input-node> " \ - ""PW_KEY_LINK_INPUT_PORT"=<input-port> " \ - ""PW_KEY_OBJECT_LINGER"=<bool> " \ - ""PW_KEY_LINK_PASSIVE"=<bool>" +#define FACTORY_USAGE "("PW_KEY_LINK_OUTPUT_NODE"=<output-node>) " \ + "("PW_KEY_LINK_OUTPUT_PORT"=<output-port>) " \ + "("PW_KEY_LINK_INPUT_NODE"=<input-node>) " \ + "("PW_KEY_LINK_INPUT_PORT"=<input-port>) " \ + "("PW_KEY_OBJECT_LINGER"=<bool>) " \ + "("PW_KEY_LINK_PASSIVE"=<bool>)" + +#define MODULE_USAGE "( allow.link.passive=<bool, default false> ) " static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, { PW_KEY_MODULE_DESCRIPTION, "Allow clients to create links" }, + { PW_KEY_MODULE_USAGE, MODULE_USAGE }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; struct factory_data { struct pw_context *context; + struct pw_properties *props; + + unsigned int allow_passive:1; + struct pw_impl_module *module; struct spa_hook module_listener; @@ -396,6 +403,8 @@ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d", pw_impl_client_get_info(client)->id); + if (!d->allow_passive) + pw_properties_set(properties, PW_KEY_LINK_PASSIVE, NULL); link = pw_context_create_link(context, outport, inport, NULL, properties, sizeof(struct link_data)); properties = NULL; @@ -462,6 +471,7 @@ d->factory = NULL; if (d->module) pw_impl_module_destroy(d->module); + pw_properties_free(d->props); } static const struct pw_impl_factory_events factory_events = { @@ -527,6 +537,12 @@ data->module = module; data->context = context; data->work = pw_context_get_work_queue(context); + data->props = args ? pw_properties_new_string(args) : NULL; + + if (data->props) { + data->allow_passive = pw_properties_get_bool(data->props, + "allow.link.passive", false); + } spa_list_init(&data->link_list);
View file
pipewire-0.3.67.tar.gz/src/modules/module-loopback.c -> pipewire-0.3.68.tar.gz/src/modules/module-loopback.c
Changed
@@ -130,15 +130,15 @@ static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, { PW_KEY_MODULE_DESCRIPTION, "Create loopback streams" }, - { PW_KEY_MODULE_USAGE, " remote.name=<remote> " - " node.latency=<latency as fraction> " - " node.description=<description of the nodes> " - " audio.rate=<sample rate> " - " audio.channels=<number of channels> " - " audio.position=<channel map> " - " target.delay.sec=<delay as seconds in float> " - " capture.props=<properties> " - " playback.props=<properties> " }, + { PW_KEY_MODULE_USAGE, " ( remote.name=<remote> ) " + "( node.latency=<latency as fraction> ) " + "( node.description=<description of the nodes> ) " + "( audio.rate=<sample rate> ) " + "( audio.channels=<number of channels> ) " + "( audio.position=<channel map> ) " + "( target.delay.sec=<delay as seconds in float> ) " + "( capture.props=<properties> ) " + "( playback.props=<properties> ) " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, };
View file
pipewire-0.3.67.tar.gz/src/modules/module-pipe-tunnel.c -> pipewire-0.3.68.tar.gz/src/modules/module-pipe-tunnel.c
Changed
@@ -114,18 +114,18 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic -#define MODULE_USAGE " remote.name=<remote> " \ - " node.latency=<latency as fraction> " \ - " node.name=<name of the nodes> " \ - " node.description=<description of the nodes> " \ - " target.object=<remote node target name or serial> "\ - " audio.format=<sample format> " \ - " audio.rate=<sample rate> " \ - " audio.channels=<number of channels> " \ - " audio.position=<channel map> " \ - " tunnel.mode=capture|playback|sink|source " \ - " pipe.filename=<filename> " \ - " stream.props=<properties> " +#define MODULE_USAGE "( remote.name=<remote> ) " \ + "( node.latency=<latency as fraction> ) " \ + "( node.name=<name of the nodes> ) " \ + "( node.description=<description of the nodes> ) " \ + "( target.object=<remote node target name or serial> ) "\ + "( audio.format=<sample format> ) " \ + "( audio.rate=<sample rate> ) " \ + "( audio.channels=<number of channels> ) " \ + "( audio.position=<channel map> ) " \ + "( tunnel.mode=capture|playback|sink|source )" \ + "( pipe.filename=<filename> )" \ + "( stream.props=<properties> ) " static const struct spa_dict_item module_props = { @@ -294,7 +294,7 @@ .process = capture_stream_process }; -static int create_stream(struct impl *impl) +static int create_stream(struct impl *impl) { int res; uint32_t n_params;
View file
pipewire-0.3.67.tar.gz/src/modules/module-protocol-native/protocol-native.c -> pipewire-0.3.68.tar.gz/src/modules/module-protocol-native/protocol-native.c
Changed
@@ -385,6 +385,7 @@ struct pw_proxy *proxy = data; struct spa_pod_parser prs; uint32_t id, global_id; + struct spa_dict props = SPA_DICT_INIT(NULL, 0); spa_pod_parser_init(&prs, msg->data, msg->size); if (spa_pod_parser_get_struct(&prs, @@ -392,7 +393,33 @@ SPA_POD_Int(&global_id)) < 0) return -EINVAL; - return pw_proxy_notify(proxy, struct pw_core_events, bound_id, 0, id, global_id); + /* old client / old/new server -> bound_id + * new client / old server -> bound_id + bound_props (in case it's using bound_props only) */ + pw_proxy_notify(proxy, struct pw_core_events, bound_id, 0, id, global_id); + return pw_proxy_notify(proxy, struct pw_core_events, bound_props, 1, id, global_id, &props); +} + +static int core_event_demarshal_bound_props(void *data, const struct pw_protocol_native_message *msg) +{ + struct pw_proxy *proxy = data; + struct spa_pod_parser prs; + uint32_t id, global_id; + struct spa_pod_frame f2; + struct spa_dict props = SPA_DICT_INIT(NULL, 0); + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_push_struct(&prs, &f0) < 0) + return -EINVAL; + if (spa_pod_parser_get(&prs, + SPA_POD_Int(&id), + SPA_POD_Int(&global_id), NULL) < 0) + return -EINVAL; + + parse_dict_struct(&prs, &f1, &props); + + /* new client / new server -> bound_props + bound_id (in case it's not using bound_props yet) */ + pw_proxy_notify(proxy, struct pw_core_events, bound_id, 0, id, global_id); + return pw_proxy_notify(proxy, struct pw_core_events, bound_props, 1, id, global_id, &props); } static int core_event_demarshal_add_mem(void *data, const struct pw_protocol_native_message *msg) @@ -526,6 +553,25 @@ pw_protocol_native_end_resource(resource, b); } +static void core_event_marshal_bound_props(void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props) +{ + struct pw_resource *resource = data; + struct spa_pod_builder *b; + struct spa_pod_frame f; + + b = pw_protocol_native_begin_resource(resource, PW_CORE_EVENT_BOUND_PROPS, NULL); + + spa_pod_builder_push_struct(b, &f); + spa_pod_builder_add(b, + SPA_POD_Int(id), + SPA_POD_Int(global_id), + NULL); + push_dict(b, props); + spa_pod_builder_pop(b, &f); + + pw_protocol_native_end_resource(resource, b); +} + static void core_event_marshal_add_mem(void *data, uint32_t id, uint32_t type, int fd, uint32_t flags) { struct pw_resource *resource = data; @@ -1863,6 +1909,7 @@ .bound_id = &core_event_marshal_bound_id, .add_mem = &core_event_marshal_add_mem, .remove_mem = &core_event_marshal_remove_mem, + .bound_props = &core_event_marshal_bound_props, }; static const struct pw_protocol_native_demarshal @@ -1876,6 +1923,7 @@ PW_CORE_EVENT_BOUND_ID = { &core_event_demarshal_bound_id, 0, }, PW_CORE_EVENT_ADD_MEM = { &core_event_demarshal_add_mem, 0, }, PW_CORE_EVENT_REMOVE_MEM = { &core_event_demarshal_remove_mem, 0, }, + PW_CORE_EVENT_BOUND_PROPS = { &core_event_demarshal_bound_props, 0, }, }; static const struct pw_protocol_marshal pw_protocol_native_core_marshal = {
View file
pipewire-0.3.67.tar.gz/src/modules/module-protocol-pulse/cmd.c -> pipewire-0.3.68.tar.gz/src/modules/module-protocol-pulse/cmd.c
Changed
@@ -17,9 +17,9 @@ struct module *module; char *a2 = { NULL }; - n = pw_split_ip(args, WHITESPACE, 2, a); + n = args != NULL ? pw_split_ip(args, WHITESPACE, 2, a) : 0; if (n < 1) { - pw_log_info("load-module expects module name"); + pw_log_info("load-module expects module name got '%s'", args); return -EINVAL; }
View file
pipewire-0.3.67.tar.gz/src/modules/module-protocol-pulse/defs.h -> pipewire-0.3.68.tar.gz/src/modules/module-protocol-pulse/defs.h
Changed
@@ -116,6 +116,44 @@ return ERR_UNKNOWN; } +static inline int err_to_res(int err) +{ + switch (err) { + case ERR_OK: return 0; + case ERR_ACCESS: return -EACCES; + case ERR_COMMAND: return -ENOTTY; + case ERR_INVALID: return -EINVAL; + case ERR_EXIST: return -EEXIST; + case ERR_NOENTITY: return -ENOENT; + case ERR_CONNECTIONREFUSED: return -ECONNREFUSED; + case ERR_PROTOCOL: return -EPROTO; + case ERR_TIMEOUT: return -ETIMEDOUT; +#ifdef ENOKEY + case ERR_AUTHKEY: return -ENOKEY; +#endif + case ERR_INTERNAL: return -ENFILE; + case ERR_CONNECTIONTERMINATED: return -ECONNRESET; + case ERR_KILLED: return -EFAULT; + case ERR_INVALIDSERVER: return -EINVAL; + case ERR_MODINITFAILED: return -EIO; +#ifdef EBADFD + case ERR_BADSTATE: return -EBADFD; +#endif + case ERR_NODATA: return -ENODATA; + case ERR_VERSION: return -EPROTO; + case ERR_TOOLARGE: return -E2BIG; + case ERR_NOTSUPPORTED: return -ENOTSUP; + case ERR_UNKNOWN: return -EIO; + case ERR_NOEXTENSION: return -ENOTTY; + case ERR_OBSOLETE: return -ENOTSUP; + case ERR_NOTIMPLEMENTED: return -ENOSYS; + case ERR_FORKED: return -EIO; + case ERR_IO: return -EIO; + case ERR_BUSY: return -EBUSY; + } + return -EIO; +} + enum { SUBSCRIPTION_MASK_NULL = 0x0000U, SUBSCRIPTION_MASK_SINK = 0x0001U,
View file
pipewire-0.3.67.tar.gz/src/modules/module-protocol-pulse/modules/module-null-sink.c -> pipewire-0.3.68.tar.gz/src/modules/module-protocol-pulse/modules/module-null-sink.c
Changed
@@ -40,7 +40,7 @@ module_schedule_unload(module); } -static void module_null_sink_proxy_bound(void *data, uint32_t global_id) +static void module_null_sink_proxy_bound_props(void *data, uint32_t global_id, const struct spa_dict *props) { struct module *module = data; struct module_null_sink_data *d = module->user_data; @@ -63,7 +63,7 @@ static const struct pw_proxy_events proxy_events = { PW_VERSION_PROXY_EVENTS, .removed = module_null_sink_proxy_removed, - .bound = module_null_sink_proxy_bound, + .bound_props = module_null_sink_proxy_bound_props, .error = module_null_sink_proxy_error, .destroy = module_null_sink_proxy_destroy, };
View file
pipewire-0.3.67.tar.gz/src/modules/module-protocol-pulse/modules/module-rtp-recv.c -> pipewire-0.3.68.tar.gz/src/modules/module-protocol-pulse/modules/module-rtp-recv.c
Changed
@@ -51,13 +51,16 @@ fprintf(f, "{"); pw_properties_serialize_dict(f, &data->global_props->dict, 0); - fprintf(f, " stream.props = {"); + fprintf(f, " stream.rules = "); + fprintf(f, " { matches = { rtp.session = \"~.*\" } "), + fprintf(f, " actions = { create-stream = { "); pw_properties_serialize_dict(f, &data->stream_props->dict, 0); - fprintf(f, " } }"); + fprintf(f, " } } } "); + fprintf(f, " }"); fclose(f); data->mod = pw_context_load_module(module->impl->context, - "libpipewire-module-rtp-source", + "libpipewire-module-rtp-sap", args, NULL); free(args); @@ -113,14 +116,13 @@ res = -errno; goto out; } - if ((str = pw_properties_get(props, "sink")) != NULL) - pw_properties_set(stream_props, PW_KEY_TARGET_OBJECT, str); - if ((str = pw_properties_get(props, "sap_address")) != NULL) pw_properties_set(global_props, "sap.ip", str); + if ((str = pw_properties_get(props, "sink")) != NULL) + pw_properties_set(stream_props, PW_KEY_TARGET_OBJECT, str); if ((str = pw_properties_get(props, "latency_msec")) != NULL) - pw_properties_set(global_props, "sess.latency.msec", str); + pw_properties_set(stream_props, "sess.latency.msec", str); d->module = module; d->stream_props = stream_props;
View file
pipewire-0.3.67.tar.gz/src/modules/module-protocol-pulse/modules/module-rtp-send.c -> pipewire-0.3.68.tar.gz/src/modules/module-protocol-pulse/modules/module-rtp-send.c
Changed
@@ -19,8 +19,13 @@ struct spa_hook mod_listener; struct pw_impl_module *mod; + struct spa_hook sap_listener; + struct pw_impl_module *sap; + struct pw_properties *stream_props; struct pw_properties *global_props; + struct pw_properties *sap_props; + struct spa_audio_info_raw info; }; @@ -37,6 +42,19 @@ .destroy = module_destroy }; +static void sap_module_destroy(void *data) +{ + struct module_rtp_send_data *d = data; + spa_hook_remove(&d->sap_listener); + d->sap = NULL; + module_schedule_unload(d->module); +} + +static const struct pw_impl_module_events sap_module_events = { + PW_VERSION_IMPL_MODULE_EVENTS, + .destroy = sap_module_destroy +}; + static int module_rtp_send_load(struct module *module) { struct module_rtp_send_data *data = module->user_data; @@ -75,7 +93,6 @@ data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-rtp-sink", args, NULL); - free(args); if (data->mod == NULL) @@ -85,6 +102,29 @@ &data->mod_listener, &module_events, data); + if ((f = open_memstream(&args, &size)) == NULL) + return -errno; + + fprintf(f, "{"); + pw_properties_serialize_dict(f, &data->sap_props->dict, 0); + fprintf(f, " stream.rules = "); + fprintf(f, " { matches = { pulse.module.id = %u } ", module->index); + fprintf(f, " actions = { announce-stream = { } } "); + fprintf(f, " } }"); + fclose(f); + + data->sap = pw_context_load_module(module->impl->context, + "libpipewire-module-rtp-sap", + args, NULL); + free(args); + + if (data->sap == NULL) + return -errno; + + pw_impl_module_add_listener(data->sap, + &data->sap_listener, + &sap_module_events, data); + return 0; } @@ -92,6 +132,11 @@ { struct module_rtp_send_data *d = module->user_data; + if (d->sap) { + spa_hook_remove(&d->sap_listener); + pw_impl_module_destroy(d->sap); + d->sap = NULL; + } if (d->mod) { spa_hook_remove(&d->mod_listener); pw_impl_module_destroy(d->mod); @@ -100,6 +145,7 @@ pw_properties_free(d->global_props); pw_properties_free(d->stream_props); + pw_properties_free(d->sap_props); return 0; } @@ -127,7 +173,7 @@ { struct module_rtp_send_data * const d = module->user_data; struct pw_properties * const props = module->props; - struct pw_properties *stream_props = NULL, *global_props = NULL; + struct pw_properties *stream_props = NULL, *global_props = NULL, *sap_props = NULL; struct spa_audio_info_raw info = { 0 }; const char *str; int res; @@ -136,7 +182,8 @@ stream_props = pw_properties_new(NULL, NULL); global_props = pw_properties_new(NULL, NULL); - if (!stream_props || !global_props) { + sap_props = pw_properties_new(NULL, NULL); + if (!stream_props || !global_props || !sap_props) { res = -errno; goto out; } @@ -165,31 +212,46 @@ } } - if ((str = pw_properties_get(props, "destination_ip")) != NULL) - pw_properties_set(global_props, "destination.ip", str); - if ((str = pw_properties_get(props, "source_ip")) != NULL) + pw_properties_set(global_props, "sess.media", "audio"); + if ((str = pw_properties_get(props, "enable_opus")) != NULL) { + if (module_args_parse_bool(str)) + pw_properties_set(global_props, "sess.media", "opus"); + } + if ((str = pw_properties_get(props, "source_ip")) != NULL) { pw_properties_set(global_props, "source.ip", str); + pw_properties_set(sap_props, "source.ip", str); + } + if ((str = pw_properties_get(props, "destination_ip")) != NULL) { + pw_properties_set(global_props, "destination.ip", str); + pw_properties_set(sap_props, "sap.ip", str); + } if ((str = pw_properties_get(props, "port")) != NULL) pw_properties_set(global_props, "destination.port", str); if ((str = pw_properties_get(props, "mtu")) != NULL) pw_properties_set(global_props, "net.mtu", str); - if ((str = pw_properties_get(props, "loop")) != NULL) - pw_properties_set(global_props, "net.loop", - module_args_parse_bool(str) ? "true" : "false"); - if ((str = pw_properties_get(props, "ttl")) != NULL) + if ((str = pw_properties_get(props, "loop")) != NULL) { + const char *b = module_args_parse_bool(str) ? "true" : "false"; + pw_properties_set(global_props, "net.loop", b); + pw_properties_set(sap_props, "net.loop", b); + } + if ((str = pw_properties_get(props, "ttl")) != NULL) { pw_properties_set(global_props, "net.ttl", str); + pw_properties_set(sap_props, "net.ttl", str); + } if ((str = pw_properties_get(props, "stream_name")) != NULL) pw_properties_set(global_props, "sess.name", str); d->module = module; d->stream_props = stream_props; d->global_props = global_props; + d->sap_props = sap_props; d->info = info; return 0; out: pw_properties_free(stream_props); pw_properties_free(global_props); + pw_properties_free(sap_props); return res; }
View file
pipewire-0.3.67.tar.gz/src/modules/module-protocol-pulse/modules/module-zeroconf-publish.c -> pipewire-0.3.68.tar.gz/src/modules/module-protocol-pulse/modules/module-zeroconf-publish.c
Changed
@@ -5,6 +5,7 @@ #include <sys/utsname.h> #include <arpa/inet.h> +#include <netinet/in.h> #include <pipewire/pipewire.h>
View file
pipewire-0.3.67.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.68.tar.gz/src/modules/module-protocol-pulse/pulse-server.c
Changed
@@ -1154,30 +1154,25 @@ struct buffer_attr *attr, struct spa_pod_builder *b) { const struct spa_pod *param; - uint32_t blocks, buffers, size, maxsize, stride; + uint32_t blocks, size, stride; struct defs *defs = &s->impl->defs; blocks = 1; stride = s->frame_size; - maxsize = defs->quantum_limit * 32 * s->frame_size; - if (s->direction == PW_DIRECTION_OUTPUT) { - size = attr->minreq; - } else { - size = attr->fragsize; - } - buffers = SPA_CLAMP(maxsize / size, MIN_BUFFERS, MAX_BUFFERS); + size = defs->quantum_limit * s->frame_size; - pw_log_info("%s stride %d maxsize %d size %u buffers %d", s->client->name, - stride, maxsize, size, buffers); + pw_log_info("%s stride %d size %u", s->client->name, stride, size); param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(buffers, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, MIN_BUFFERS, MAX_BUFFERS), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(blocks), SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( - size, size, maxsize), + size, + 16 * s->frame_size, + INT32_MAX), SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride)); return param; } @@ -1411,7 +1406,20 @@ if (avail < (int32_t)minreq || stream->corked) { /* underrun, produce a silence buffer */ size = SPA_MIN(d->maxsize, minreq); - memset(p, 0, size); + switch (stream->ss.format) { + case SPA_AUDIO_FORMAT_U8: + memset(p, 0x80, size); + break; + case SPA_AUDIO_FORMAT_ALAW: + memset(p, 0x80 ^ 0x55, size); + break; + case SPA_AUDIO_FORMAT_ULAW: + memset(p, 0x00 ^ 0xff, size); + break; + default: + memset(p, 0, size); + break; + } if (stream->draining && !stream->corked) { stream->draining = false;
View file
pipewire-0.3.67.tar.gz/src/modules/module-protocol-simple.c -> pipewire-0.3.68.tar.gz/src/modules/module-protocol-simple.c
Changed
@@ -49,7 +49,7 @@ * - `capture.node`: an optional node serial or name to use for capture. * - `playback.node`: an optional node serial or name to use for playback. * - `server.address = `: an array of server addresses to listen on as - * tcp:<ip>:<port>. + * tcp:(<ip>:)<port>. * * ## General options * @@ -119,18 +119,18 @@ #define MAX_CLIENTS 10 -#define MODULE_USAGE " capture=<bool> " \ - " playback=<bool> " \ - " remote.name=<remote> " \ - " node.latency=<num/denom, default:"DEFAULT_LATENCY"> " \ - " node.rate=<1/rate, default:1/"SPA_STRINGIFY(DEFAULT_RATE)"> " \ - " capture.node=<source-target> stream.capture.sink=true " \ - " playback.node=<sink-target> " \ - " audio.rate=<sample-rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> " \ - " audio.format=<format, default:"DEFAULT_FORMAT"> " \ - " audio.channels=<channels, default: "SPA_STRINGIFY(DEFAULT_CHANNELS)"> " \ - " audio.position=<position, default:"DEFAULT_POSITION"> " \ - " server.address=< tcp:<ip>:<port>,... , default:"DEFAULT_SERVER">" \ +#define MODULE_USAGE "( capture=<bool> ) " \ + "( playback=<bool> ) " \ + "( remote.name=<remote> ) " \ + "( node.latency=<num/denom, default:"DEFAULT_LATENCY"> ) " \ + "( node.rate=<1/rate, default:1/"SPA_STRINGIFY(DEFAULT_RATE)"> ) " \ + "( capture.node=<source-target> ( stream.capture.sink=true )) " \ + "( playback.node=<sink-target> ) " \ + "( audio.rate=<sample-rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) " \ + "( audio.format=<format, default:"DEFAULT_FORMAT"> ) " \ + "( audio.channels=<channels, default: "SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) " \ + "( audio.position=<position, default:"DEFAULT_POSITION"> ) " \ + "( server.address=< tcp:(<ip>:)<port>(,...) , default:"DEFAULT_SERVER"> )" \ static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
View file
pipewire-0.3.67.tar.gz/src/modules/module-pulse-tunnel.c -> pipewire-0.3.68.tar.gz/src/modules/module-pulse-tunnel.c
Changed
@@ -32,6 +32,7 @@ #include <pipewire/private.h> #include <pulse/pulseaudio.h> +#include "module-protocol-pulse/defs.h" #include "module-protocol-pulse/format.h" /** \page page_module_pulse_tunnel PipeWire Module: Pulse Tunnel @@ -105,19 +106,19 @@ #define DEFAULT_CHANNELS 2 #define DEFAULT_POSITION " FL FR " -#define MODULE_USAGE " remote.name=<remote> " \ - " node.latency=<latency as fraction> " \ - " node.name=<name of the nodes> " \ - " node.description=<description of the nodes> " \ - " node.target=<remote node target name or serial> " \ - " audio.format=<sample format> " \ - " audio.rate=<sample rate> " \ - " audio.channels=<number of channels> " \ - " audio.position=<channel map> " \ +#define MODULE_USAGE "( remote.name=<remote> " \ + "( node.latency=<latency as fraction> " \ + "( node.name=<name of the nodes> " \ + "( node.description=<description of the nodes> " \ + "( node.target=<remote node target name or serial> " \ + "( audio.format=<sample format> " \ + "( audio.rate=<sample rate> " \ + "( audio.channels=<number of channels> " \ + "( audio.position=<channel map> " \ "pulse.server.address=<address> " \ - "pulse.latency=<latency in msec> " \ - " tunnel.mode=source|sink " \ - " stream.props=<properties> " + "( pulse.latency=<latency in msec, default 200> ) " \ + "( tunnel.mode=source|sink, default sink ) " \ + "( stream.props=<properties> ) " static const struct spa_dict_item module_props = { @@ -134,6 +135,7 @@ struct impl { struct pw_context *context; + struct pw_loop *main_loop; #define MODE_SINK 0 #define MODE_SOURCE 1 @@ -231,23 +233,22 @@ } } -static void update_rate(struct impl *impl, bool playback) +static void update_rate(struct impl *impl, uint32_t filled) { float error, corr; + uint32_t current_latency; if (impl->rate_match == NULL) return; - if (playback) - error = (float)impl->target_latency - (float)impl->current_latency; - else - error = (float)impl->current_latency - (float)impl->target_latency; + current_latency = impl->current_latency + filled; + error = (float)impl->target_latency - (float)(current_latency); error = SPA_CLAMP(error, -impl->max_error, impl->max_error); corr = spa_dll_update(&impl->dll, error); pw_log_debug("error:%f corr:%f current:%u target:%u", error, corr, - impl->current_latency, impl->target_latency); + current_latency, impl->target_latency); SPA_FLAG_SET(impl->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE); impl->rate_match->rate = 1.0f / corr; @@ -282,7 +283,7 @@ size, RINGBUFFER_SIZE); impl->resync = true; } else { - update_rate(impl, true); + update_rate(impl, filled / impl->frame_size); } spa_ringbuffer_write_data(&impl->ring, impl->buffer, RINGBUFFER_SIZE, @@ -323,7 +324,7 @@ avail = impl->target_buffer; index += avail - impl->target_buffer; } else { - update_rate(impl, false); + update_rate(impl, avail / impl->frame_size); } spa_ringbuffer_read_data(&impl->ring, impl->buffer, RINGBUFFER_SIZE, @@ -415,6 +416,20 @@ return 0; } +static int +do_schedule_destroy(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *impl = user_data; + pw_impl_module_schedule_destroy(impl->module); + return 0; +} + +void module_schedule_destroy(struct impl *impl) +{ + pw_loop_invoke(impl->main_loop, do_schedule_destroy, 1, NULL, 0, false, impl); +} + static void context_state_cb(pa_context *c, void *userdata) { struct impl *impl = userdata; @@ -436,7 +451,7 @@ break; } if (do_destroy) - pw_impl_module_schedule_destroy(impl->module); + module_schedule_destroy(impl); } static void stream_state_cb(pa_stream *s, void * userdata) @@ -458,7 +473,7 @@ break; } if (do_destroy) - pw_impl_module_schedule_destroy(impl->module); + module_schedule_destroy(impl); } static void stream_read_request_cb(pa_stream *s, size_t length, void *userdata) @@ -511,7 +526,6 @@ pa_stream_get_latency(impl->pa_stream, &latency, &negative); impl->current_latency = latency * impl->info.rate / SPA_USEC_PER_SEC; - impl->current_latency += filled / impl->frame_size; spa_ringbuffer_write_update(&impl->ring, index); } @@ -536,7 +550,6 @@ pa_stream_get_latency(impl->pa_stream, &latency, &negative); impl->current_latency = latency * impl->info.rate / SPA_USEC_PER_SEC; - impl->current_latency += avail / impl->frame_size; while (avail < (int32_t)length) { uint32_t maxsize = SPA_ROUND_DOWN(sizeof(impl->empty), impl->frame_size); @@ -753,7 +766,7 @@ pa_threaded_mainloop_unlock(impl->pa_mainloop); error: pw_log_error("failed to connect: %s", pa_strerror(res)); - return -res; + return err_to_res(res); } @@ -960,6 +973,7 @@ impl->module = module; impl->context = context; + impl->main_loop = pw_context_get_main_loop(context); spa_ringbuffer_init(&impl->ring); impl->buffer = calloc(1, RINGBUFFER_SIZE);
View file
pipewire-0.3.67.tar.gz/src/modules/module-raop-discover.c -> pipewire-0.3.68.tar.gz/src/modules/module-raop-discover.c
Changed
@@ -31,19 +31,56 @@ * Automatically creates RAOP (Airplay) sink devices based on zeroconf * information. * - * This module will load module-raop-sink for each discovered sink - * with the right parameters. + * This module will load module-raop-sink for each announced stream that matches + * the rule with the create-stream action. + * + * If no stream.rules are given, it will create a sink for all announced + * streams. * * ## Module Options * - * This module has no options. + * Options specific to the behavior of this module + * + * - `stream.rules` = <rules>: match rules, use create-stream actions. See + * \ref page_module_raop_sink for module properties. * * ## Example configuration * *\code{.unparsed} * context.modules = * { name = libpipewire-raop-discover - * args = { } + * args = { + * stream.rules = + * { matches = + * { raop.ip = "~.*" + * #raop.ip.version = 4 | 6 + * #raop.ip.version = 4 + * #raop.port = 1000 + * #raop.name = "" + * #raop.hostname = "" + * #raop.domain = "" + * #raop.device = "" + * #raop.transport = "udp" | "tcp" + * #raop.encryption.type = "RSA" | "auth_setup" | "none" + * #raop.audio.codec = "PCM" | "ALAC" | "AAC" | "AAC-ELD" + * #audio.channels = 2 + * #audio.format = "S16" | "S24" | "S32" + * #audio.rate = 44100 + * #device.model = "" + * } + * + * actions = { + * create-stream = { + * #raop.password = "" + * stream.props = { + * #target.object = "" + * #media.class = "Audio/Sink" + * } + * } + * } + * } + * + * } * } * *\endcode @@ -58,7 +95,10 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic -#define MODULE_USAGE " " +#define MODULE_USAGE "( stream.rules=<rules>, use create-stream actions )" + +#define DEFAULT_CREATE_RULES \ + " { matches = { raop.ip = \"~.*\" } actions = { create-stream = { } } } " static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, @@ -88,6 +128,7 @@ AvahiIfIndex interface; AvahiProtocol protocol; const char *name; + const char *host_name; const char *type; const char *domain; }; @@ -114,6 +155,7 @@ t->info.interface = info->interface; t->info.protocol = info->protocol; t->info.name = strdup(info->name); + t->info.host_name = strdup(info->host_name); t->info.type = strdup(info->type); t->info.domain = strdup(info->domain); spa_list_append(&impl->tunnel_list, &t->link); @@ -255,6 +297,7 @@ spa_hook_remove(&t->module_listener); free((char *) t->info.name); + free((char *) t->info.host_name); free((char *) t->info.type); free((char *) t->info.domain); @@ -266,22 +309,87 @@ .destroy = submodule_destroy, }; +struct match_info { + struct impl *impl; + struct pw_properties *props; + struct tunnel_info *tinfo; + bool matched; +}; + +static int create_stream(struct impl *impl, struct pw_properties *props, + struct tunnel_info *tinfo) +{ + FILE *f; + char *args; + size_t size; + int res = 0; + struct pw_impl_module *mod; + struct tunnel *t; + + if ((f = open_memstream(&args, &size)) == NULL) { + res = -errno; + pw_log_error("Can't open memstream: %m"); + goto done; + } + + fprintf(f, "{"); + pw_properties_serialize_dict(f, &props->dict, 0); + fprintf(f, "}"); + fclose(f); + + pw_log_info("loading module args:'%s'", args); + mod = pw_context_load_module(impl->context, + "libpipewire-module-raop-sink", + args, NULL); + free(args); + + if (mod == NULL) { + res = -errno; + pw_log_error("Can't load module: %m"); + goto done; + } + + t = make_tunnel(impl, tinfo); + if (t == NULL) { + res = -errno; + pw_log_error("Can't make tunnel: %m"); + pw_impl_module_destroy(mod); + goto done; + } + + pw_impl_module_add_listener(mod, &t->module_listener, &submodule_events, t); + + t->module = mod; +done: + return res; +} + +static int rule_matched(void *data, const char *location, const char *action, + const char *str, size_t len) +{ + struct match_info *i = data; + int res = 0; + + i->matched = true; + if (spa_streq(action, "create-stream")) { + pw_properties_update_string(i->props, str, len); + create_stream(i->impl, i->props, i->tinfo); + } + return res; +} + static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *a, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) { struct impl *impl = userdata; - struct tunnel *t; struct tunnel_info tinfo; const char *str; AvahiStringList *l; - FILE *f; - char *args; - size_t size; - struct pw_impl_module *mod; struct pw_properties *props = NULL; char atAVAHI_ADDRESS_STR_MAX; + int ipv; if (event != AVAHI_RESOLVER_FOUND) { pw_log_error("Resolving of '%s' failed: %s", name, @@ -290,6 +398,7 @@ } tinfo = TUNNEL_INFO(.interface = interface, .protocol = protocol, + .host_name = host_name, .name = name, .type = type, .domain = domain); @@ -301,18 +410,14 @@ }
View file
pipewire-0.3.67.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.68.tar.gz/src/modules/module-raop-sink.c
Changed
@@ -18,6 +18,9 @@ #include <netinet/in.h> #include <openssl/err.h> +#if OPENSSL_API_LEVEL >= 30000 +#include <openssl/core_names.h> +#endif #include <openssl/rand.h> #include <openssl/rsa.h> #include <openssl/engine.h> @@ -53,8 +56,10 @@ * * Options specific to the behavior of this module * - * - `raop.hostname`: The hostname of the remote end. + * - `raop.ip`: The ip address of the remote end. * - `raop.port`: The port of the remote end. + * - `raop.name`: The name of the remote end. + * - `raop.hostname`: The hostname of the remote end. * - `raop.transport`: The data transport to use, one of "udp" or "tcp". Defaults * to "udp". * - `raop.encryption.type`: The encryption type to use. One of "none", "RSA" or @@ -84,8 +89,10 @@ * { name = libpipewire-module-raop-sink * args = { * # Set the remote address to tunnel to - * raop.hostname = "my-raop-device" + * raop.ip = "127.0.0.1" * raop.port = 8190 + * raop.name = "my-raop-device" + * raop.hostname = "My Service" * #raop.transport = "udp" * raop.encryption.type = "RSA" * #raop.audio.codec = "PCM" @@ -138,20 +145,22 @@ #define DEFAULT_LATENCY 22050 -#define MODULE_USAGE " raop.hostname=<name of host> " \ - " raop.port=<remote port> " \ - " raop.transport=<transport, default:udp> " \ - " raop.encryption.type=<encryption, default:none> " \ - " raop.audio.codec=PCM " \ - " raop.password=<password for auth> " \ - " node.latency=<latency as fraction> " \ - " node.name=<name of the nodes> " \ - " node.description=<description of the nodes> " \ - " audio.format=<format, default:"DEFAULT_FORMAT"> " \ - " audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)"> " \ - " audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> " \ - " audio.position=<channel map, default:"DEFAULT_POSITION"> " \ - " stream.props=<properties> " +#define MODULE_USAGE "( raop.ip=<ip address of host> ) " \ + "( raop.port=<remote port> ) " \ + "( raop.name=<name of host> ) " \ + "( raop.hostname=<hostname of host> ) " \ + "( raop.transport=<transport, default:udp> ) " \ + "( raop.encryption.type=<encryption, default:none> ) " \ + "( raop.audio.codec=PCM ) " \ + "( raop.password=<password for auth> ) " \ + "( node.latency=<latency as fraction> ) " \ + "( node.name=<name of the nodes> ) " \ + "( node.description=<description of the nodes> ) " \ + "( audio.format=<format, default:"DEFAULT_FORMAT"> ) " \ + "( audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)"> ) " \ + "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) " \ + "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) " \ + "( stream.props=<properties> ) " static const struct spa_dict_item module_props = { @@ -207,12 +216,15 @@ char session_id32; char *password; + char *auth_method; + char *realm; + char *nonce; unsigned int do_disconnect:1; uint8_t keyAES_CHUNK_SIZE; /* Key for aes-cbc */ uint8_t ivAES_CHUNK_SIZE; /* Initialization vector for cbc */ - AES_KEY aes; /* AES encryption */ + EVP_CIPHER_CTX *ctx; uint16_t control_port; int control_fd; @@ -266,21 +278,10 @@ static int aes_encrypt(struct impl *impl, uint8_t *data, int len) { - uint8_t nvAES_CHUNK_SIZE; - uint8_t *buffer; - int i, j; - - memcpy(nv, impl->iv, AES_CHUNK_SIZE); - for (i = 0; i + AES_CHUNK_SIZE <= len; i += AES_CHUNK_SIZE) { - buffer = data + i; - for (j = 0; j < AES_CHUNK_SIZE; j++) - bufferj ^= nvj; - - AES_encrypt(buffer, buffer, &impl->aes); - - memcpy(nv, buffer, AES_CHUNK_SIZE); - } - return i; + int i = len & ~0xf, clen = i; + EVP_EncryptInit(impl->ctx, EVP_aes_128_cbc(), impl->key, impl->iv); + EVP_EncryptUpdate(impl->ctx, data, &clen, data, i); + return i; } static inline uint64_t timespec_to_ntp(struct timespec *ts) @@ -587,7 +588,7 @@ size_t salen; int res, af; - host = pw_properties_get(impl->props, "raop.hostname"); + host = pw_properties_get(impl->props, "raop.ip"); if (host == NULL) return -EINVAL; @@ -703,6 +704,125 @@ } } +static void base64_encode(const uint8_t *data, size_t len, char *enc, char pad) +{ + static const char tab = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + size_t i; + for (i = 0; i < len; i += 3) { + uint32_t v; + v = datai+0 << 16; + v |= (i+1 < len ? datai+1 : 0) << 8; + v |= (i+2 < len ? datai+2 : 0); + *enc++ = tab(v >> (3*6)) & 0x3f; + *enc++ = tab(v >> (2*6)) & 0x3f; + *enc++ = i+1 < len ? tab(v >> (1*6)) & 0x3f : pad; + *enc++ = i+2 < len ? tab(v >> (0*6)) & 0x3f : pad; + } + *enc = '\0'; +} + +static size_t base64_decode(const char *data, size_t len, uint8_t *dec) +{ + uint8_t tab = { + 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, + -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, + -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; + size_t i, j; + for (i = 0, j = 0; i < len; i += 4) { + uint32_t v; + v = tabdatai+0-43 << (3*6); + v |= tabdatai+1-43 << (2*6); + v |= (datai+2 == '=' ? 0 : tabdatai+2-43) << (1*6); + v |= (datai+3 == '=' ? 0 : tabdatai+3-43); + decj++ = (v >> 16) & 0xff; + if (datai+2 != '=') decj++ = (v >> 8) & 0xff; + if (datai+3 != '=') decj++ = v & 0xff; + } + return j; +} + +SPA_PRINTF_FUNC(2,3) +static int MD5_hash(char hashMD5_HASH_LENGTH+1, const char *fmt, ...) +{ + unsigned char dMD5_DIGEST_LENGTH; + int i; + va_list args; + char buffer1024; + unsigned int size; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + size = MD5_DIGEST_LENGTH; + EVP_Digest(buffer, strlen(buffer), d, &size, EVP_md5(), NULL); + for (i = 0; i < MD5_DIGEST_LENGTH; i++) + sprintf(&hash2*i, "%02x", (uint8_t) di); + hashMD5_HASH_LENGTH = '\0'; + return 0; +} + +static int rtsp_add_auth(struct impl *impl, const char *method) +{ + char auth1024; + + if (impl->auth_method == NULL) + return 0; + + if (spa_streq(impl->auth_method, "Basic")) { + char buf256;
View file
pipewire-0.3.67.tar.gz/src/modules/module-raop/rtsp-client.c -> pipewire-0.3.68.tar.gz/src/modules/module-raop/rtsp-client.c
Changed
@@ -530,6 +530,8 @@ int pw_rtsp_client_disconnect(struct pw_rtsp_client *client) { + struct message *msg; + if (client->source == NULL) return 0; @@ -539,6 +541,11 @@ client->url = NULL; free(client->session_id); client->session_id = NULL; + + spa_list_consume(msg, &client->messages, link) { + spa_list_remove(&msg->link); + free(msg); + } pw_rtsp_client_emit_disconnected(client); return 0; }
View file
pipewire-0.3.67.tar.gz/src/modules/module-roc-sink.c -> pipewire-0.3.68.tar.gz/src/modules/module-roc-sink.c
Changed
@@ -54,7 +54,6 @@ * context.modules = * { name = libpipewire-module-roc-sink * args = { - * local.ip = 0.0.0.0 * fec.code = disable * remote.ip = 192.168.0.244 * remote.source.port = 10001 @@ -350,13 +349,12 @@ static const struct spa_dict_item module_roc_sink_info = { { 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> " - "local.ip=<local sender ip> " - "fec.code=<empty>|disable|rs8m|ldpc " + { PW_KEY_MODULE_USAGE, "( sink.name=<name for the sink> ) " + "( fec.code=<empty>|disable|rs8m|ldpc ) " "remote.ip=<remote receiver ip> " - "remote.source.port=<remote receiver port for source packets> " - "remote.repair.port=<remote receiver port for repair packets> " - "sink.props= { key=val ... } " }, + "( remote.source.port=<remote receiver port for source packets> ) " + "( remote.repair.port=<remote receiver port for repair packets> ) " + "( sink.props= { key=val ... } ) " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, };
View file
pipewire-0.3.67.tar.gz/src/modules/module-roc-source.c -> pipewire-0.3.68.tar.gz/src/modules/module-roc-source.c
Changed
@@ -371,14 +371,14 @@ static const struct spa_dict_item module_roc_source_info = { { 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> " - "resampler.profile=<empty>|disable|high|medium|low " - "fec.code=<empty>|disable|rs8m|ldpc " - "sess.latency.msec=<target network latency in milliseconds> " - "local.ip=<local receiver ip> " - "local.source.port=<local receiver port for source packets> " - "local.repair.port=<local receiver port for repair packets> " - "source.props= { key=value ... }" }, + { PW_KEY_MODULE_USAGE, "( source.name=<name for the source> ) " + "( resampler.profile=<empty>|disable|high|medium|low ) " + "( fec.code=<empty>|disable|rs8m|ldpc ) " + "( sess.latency.msec=<target network latency in milliseconds> ) " + "( local.ip=<local receiver ip> ) " + "( local.source.port=<local receiver port for source packets> ) " + "( local.repair.port=<local receiver port for repair packets> ) " + "( source.props= { key=value ... } ) " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, };
View file
pipewire-0.3.67.tar.gz/src/modules/module-rt.c -> pipewire-0.3.68.tar.gz/src/modules/module-rt.c
Changed
@@ -119,10 +119,10 @@ #define DEFAULT_RT_TIME_SOFT -1 #define DEFAULT_RT_TIME_HARD -1 -#define MODULE_USAGE "nice.level=<priority: default "SPA_STRINGIFY(DEFAULT_NICE_LEVEL)"(don't change)> " \ - "rt.prio=<priority: default "SPA_STRINGIFY(DEFAULT_RT_PRIO)"> " \ - "rt.time.soft=<in usec: default "SPA_STRINGIFY(DEFAULT_RT_TIME_SOFT)" " \ - "rt.time.hard=<in usec: default "SPA_STRINGIFY(DEFAULT_RT_TIME_HARD)" " +#define MODULE_USAGE "( nice.level=<priority: default "SPA_STRINGIFY(DEFAULT_NICE_LEVEL)"(don't change)> ) " \ + "( rt.prio=<priority: default "SPA_STRINGIFY(DEFAULT_RT_PRIO)"> ) " \ + "( rt.time.soft=<in usec: default "SPA_STRINGIFY(DEFAULT_RT_TIME_SOFT)" ) " \ + "( rt.time.hard=<in usec: default "SPA_STRINGIFY(DEFAULT_RT_TIME_HARD)" ) " static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, @@ -574,6 +574,7 @@ return false; } if (try == 2) { +#ifdef RLIMIT_RTPRIO struct rlimit rlim; /* second try, try to clamp to RLIMIT_RTPRIO */ if (getrlimit(RLIMIT_RTPRIO, &rlim) == 0 && max > (int)rlim.rlim_max) { @@ -581,6 +582,7 @@ max = (int)rlim.rlim_max; } else +#endif break; } if (max < DEFAULT_RT_PRIO_MIN) {
View file
pipewire-0.3.68.tar.gz/src/modules/module-rtp-sap.c
Added
@@ -0,0 +1,1509 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> */ +/* SPDX-License-Identifier: MIT */ + +#include "config.h" + +#include <limits.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#include <netinet/in.h> +#include <net/if.h> +#include <ctype.h> + +#include <spa/utils/hook.h> +#include <spa/utils/result.h> +#include <spa/debug/types.h> + +#include <pipewire/pipewire.h> +#include <pipewire/impl.h> + +#include <module-rtp/sap.h> + +#ifdef __FreeBSD__ +#define ifr_ifindex ifr_index +#endif + +/** \page page_module_rtp_sap PipeWire Module: Announce and create RTP streams + * + * The `rtp-sap` module announces RTP streams that match the rules with the + * announce-stream action. + * + * It will create source RTP streams that are announced with SAP when they + * match the rule with the create-stream action. + * + * If no stream.rules are given, it will announce all streams with + * sess.sap.announce = true and it will create a receiver for all announced + * streams. + * + * ## Module Options + * + * Options specific to the behavior of this module + * + * - `local.ifname = <str>`: interface name to use + * - `sap.ip = <str>`: IP address of the SAP messages, default "224.0.0.56" + * - `sap.port = <int>`: port of the SAP messages, default 9875 + * - `sap.cleanup.sec = <int>`: cleanup interval in seconds, default 90 seconds + * - `source.ip =<str>`: source IP address, default "0.0.0.0" + * - `net.ttl = <int>`: TTL to use, default 1 + * - `net.loop = <bool>`: loopback multicast, default false + * - `stream.rules` = <rules>: match rules, use create-stream and announce-stream actions + * + * ## General options + * + * Options with well-known behavior: + * + * - \ref PW_KEY_REMOTE_NAME + * + * ## Example configuration + *\code{.unparsed} + * context.modules = + * { name = libpipewire-module-rtp-sap + * args = { + * #local.ifname = "eth0" + * #sap.ip = "224.0.0.56" + * #sap.port = 9875 + * #sap.cleanup.sec = 5 + * #source.ip = "0.0.0.0" + * #net.ttl = 1 + * #net.loop = false + * stream.rules = + * { matches = + * # any of the items in matches needs to match, if one does, + * # actions are emited. + * { # all keys must match the value. ~ in value starts regex. + * #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0" + * #rtp.payload = "127" + * #rtp.fmt = "L16/48000/2" + * #rtp.session = "PipeWire RTP Stream on fedora" + * #rtp.ts-offset = 0 + * #rtp.ts-refclk = "private" + * sess.sap.announce = true + * } + * + * actions = { + * announce-stream = { + * } + * } + * } + * { matches = + * { # all keys must match the value. ~ in value starts regex. + * #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0" + * #rtp.payload = "127" + * #rtp.fmt = "L16/48000/2" + * #rtp.session = "PipeWire RTP Stream on fedora" + * #rtp.ts-offset = 0 + * #rtp.ts-refclk = "private" + * rtp.session = "~.*" + * } + * + * actions = { + * create-stream = { + * #sess.latency.msec = 100 + * #sess.ts-direct = false + * #target.object = "" + * } + * } + * } + * + * } + * } + * + *\endcode + * + * \since 0.3.67 + */ + +#define NAME "rtp-sap" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +#define MAX_SESSIONS 64 + +#define DEFAULT_ANNOUNCE_RULES \ + " { matches = { sess.sap.announce = true } actions = { announce-stream = { } } } " +#define DEFAULT_CREATE_RULES \ + " { matches = { rtp.session = \"~.*\" } actions = { create-stream = { } } } " + +#define DEFAULT_CLEANUP_SEC 90 +#define SAP_INTERVAL_SEC 5 +#define SAP_MIME_TYPE "application/sdp" + +#define DEFAULT_SAP_IP "224.0.0.56" +#define DEFAULT_SAP_PORT 9875 + +#define DEFAULT_SOURCE_IP "0.0.0.0" +#define DEFAULT_TTL 1 +#define DEFAULT_LOOP false + +#define USAGE "( local.ifname=<local interface name to use> ) " \ + "( sap.ip=<SAP IP address to send announce, default:"DEFAULT_SAP_IP"> ) " \ + "( sap.port=<SAP port to send on, default:"SPA_STRINGIFY(DEFAULT_SAP_PORT)"> ) " \ + "( sap.cleanup.sec=<cleanup interval in seconds, default 90> ) " \ + "( source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> ) " \ + "( net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> ) " \ + "( net.loop=<desired loopback, default:"SPA_STRINGIFY(DEFAULT_LOOP)"> ) " \ + "( stream.rules=<rules>, use announce-stream and create-stream actions )" + +static const struct spa_dict_item module_info = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, + { PW_KEY_MODULE_DESCRIPTION, "RTP SAP announce/listen" }, + { PW_KEY_MODULE_USAGE, USAGE }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + +struct sdp_info { + uint16_t hash; + uint32_t ntp; + + char *origin; + char *session_name; + char *media_type; + char *mime_type; + char channelmap512; + + uint16_t dst_port; + struct sockaddr_storage dst_addr; + socklen_t dst_len; + uint16_t ttl; + + uint16_t port; + uint8_t payload; + + uint32_t rate; + uint32_t channels; + + float ptime; + + uint32_t ts_offset; + char *ts_refclk; +}; + +struct session { + struct spa_list link; + + bool announce; + uint64_t timestamp; + + struct impl *impl; + struct node *node; + + struct sdp_info info; + + unsigned has_sent_sap:1; +
View file
pipewire-0.3.68.tar.gz/src/modules/module-rtp-session.c
Added
@@ -0,0 +1,1850 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> */ +/* SPDX-License-Identifier: MIT */ + +#include "config.h" + +#include <limits.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#include <netinet/in.h> +#include <net/if.h> +#include <ctype.h> + +#include <spa/utils/hook.h> +#include <spa/utils/result.h> +#include <spa/utils/ringbuffer.h> +#include <spa/utils/json.h> +#include <spa/param/audio/format-utils.h> +#include <spa/debug/types.h> +#include <spa/debug/mem.h> + +#include <pipewire/pipewire.h> +#include <pipewire/impl.h> + +#include <avahi-client/publish.h> +#include <avahi-client/lookup.h> +#include <avahi-common/error.h> +#include <avahi-common/malloc.h> + +#include "module-zeroconf-discover/avahi-poll.h" + +#include <module-rtp/rtp.h> +#include <module-rtp/apple-midi.h> +#include <module-rtp/stream.h> + +#ifdef __FreeBSD__ +#define ifr_ifindex ifr_index +#endif + +/** \page page_module_rtp_session PipeWire Module: RTP session + * + * The `rtp-session` module creates a media session that is announced + * with avahi/mDNS/Bonjour. + * + * Other machines on the network that run a compatible session will see + * eachother and will be able to send audio/midi between eachother. + * + * The session setup is based on apple-midi and is compatible with + * apple-midi when the session is using midi. + * + * ## Module Options + * + * Options specific to the behavior of this module + * + * - `local.ifname = <str>`: interface name to use + * - `control.ip =<str>`: control IP address, default "0.0.0.0" + * - `control.port =<int>`: control port, default "0" + * - `net.mtu = <int>`: MTU to use, default 1280 + * - `net.ttl = <int>`: TTL to use, default 1 + * - `net.loop = <bool>`: loopback multicast, default false + * - `sess.min-ptime = <int>`: minimum packet time in milliseconds, default 2 + * - `sess.max-ptime = <int>`: maximum packet time in milliseconds, default 20 + * - `sess.latency.msec = <int>`: receiver latency in milliseconds, default 100 + * - `sess.name = <str>`: a session name + * - `sess.ts-offset = <int>`: an offset to apply to the timestamp, default -1 = random offset + * - `sess.ts-refclk = <string>`: the name of a reference clock + * - `sess.media = <string>`: the media type audio|midi|opus, default midi + * - `stream.props = {}`: properties to be passed to the stream + * + * ## General options + * + * Options with well-known behavior: + * + * - \ref PW_KEY_REMOTE_NAME + * - \ref PW_KEY_AUDIO_FORMAT + * - \ref PW_KEY_AUDIO_RATE + * - \ref PW_KEY_AUDIO_CHANNELS + * - \ref SPA_KEY_AUDIO_POSITION + * - \ref PW_KEY_NODE_NAME + * - \ref PW_KEY_NODE_DESCRIPTION + * - \ref PW_KEY_MEDIA_NAME + * - \ref PW_KEY_NODE_GROUP + * - \ref PW_KEY_NODE_LATENCY + * - \ref PW_KEY_NODE_VIRTUAL + * - \ref PW_KEY_MEDIA_CLASS + * + * ## Example configuration + *\code{.unparsed} + * context.modules = + * { name = libpipewire-module-rtp-session + * args = { + * #local.ifname = "eth0" + * #control.ip = "0.0.0.0" + * #control.port = 0 + * #net.mtu = 1280 + * #net.ttl = 1 + * #net.loop = false + * #sess.min-ptime = 2 + * #sess.max-ptime = 20 + * #sess.name = "PipeWire RTP stream" + * #sess.media = "audio" + * stream.props = { + * node.name = "rtp-sink" + * #audio.format = "S16BE" + * #audio.rate = 48000 + * #audio.channels = 2 + * #audio.position = FL FR + * } + * } + *} + * + *\endcode + * + * \since 0.3.60 + */ + +#define NAME "rtp-session" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +#define DEFAULT_CONTROL_IP "0.0.0.0" +#define DEFAULT_CONTROL_PORT 0 +#define DEFAULT_TTL 1 +#define DEFAULT_LOOP false + +#define USAGE "( control.ip=<destination IP address, default:"DEFAULT_CONTROL_IP"> ) " \ + "( control.port=<int, default:"SPA_STRINGIFY(DEFAULT_CONTROL_PORT)"> ) " \ + "( local.ifname=<local interface name to use> ) " \ + "( net.mtu=<desired MTU, default:"SPA_STRINGIFY(DEFAULT_MTU)"> ) " \ + "( net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> ) " \ + "( net.loop=<desired loopback, default:"SPA_STRINGIFY(DEFAULT_LOOP)"> ) " \ + "( sess.name=<a name for the session> ) " \ + "( sess.min-ptime=<minimum packet time in milliseconds, default:2> ) " \ + "( sess.max-ptime=<maximum packet time in milliseconds, default:20> ) " \ + "( sess.media=<string, the media type audio|midi|opus, default midi> ) " \ + "( audio.format=<format, default:"DEFAULT_FORMAT"> ) " \ + "( audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) " \ + "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) "\ + "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) " \ + "( stream.props= { key=value ... } ) " + +static const struct spa_dict_item module_info = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, + { PW_KEY_MODULE_DESCRIPTION, "RTP Sink" }, + { PW_KEY_MODULE_USAGE, USAGE }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + +struct service_info { + AvahiIfIndex interface; + AvahiProtocol protocol; + const char *name; + const char *type; + const char *domain; + const char *host_name; + AvahiAddress address; + uint16_t port; +}; + +#define SERVICE_INFO(...) ((struct service_info){ __VA_ARGS__ }) + +struct service { + struct service_info info; + + struct spa_list link; + struct impl *impl; + + struct session *sess; +}; + +struct session { + struct impl *impl; + struct spa_list link; + + struct sockaddr_storage ctrl_addr; + socklen_t ctrl_len; + struct sockaddr_storage data_addr; + socklen_t data_len; + + struct rtp_stream *send; + struct spa_hook send_listener; + struct rtp_stream *recv; + struct spa_hook recv_listener; + + char *name; + + unsigned we_initiated:1; + +#define SESSION_STATE_INIT 0 +#define SESSION_STATE_SENDING_CTRL_IN 1 +#define SESSION_STATE_SENDING_DATA_IN 2 +#define SESSION_STATE_ESTABLISHING 3 +#define SESSION_STATE_ESTABLISHED 4 + int state;
View file
pipewire-0.3.67.tar.gz/src/modules/module-rtp-sink.c -> pipewire-0.3.68.tar.gz/src/modules/module-rtp-sink.c
Changed
@@ -25,9 +25,12 @@ #include <pipewire/pipewire.h> #include <pipewire/impl.h> -#include <module-rtp/sap.h> -#include <module-rtp/rtp.h> +#include <module-rtp/stream.h> +#ifndef IPTOS_DSCP +#define IPTOS_DSCP_MASK 0xfc +#define IPTOS_DSCP(x) ((x) & IPTOS_DSCP_MASK) +#endif /** \page page_module_rtp_sink PipeWire Module: RTP sink * @@ -38,8 +41,6 @@ * * Options specific to the behavior of this module * - * - `sap.ip = <str>`: IP address of the SAP messages, default "224.0.0.56" - * - `sap.port = <int>`: port of the SAP messages, default 9875 * - `source.ip =<str>`: source IP address, default "0.0.0.0" * - `destination.ip =<str>`: destination IP address, default "224.0.0.56" * - `destination.port =<int>`: destination port, default random beteen 46000 and 47024 @@ -52,7 +53,7 @@ * - `sess.name = <str>`: a session name * - `sess.ts-offset = <int>`: an offset to apply to the timestamp, default -1 = random offset * - `sess.ts-refclk = <string>`: the name of a reference clock - * - `sess.media = <string>`: the session media type audio|midi, default audio + * - `sess.media = <string>`: the media type audio|midi|opus, default audio * - `stream.props = {}`: properties to be passed to the stream * * ## General options @@ -77,19 +78,17 @@ * context.modules = * { name = libpipewire-module-rtp-sink * args = { - * #sap.ip = "224.0.0.56" - * #sap.port = 9875 + * #local.ifname = "eth0" * #source.ip = "0.0.0.0" * #destination.ip = "224.0.0.56" * #destination.port = 46000 - * #local.ifname = "eth0" * #net.mtu = 1280 * #net.ttl = 1 * #net.loop = false * #sess.min-ptime = 2 * #sess.max-ptime = 20 * #sess.name = "PipeWire RTP stream" - * #sess.media = audio + * #sess.media = "audio" * #audio.format = "S16BE" * #audio.rate = 48000 * #audio.channels = 2 @@ -110,52 +109,32 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic -#define SAP_INTERVAL_SEC 5 -#define SAP_MIME_TYPE "application/sdp" - -#define BUFFER_SIZE (1u<<20) -#define BUFFER_MASK (BUFFER_SIZE-1) - -#define DEFAULT_SAP_IP "224.0.0.56" -#define DEFAULT_SAP_PORT 9875 - -#define DEFAULT_SESS_MEDIA "audio" - -#define DEFAULT_FORMAT "S16BE" -#define DEFAULT_RATE 48000 -#define DEFAULT_CHANNELS 2 -#define DEFAULT_POSITION " FL FR " - #define DEFAULT_PORT 46000 #define DEFAULT_SOURCE_IP "0.0.0.0" #define DEFAULT_DESTINATION_IP "224.0.0.56" #define DEFAULT_TTL 1 -#define DEFAULT_MTU 1280 #define DEFAULT_LOOP false #define DEFAULT_DSCP 34 /* Default to AES-67 AF41 (34) */ -#define DEFAULT_MIN_PTIME 2 -#define DEFAULT_MAX_PTIME 20 #define DEFAULT_TS_OFFSET -1 -#define USAGE "sap.ip=<SAP IP address to send announce, default:"DEFAULT_SAP_IP"> " \ - "sap.port=<SAP port to send on, default:"SPA_STRINGIFY(DEFAULT_SAP_PORT)"> " \ - "source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> " \ - "destination.ip=<destination IP address, default:"DEFAULT_DESTINATION_IP"> " \ - "local.ifname=<local interface name to use> " \ - "net.mtu=<desired MTU, default:"SPA_STRINGIFY(DEFAULT_MTU)"> " \ - "net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> " \ - "net.loop=<desired loopback, default:"SPA_STRINGIFY(DEFAULT_LOOP)"> " \ - "net.dscp=<desired DSCP, default:"SPA_STRINGIFY(DEFAULT_DSCP)"> " \ - "sess.name=<a name for the session> " \ - "sess.min-ptime=<minimum packet time in milliseconds, default:2> " \ - "sess.max-ptime=<maximum packet time in milliseconds, default:20> " \ - "sess.media=<media type, audio or midi, default:audio> " \ - "audio.format=<format, default:"DEFAULT_FORMAT"> " \ - "audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> " \ - "audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> "\ - "audio.position=<channel map, default:"DEFAULT_POSITION"> " \ - "stream.props= { key=value ... }" +#define USAGE "( source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> ) " \ + "( destination.ip=<destination IP address, default:"DEFAULT_DESTINATION_IP"> ) " \ + "( destination.port=<int, default random beteen 46000 and 47024> ) " \ + "( local.ifname=<local interface name to use> ) " \ + "( net.mtu=<desired MTU, default:"SPA_STRINGIFY(DEFAULT_MTU)"> ) " \ + "( net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> ) " \ + "( net.loop=<desired loopback, default:"SPA_STRINGIFY(DEFAULT_LOOP)"> ) " \ + "( net.dscp=<desired DSCP, default:"SPA_STRINGIFY(DEFAULT_DSCP)"> ) " \ + "( sess.name=<a name for the session> ) " \ + "( sess.min-ptime=<minimum packet time in milliseconds, default:2> ) " \ + "( sess.max-ptime=<maximum packet time in milliseconds, default:20> ) " \ + "( sess.media=<string, the media type audio|midi|opus, default audio> ) " \ + "( audio.format=<format, default:"DEFAULT_FORMAT"> ) " \ + "( audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) " \ + "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) " \ + "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) " \ + "( stream.props= { key=value ... } ) " static const struct spa_dict_item module_info = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, @@ -165,10 +144,11 @@ }; struct impl { + struct pw_context *context; + struct pw_impl_module *module; struct spa_hook module_listener; struct pw_properties *props; - struct pw_context *module_context; struct pw_loop *loop; @@ -176,110 +156,47 @@ struct spa_hook core_listener; struct spa_hook core_proxy_listener; - struct spa_source *timer; - struct pw_properties *stream_props; - struct pw_stream *stream; - struct spa_hook stream_listener; - - struct spa_io_position *io_position; + struct rtp_stream *stream; unsigned int do_disconnect:1; char *ifname; char *session_name; - uint32_t mtu; bool ttl; bool mcast_loop; uint32_t dscp; - float min_ptime; - float max_ptime; - uint32_t psamples; - uint32_t min_samples; - uint32_t max_samples; struct sockaddr_storage src_addr; socklen_t src_len; - uint16_t port; + uint16_t dst_port; struct sockaddr_storage dst_addr; socklen_t dst_len; - uint16_t sap_port; - struct sockaddr_storage sap_addr; - socklen_t sap_len; - - uint16_t msg_id_hash; - uint32_t ntp; - - struct spa_audio_info info; - const struct format_info *format_info; - uint32_t rate; - uint32_t stride; - int payload; - uint16_t seq; - uint32_t ssrc; - uint32_t ts_offset; - char ts_refclk64; - - struct spa_ringbuffer ring; - uint8_t bufferBUFFER_SIZE; - int rtp_fd; - int sap_fd; - - unsigned sync:1; - unsigned has_sent_sap:1; };
View file
pipewire-0.3.67.tar.gz/src/modules/module-rtp-source.c -> pipewire-0.3.68.tar.gz/src/modules/module-rtp-source.c
Changed
@@ -19,14 +19,16 @@ #include <spa/utils/result.h> #include <spa/utils/ringbuffer.h> #include <spa/utils/dll.h> +#include <spa/utils/json.h> #include <spa/param/audio/format-utils.h> #include <spa/control/control.h> +#include <spa/debug/types.h> +#include <spa/debug/mem.h> #include <pipewire/pipewire.h> #include <pipewire/impl.h> -#include <module-rtp/sap.h> -#include <module-rtp/rtp.h> +#include <module-rtp/stream.h> #ifdef __FreeBSD__ #define ifr_ifindex ifr_index @@ -35,63 +37,52 @@ /** \page page_module_rtp_source PipeWire Module: RTP source * * The `rtp-source` module creates a PipeWire source that receives audio - * RTP packets. + * and midi RTP packets. * * ## Module Options * * Options specific to the behavior of this module * - * - `sap.ip = <str>`: IP address of the SAP messages, default "224.0.0.56" - * - `sap.port = <str>`: port of the SAP messages, default 9875 * - `local.ifname = <str>`: interface name to use + * - `node.always-process = <bool>`: true to receive even when not running * - `sess.latency.msec = <str>`: target network latency in milliseconds, default 100 + * - `sess.media = <string>`: the media type audio|midi|opus, default audio * - `stream.props = {}`: properties to be passed to the stream * * ## General options * * Options with well-known behavior: * - * - \ref PW_KEY_NODE_NAME - * - \ref PW_KEY_NODE_DESCRIPTION + * - \ref PW_KEY_REMOTE_NAME + * - \ref PW_KEY_AUDIO_FORMAT + * - \ref PW_KEY_AUDIO_RATE + * - \ref PW_KEY_AUDIO_CHANNELS + * - \ref SPA_KEY_AUDIO_POSITION * - \ref PW_KEY_MEDIA_NAME * - \ref PW_KEY_MEDIA_CLASS + * - \ref PW_KEY_NODE_NAME + * - \ref PW_KEY_NODE_DESCRIPTION + * - \ref PW_KEY_NODE_GROUP + * - \ref PW_KEY_NODE_LATENCY + * - \ref PW_KEY_NODE_VIRTUAL * * ## Example configuration *\code{.unparsed} * context.modules = * { name = libpipewire-module-rtp-source * args = { - * #sap.ip = 224.0.0.56 - * #sap.port = 9875 * #local.ifname = eth0 * sess.latency.msec = 100 - * #node.always-process = false # true to receive even when not running + * #node.always-process = false + * #sess.media = "audio" + * #audio.format = "S16BE" + * #audio.rate = 48000 + * #audio.channels = 2 + * #audio.position = FL FR * stream.props = { * #media.class = "Audio/Source" - * #node.name = "rtp-source" + * node.name = "rtp-source" * } - * stream.rules = - * { matches = - * # any of the items in matches needs to match, if one does, - * # actions are emited. - * { # all keys must match the value. ~ in value starts regex. - * #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0" - * #rtp.payload = "127" - * #rtp.fmt = "L16/48000/2" - * #rtp.session = "PipeWire RTP Stream on fedora" - * #rtp.ts-offset = 0 - * #rtp.ts-refclk = "private" - * } - * - * actions = { - * create-stream = { - * #sess.latency.msec = 100 - * #sess.ts-direct = false - * #target.object = "" - * } - * } - * } - * * } * } * @@ -105,27 +96,21 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic -#define SAP_MIME_TYPE "application/sdp" - -#define ERROR_MSEC 2 -#define MAX_SESSIONS 16 - -#define DEFAULT_CLEANUP_INTERVAL_SEC 90 -#define DEFAULT_SAP_IP "224.0.0.56" -#define DEFAULT_SAP_PORT 9875 -#define DEFAULT_SESS_LATENCY 100 +#define DEFAULT_CLEANUP_SEC 60 +#define DEFAULT_SOURCE_IP "224.0.0.56" -#define BUFFER_SIZE (1u<<22) -#define BUFFER_MASK (BUFFER_SIZE-1) -#define BUFFER_SIZE2 (BUFFER_SIZE>>1) -#define BUFFER_MASK2 (BUFFER_SIZE2-1) +#define DEFAULT_TS_OFFSET -1 -#define USAGE "sap.ip=<SAP IP address to listen on, default "DEFAULT_SAP_IP"> " \ - "sap.port=<SAP port to listen on, default "SPA_STRINGIFY(DEFAULT_SAP_PORT)"> " \ - "local.ifname=<local interface name to use> " \ - "sess.latency.msec=<target network latency, default "SPA_STRINGIFY(DEFAULT_SESS_LATENCY)"> " \ - "stream.props= { key=value ... } " \ - "stream.rules=<rules> " +#define USAGE "( local.ifname=<local interface name to use> ) " \ + "( source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> ) " \ + "source.port=<int, source port> " \ + "( sess.latency.msec=<target network latency, default "SPA_STRINGIFY(DEFAULT_SESS_LATENCY)"> ) "\ + "( sess.media=<string, the media type audio|midi|opus, default audio> ) " \ + "( audio.format=<format, default:"DEFAULT_FORMAT"> ) " \ + "( audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) " \ + "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) " \ + "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) " \ + "( stream.props= { key=value ... } ) " static const struct spa_dict_item module_info = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, @@ -138,7 +123,7 @@ struct pw_impl_module *module; struct spa_hook module_listener; struct pw_properties *props; - struct pw_context *module_context; + struct pw_context *context; struct pw_loop *loop; struct pw_loop *data_loop; @@ -146,577 +131,42 @@ struct pw_core *core; struct spa_hook core_listener; struct spa_hook core_proxy_listener; - - struct spa_source *timer; - struct spa_source *sap_source; - - struct pw_properties *stream_props; - unsigned int do_disconnect:1; char *ifname; - char *sap_ip; bool always_process; - int sap_port; - int sess_latency_msec; uint32_t cleanup_interval; - struct spa_list sessions; - uint32_t n_sessions; -}; - -struct format_info { - uint32_t media_subtype; - uint32_t format; - uint32_t size; - const char *mime; - const char *media_type; -}; - -static const struct format_info audio_format_info = { - { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_U8, 1, "L8", "audio" }, - { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ALAW, 1, "PCMA", "audio" }, - { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ULAW, 1, "PCMU", "audio" }, - { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S16_BE, 2, "L16", "audio" }, - { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S24_BE, 3, "L24", "audio" }, - { SPA_MEDIA_SUBTYPE_control, 0, 1, "rtp-midi", "audio" }, -}; - -static const struct format_info *find_format_info(const char *mime) -{ - SPA_FOR_EACH_ELEMENT_VAR(audio_format_info, f) - if (spa_streq(f->mime, mime)) - return f; - return NULL; -}
View file
pipewire-0.3.68.tar.gz/src/modules/module-rtp/apple-midi.h
Added
@@ -0,0 +1,52 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_APPLE_MIDI_H +#define PIPEWIRE_APPLE_MIDI_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtp_apple_midi { + uint32_t cmd; + uint32_t protocol; + uint32_t initiator; + uint32_t ssrc; + char name0; +} __attribute__ ((packed)); + +struct rtp_apple_midi_ck { + uint32_t cmd; + uint32_t ssrc; + uint8_t count; + uint8_t padding3; + uint32_t ts1_h; + uint32_t ts1_l; + uint32_t ts2_h; + uint32_t ts2_l; + uint32_t ts3_h; + uint32_t ts3_l; +} __attribute__ ((packed)); + +struct rtp_apple_midi_rs { + uint32_t cmd; + uint32_t ssrc; + uint32_t seqnum; +} __attribute__ ((packed)); + +#define APPLE_MIDI_CMD_IN (0xffff0000 | 'I'<<8 | 'N') +#define APPLE_MIDI_CMD_NO (0xffff0000 | 'N'<<8 | 'O') +#define APPLE_MIDI_CMD_OK (0xffff0000 | 'O'<<8 | 'K') +#define APPLE_MIDI_CMD_CK (0xffff0000 | 'C'<<8 | 'K') +#define APPLE_MIDI_CMD_BY (0xffff0000 | 'B'<<8 | 'Y') +#define APPLE_MIDI_CMD_RS (0xffff0000 | 'R'<<8 | 'S') + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_APPLE_MIDI_H */
View file
pipewire-0.3.68.tar.gz/src/modules/module-rtp/audio.c
Added
@@ -0,0 +1,313 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> */ +/* SPDX-License-Identifier: MIT */ + +static void rtp_audio_process_playback(void *data) +{ + struct impl *impl = data; + struct pw_buffer *buf; + struct spa_data *d; + uint32_t wanted, timestamp, target_buffer, stride, maxsize; + int32_t avail; + + if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) { + pw_log_debug("Out of stream buffers: %m"); + return; + } + d = buf->buffer->datas; + + stride = impl->stride; + + maxsize = d0.maxsize / stride; + wanted = buf->requested ? SPA_MIN(buf->requested, maxsize) : maxsize; + + if (impl->io_position && impl->direct_timestamp) { + /* in direct mode, read directly from the timestamp index, + * because sender and receiver are in sync, this would keep + * target_buffer of samples available. */ + spa_ringbuffer_read_update(&impl->ring, + impl->io_position->clock.position); + } + avail = spa_ringbuffer_get_read_index(&impl->ring, ×tamp); + + target_buffer = impl->target_buffer; + + if (avail < (int32_t)wanted) { + enum spa_log_level level; + memset(d0.data, 0, wanted * stride); + if (impl->have_sync) { + impl->have_sync = false; + level = SPA_LOG_LEVEL_WARN; + } else { + level = SPA_LOG_LEVEL_DEBUG; + } + pw_log(level, "underrun %d/%u < %u", + avail, target_buffer, wanted); + } else { + float error, corr; + if (impl->first) { + if ((uint32_t)avail > target_buffer) { + uint32_t skip = avail - target_buffer; + pw_log_debug("first: avail:%d skip:%u target:%u", + avail, skip, target_buffer); + timestamp += skip; + avail = target_buffer; + } + impl->first = false; + } else if (avail > (int32_t)SPA_MIN(target_buffer * 8, BUFFER_SIZE / stride)) { + pw_log_warn("overrun %u > %u", avail, target_buffer * 8); + timestamp += avail - target_buffer; + avail = target_buffer; + } + if (!impl->direct_timestamp) { + /* when not using direct timestamp and clocks are not + * in sync, try to adjust our playback rate to keep the + * requested target_buffer bytes in the ringbuffer */ + error = (float)target_buffer - (float)avail; + error = SPA_CLAMP(error, -impl->max_error, impl->max_error); + + corr = spa_dll_update(&impl->dll, error); + + pw_log_debug("avail:%u target:%u error:%f corr:%f", avail, + target_buffer, error, corr); + + if (impl->io_rate_match) { + SPA_FLAG_SET(impl->io_rate_match->flags, + SPA_IO_RATE_MATCH_FLAG_ACTIVE); + impl->io_rate_match->rate = 1.0f / corr; + } + } + spa_ringbuffer_read_data(&impl->ring, + impl->buffer, + BUFFER_SIZE, + (timestamp * stride) & BUFFER_MASK, + d0.data, wanted * stride); + + timestamp += wanted; + spa_ringbuffer_read_update(&impl->ring, timestamp); + } + d0.chunk->size = wanted * stride; + d0.chunk->stride = stride; + d0.chunk->offset = 0; + buf->size = wanted; + + pw_stream_queue_buffer(impl->stream, buf); +} + +static int rtp_audio_receive(struct impl *impl, uint8_t *buffer, ssize_t len) +{ + struct rtp_header *hdr; + ssize_t hlen, plen; + uint16_t seq; + uint32_t timestamp, samples, write, expected_write; + uint32_t stride = impl->stride; + int32_t filled; + + if (len < 12) + goto short_packet; + + hdr = (struct rtp_header*)buffer; + if (hdr->v != 2) + goto invalid_version; + + hlen = 12 + hdr->cc * 4; + if (hlen > len) + goto invalid_len; + + if (impl->have_ssrc && impl->ssrc != hdr->ssrc) + goto unexpected_ssrc; + impl->ssrc = hdr->ssrc; + impl->have_ssrc = true; + + seq = ntohs(hdr->sequence_number); + if (impl->have_seq && impl->seq != seq) { + pw_log_info("unexpected seq (%d != %d) SSRC:%u", + seq, impl->seq, hdr->ssrc); + impl->have_sync = false; + } + impl->seq = seq + 1; + impl->have_seq = true; + + timestamp = ntohl(hdr->timestamp) - impl->ts_offset; + + impl->receiving = true; + + plen = len - hlen; + samples = plen / stride; + + filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_write); + + /* we always write to timestamp + delay */ + write = timestamp + impl->target_buffer; + + if (!impl->have_sync) { + pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u target:%u direct:%u", + timestamp, seq, impl->ts_offset, impl->ssrc, + impl->target_buffer, impl->direct_timestamp); + + /* we read from timestamp, keeping target_buffer of data + * in the ringbuffer. */ + impl->ring.readindex = timestamp; + impl->ring.writeindex = write; + filled = impl->target_buffer; + + spa_dll_init(&impl->dll); + spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 128, impl->rate); + memset(impl->buffer, 0, BUFFER_SIZE); + impl->have_sync = true; + } else if (expected_write != write) { + pw_log_debug("unexpected write (%u != %u)", + write, expected_write); + } + + if (filled + samples > BUFFER_SIZE / stride) { + pw_log_debug("capture overrun %u + %u > %u", filled, samples, + BUFFER_SIZE / stride); + impl->have_sync = false; + } else { + pw_log_debug("got samples:%u", samples); + spa_ringbuffer_write_data(&impl->ring, + impl->buffer, + BUFFER_SIZE, + (write * stride) & BUFFER_MASK, + &bufferhlen, (samples * stride)); + write += samples; + spa_ringbuffer_write_update(&impl->ring, write); + } + return 0; + +short_packet: + pw_log_warn("short packet received"); + return -EINVAL; +invalid_version: + pw_log_warn("invalid RTP version"); + spa_debug_mem(0, buffer, len); + return -EPROTO; +invalid_len: + pw_log_warn("invalid RTP length"); + return -EINVAL; +unexpected_ssrc: + pw_log_warn("unexpected SSRC (expected %u != %u)", + impl->ssrc, hdr->ssrc); + return -EINVAL; +} + +static inline void +set_iovec(struct spa_ringbuffer *rbuf, void *buffer, uint32_t size, + uint32_t offset, struct iovec *iov, uint32_t len) +{ + iov0.iov_len = SPA_MIN(len, size - offset);
View file
pipewire-0.3.68.tar.gz/src/modules/module-rtp/midi.c
Added
@@ -0,0 +1,498 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> */ +/* SPDX-License-Identifier: MIT */ + +static void rtp_midi_process_playback(void *data) +{ + struct impl *impl = data; + struct pw_buffer *buf; + struct spa_data *d; + uint32_t timestamp, duration, maxsize, read, rate; + struct spa_pod_builder b; + struct spa_pod_frame f1; + void *ptr; + struct spa_pod *pod; + struct spa_pod_control *c; + + if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) { + pw_log_debug("Out of stream buffers: %m"); + return; + } + d = buf->buffer->datas; + + maxsize = d0.maxsize; + + /* we always use the graph position to select events, the receiver side is + * responsible for smoothing out the RTP timestamps to graph time */ + if (impl->io_position) { + duration = impl->io_position->clock.duration; + timestamp = impl->io_position->clock.position; + rate = impl->io_position->clock.rate.denom; + } else { + duration = 8192; + timestamp = 0; + rate = impl->rate; + } + + /* we copy events into the buffer based on the rtp timestamp + delay. */ + spa_pod_builder_init(&b, d0.data, maxsize); + spa_pod_builder_push_sequence(&b, &f0, 0); + + while (true) { + int32_t avail = spa_ringbuffer_get_read_index(&impl->ring, &read); + if (avail <= 0) + break; + + ptr = SPA_PTROFF(impl->buffer, read & BUFFER_MASK2, void); + + if ((pod = spa_pod_from_data(ptr, avail, 0, avail)) == NULL) + goto done; + if (!spa_pod_is_sequence(pod)) + goto done; + + /* the ringbuffer contains series of sequences, one for each + * received packet */ + SPA_POD_SEQUENCE_FOREACH((struct spa_pod_sequence*)pod, c) { + /* try to render with given delay */ + uint32_t target = c->offset + impl->target_buffer; + target = (uint64_t)target * rate / impl->rate; + if (timestamp != 0) { + /* skip old packets */ + if (target < timestamp) + continue; + /* event for next cycle */ + if (target >= timestamp + duration) + goto complete; + } else { + timestamp = target; + } + spa_pod_builder_control(&b, target - timestamp, SPA_CONTROL_Midi); + spa_pod_builder_bytes(&b, + SPA_POD_BODY(&c->value), + SPA_POD_BODY_SIZE(&c->value)); + } + /* we completed a sequence (one RTP packet), advance ringbuffer + * and go to the next packet */ + read += SPA_PTRDIFF(c, ptr); + spa_ringbuffer_read_update(&impl->ring, read); + } +complete: + spa_pod_builder_pop(&b, &f0); + + if (b.state.offset > maxsize) { + pw_log_warn("overflow buffer %u %u", b.state.offset, maxsize); + b.state.offset = 0; + } + d0.chunk->size = b.state.offset; + d0.chunk->stride = 1; + d0.chunk->offset = 0; +done: + pw_stream_queue_buffer(impl->stream, buf); +} + +static int parse_varlen(uint8_t *p, uint32_t avail, uint32_t *result) +{ + uint32_t value = 0, offs = 0; + while (offs < avail) { + uint8_t b = poffs++; + value = (value << 7) | (b & 0x7f); + if ((b & 0x80) == 0) + break; + } + *result = value; + return offs; +} + +static int get_midi_size(uint8_t *p, uint32_t avail) +{ + int size; + uint32_t offs = 0, value; + + switch (poffs++) { + case 0xc0 ... 0xdf: + size = 2; + break; + case 0x80 ... 0xbf: + case 0xe0 ... 0xef: + size = 3; + break; + case 0xff: + case 0xf0: + case 0xf7: + size = parse_varlen(&poffs, avail - offs, &value); + size += value + 1; + break; + default: + return -EINVAL; + } + return size; +} +static int parse_journal(struct impl *impl, uint8_t *packet, uint16_t seq, uint32_t len) +{ + struct rtp_midi_journal *j = (struct rtp_midi_journal*)packet; + uint16_t seqnum = ntohs(j->checkpoint_seqnum); + rtp_stream_emit_send_feedback(impl, seqnum); + return 0; +} + +static double get_time(struct impl *impl) +{ + struct timespec ts; + struct spa_io_position *pos; + double t; + + clock_gettime(CLOCK_MONOTONIC, &ts); + if ((pos = impl->io_position) != NULL) { + t = pos->clock.position / (double) pos->clock.rate.denom; + t += (SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec) / (double)SPA_NSEC_PER_SEC; + } else { + t = SPA_TIMESPEC_TO_NSEC(&ts); + } + return t; +} + +static int rtp_midi_receive_midi(struct impl *impl, uint8_t *packet, uint32_t timestamp, + uint16_t seq, uint32_t payload_offset, uint32_t plen) +{ + uint32_t write; + struct rtp_midi_header *hdr; + int32_t filled; + struct spa_pod_builder b; + struct spa_pod_frame f1; + void *ptr; + uint32_t offs = payload_offset, len, end; + bool first = true; + + if (impl->direct_timestamp) { + /* in direct timestamp we attach the RTP timestamp directly on the + * midi events and render them in the corresponding cycle */ + if (!impl->have_sync) { + pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u direct:%d", + timestamp, seq, impl->ts_offset, impl->ssrc, + impl->direct_timestamp); + impl->have_sync = true; + } + } else { + /* in non-direct timestamp mode, we relate the graph clock against + * the RTP timestamps */ + double ts = timestamp / (float) impl->rate; + double t = get_time(impl); + double elapsed, estimated, diff; + + /* the elapsed time between RTP timestamps */ + elapsed = ts - impl->last_timestamp; + /* for that elapsed time, our clock should have advanced + * by this amount since the last estimation */ + estimated = impl->last_time + elapsed * impl->corr; + /* calculate the diff between estimated and current clock time in + * samples */ + diff = (estimated - t) * impl->rate; + + /* no sync or we drifted too far, resync */ + if (!impl->have_sync || fabs(diff) > impl->target_buffer) { + impl->corr = 1.0; + spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 256, impl->rate); + + pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u direct:%d", + timestamp, seq, impl->ts_offset, impl->ssrc, + impl->direct_timestamp); + impl->have_sync = true;
View file
pipewire-0.3.68.tar.gz/src/modules/module-rtp/opus.c
Added
@@ -0,0 +1,373 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifdef HAVE_OPUS + +#include <opus/opus.h> +#include <opus/opus_multistream.h> + +static void rtp_opus_process_playback(void *data) +{ + struct impl *impl = data; + struct pw_buffer *buf; + struct spa_data *d; + uint32_t wanted, timestamp, target_buffer, stride, maxsize; + int32_t avail; + + if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) { + pw_log_debug("Out of stream buffers: %m"); + return; + } + d = buf->buffer->datas; + + stride = impl->stride; + + maxsize = d0.maxsize / stride; + wanted = buf->requested ? SPA_MIN(buf->requested, maxsize) : maxsize; + + if (impl->io_position && impl->direct_timestamp) { + /* in direct mode, read directly from the timestamp index, + * because sender and receiver are in sync, this would keep + * target_buffer of samples available. */ + spa_ringbuffer_read_update(&impl->ring, + impl->io_position->clock.position); + } + avail = spa_ringbuffer_get_read_index(&impl->ring, ×tamp); + + target_buffer = impl->target_buffer; + + if (avail < (int32_t)wanted) { + enum spa_log_level level; + memset(d0.data, 0, wanted * stride); + if (impl->have_sync) { + impl->have_sync = false; + level = SPA_LOG_LEVEL_WARN; + } else { + level = SPA_LOG_LEVEL_DEBUG; + } + pw_log(level, "underrun %d/%u < %u", + avail, target_buffer, wanted); + } else { + float error, corr; + if (impl->first) { + if ((uint32_t)avail > target_buffer) { + uint32_t skip = avail - target_buffer; + pw_log_debug("first: avail:%d skip:%u target:%u", + avail, skip, target_buffer); + timestamp += skip; + avail = target_buffer; + } + impl->first = false; + } else if (avail > (int32_t)SPA_MIN(target_buffer * 8, BUFFER_SIZE2 / stride)) { + pw_log_warn("overrun %u > %u", avail, target_buffer * 8); + timestamp += avail - target_buffer; + avail = target_buffer; + } + if (!impl->direct_timestamp) { + /* when not using direct timestamp and clocks are not + * in sync, try to adjust our playback rate to keep the + * requested target_buffer bytes in the ringbuffer */ + error = (float)target_buffer - (float)avail; + error = SPA_CLAMP(error, -impl->max_error, impl->max_error); + + corr = spa_dll_update(&impl->dll, error); + + pw_log_debug("avail:%u target:%u error:%f corr:%f", avail, + target_buffer, error, corr); + + if (impl->io_rate_match) { + SPA_FLAG_SET(impl->io_rate_match->flags, + SPA_IO_RATE_MATCH_FLAG_ACTIVE); + impl->io_rate_match->rate = 1.0f / corr; + } + } + spa_ringbuffer_read_data(&impl->ring, + impl->buffer, + BUFFER_SIZE2, + (timestamp * stride) & BUFFER_MASK2, + d0.data, wanted * stride); + + timestamp += wanted; + spa_ringbuffer_read_update(&impl->ring, timestamp); + } + d0.chunk->size = wanted * stride; + d0.chunk->stride = stride; + d0.chunk->offset = 0; + buf->size = wanted; + + pw_stream_queue_buffer(impl->stream, buf); +} + +static int rtp_opus_receive(struct impl *impl, uint8_t *buffer, ssize_t len) +{ + struct rtp_header *hdr; + ssize_t hlen, plen; + uint16_t seq; + uint32_t timestamp, samples, write, expected_write; + uint32_t stride = impl->stride; + OpusMSDecoder *dec = impl->stream_data; + int32_t filled; + int res; + + if (len < 12) + goto short_packet; + + hdr = (struct rtp_header*)buffer; + if (hdr->v != 2) + goto invalid_version; + + hlen = 12 + hdr->cc * 4; + if (hlen > len) + goto invalid_len; + + if (impl->have_ssrc && impl->ssrc != hdr->ssrc) + goto unexpected_ssrc; + impl->ssrc = hdr->ssrc; + impl->have_ssrc = true; + + seq = ntohs(hdr->sequence_number); + if (impl->have_seq && impl->seq != seq) { + pw_log_info("unexpected seq (%d != %d) SSRC:%u", + seq, impl->seq, hdr->ssrc); + impl->have_sync = false; + } + impl->seq = seq + 1; + impl->have_seq = true; + + timestamp = ntohl(hdr->timestamp) - impl->ts_offset; + + impl->receiving = true; + + plen = len - hlen; + + filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_write); + + /* we always write to timestamp + delay */ + write = timestamp + impl->target_buffer; + + if (!impl->have_sync) { + pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u target:%u direct:%u", + timestamp, seq, impl->ts_offset, impl->ssrc, + impl->target_buffer, impl->direct_timestamp); + + /* we read from timestamp, keeping target_buffer of data + * in the ringbuffer. */ + impl->ring.readindex = timestamp; + impl->ring.writeindex = write; + filled = impl->target_buffer; + + spa_dll_init(&impl->dll); + spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 128, impl->rate); + memset(impl->buffer, 0, BUFFER_SIZE); + impl->have_sync = true; + } else if (expected_write != write) { + pw_log_debug("unexpected write (%u != %u)", + write, expected_write); + } + + if (filled + plen > BUFFER_SIZE2 / stride) { + pw_log_debug("capture overrun %u + %zd > %u", filled, plen, + BUFFER_SIZE2 / stride); + impl->have_sync = false; + } else { + uint32_t index = (write * stride) & BUFFER_MASK2, end; + + res = opus_multistream_decode_float(dec, + &bufferhlen, plen, + (float*)&impl->bufferindex, 2880, + 0); + + end = index + (res * stride); + /* fold to the lower part of the ringbuffer when overflow */ + if (end > BUFFER_SIZE2) + memmove(impl->buffer, &impl->bufferBUFFER_SIZE2, end - BUFFER_SIZE2); + + pw_log_debug("receiving %zd len:%d timestamp:%d %u", plen, res, timestamp, index); + samples = res; + + write += samples; + spa_ringbuffer_write_update(&impl->ring, write); + } + return 0; + +short_packet: + pw_log_warn("short packet received"); + return -EINVAL; +invalid_version: + pw_log_warn("invalid RTP version"); + spa_debug_mem(0, buffer, len);
View file
pipewire-0.3.67.tar.gz/src/modules/module-rtp/rtp.h -> pipewire-0.3.68.tar.gz/src/modules/module-rtp/rtp.h
Changed
@@ -58,17 +58,34 @@ unsigned z:1; unsigned j:1; unsigned b:1; - uint8_t len_b; #elif __BYTE_ORDER == __BIG_ENDIAN unsigned b:1; unsigned j:1; unsigned z:1; unsigned p:1; unsigned len:4; +#endif uint8_t len_b; +} __attribute__ ((packed)); + +struct rtp_midi_journal { +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned totchan:4; + unsigned H:1; + unsigned A:1; + unsigned Y:1; + unsigned S:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned S:1; + unsigned Y:1; + unsigned A:1; + unsigned H:1; + unsigned totchan:4; #endif + uint16_t checkpoint_seqnum; } __attribute__ ((packed)); + #ifdef __cplusplus } #endif
View file
pipewire-0.3.68.tar.gz/src/modules/module-rtp/stream.c
Added
@@ -0,0 +1,521 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <spa/utils/result.h> +#include <spa/utils/json.h> +#include <spa/utils/ringbuffer.h> +#include <spa/utils/dll.h> +#include <spa/param/audio/format-utils.h> +#include <spa/control/control.h> +#include <spa/debug/types.h> +#include <spa/debug/mem.h> + +#include "config.h" + +#include <pipewire/pipewire.h> +#include <pipewire/impl.h> + +#include <module-rtp/rtp.h> +#include <module-rtp/stream.h> +#include <module-rtp/apple-midi.h> + +#define BUFFER_SIZE (1u<<22) +#define BUFFER_MASK (BUFFER_SIZE-1) +#define BUFFER_SIZE2 (BUFFER_SIZE>>1) +#define BUFFER_MASK2 (BUFFER_SIZE2-1) + +#define rtp_stream_emit(s,m,v,...) spa_hook_list_call(&s->listener_list, \ + struct rtp_stream_events, m, v, ##__VA_ARGS__) +#define rtp_stream_emit_destroy(s) rtp_stream_emit(s, destroy, 0) +#define rtp_stream_emit_state_changed(s,n,e) rtp_stream_emit(s, state_changed,0,n,e) +#define rtp_stream_emit_send_packet(s,i,l) rtp_stream_emit(s, send_packet,0,i,l) +#define rtp_stream_emit_send_feedback(s,seq) rtp_stream_emit(s, send_feedback,0,seq) + +struct impl { + struct spa_audio_info info; + struct spa_audio_info stream_info; + + struct pw_stream *stream; + struct spa_hook stream_listener; + struct pw_stream_events stream_events; + + struct spa_hook_list listener_list; + struct spa_hook listener; + + const struct format_info *format_info; + + void *stream_data; + + uint32_t rate; + uint32_t stride; + uint8_t payload; + uint32_t ssrc; + uint16_t seq; + unsigned have_ssrc:1; + unsigned have_seq:1; + uint32_t ts_offset; + uint32_t psamples; + uint32_t mtu; + + struct spa_ringbuffer ring; + uint8_t bufferBUFFER_SIZE; + + struct spa_io_rate_match *io_rate_match; + struct spa_io_position *io_position; + struct spa_dll dll; + double corr; + uint32_t target_buffer; + float max_error; + + float last_timestamp; + float last_time; + + unsigned direct_timestamp:1; + unsigned always_process:1; + unsigned started:1; + unsigned have_sync:1; + unsigned receiving:1; + unsigned first:1; + + int (*receive_rtp)(struct impl *impl, uint8_t *buffer, ssize_t len); +}; + +#include "module-rtp/audio.c" +#include "module-rtp/midi.c" +#include "module-rtp/opus.c" + +struct format_info { + uint32_t media_subtype; + uint32_t format; + uint32_t size; + const char *mime; + const char *media_type; +}; + +static const struct format_info audio_format_info = { + { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_U8, 1, "L8", "audio" }, + { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ALAW, 1, "PCMA", "audio" }, + { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ULAW, 1, "PCMU", "audio" }, + { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S16_BE, 2, "L16", "audio" }, + { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S24_BE, 3, "L24", "audio" }, + { SPA_MEDIA_SUBTYPE_control, 0, 1, "rtp-midi", "audio" }, + { SPA_MEDIA_SUBTYPE_opus, 0, 4, "opus", "audio" }, +}; + +static void stream_io_changed(void *data, uint32_t id, void *area, uint32_t size) +{ + struct impl *impl = data; + switch (id) { + case SPA_IO_RateMatch: + impl->io_rate_match = area; + break; + case SPA_IO_Position: + impl->io_position = area; + break; + } +} + +static void stream_destroy(void *d) +{ + struct impl *impl = d; + spa_hook_remove(&impl->stream_listener); + impl->stream = NULL; +} + +static int stream_start(struct impl *impl) +{ + if (impl->started) + return 0; + + rtp_stream_emit_state_changed(impl, true, NULL); + + impl->started = true; + return 0; +} + +static int stream_stop(struct impl *impl) +{ + if (!impl->started) + return 0; + + rtp_stream_emit_state_changed(impl, false, NULL); + + impl->started = false; + return 0; +} + +static void on_stream_state_changed(void *d, enum pw_stream_state old, + enum pw_stream_state state, const char *error) +{ + struct impl *impl = d; + + switch (state) { + case PW_STREAM_STATE_UNCONNECTED: + pw_log_info("stream disconnected"); + break; + case PW_STREAM_STATE_ERROR: + pw_log_error("stream error: %s", error); + rtp_stream_emit_state_changed(impl, false, error); + break; + case PW_STREAM_STATE_STREAMING: + if ((errno = -stream_start(impl)) < 0) + pw_log_error("failed to start RTP stream: %m"); + break; + case PW_STREAM_STATE_PAUSED: + if (!impl->always_process) + stream_stop(impl); + impl->have_sync = false; + break; + default: + break; + } +} + +static const struct pw_stream_events stream_events = { + PW_VERSION_STREAM_EVENTS, + .destroy = stream_destroy, + .state_changed = on_stream_state_changed, + .io_changed = stream_io_changed, +}; + +static const struct format_info *find_audio_format_info(const struct spa_audio_info *info) +{ + SPA_FOR_EACH_ELEMENT_VAR(audio_format_info, f) + if (f->media_subtype == info->media_subtype && + (f->format == 0 || f->format == info->info.raw.format)) + return f; + return NULL; +} + +static inline uint32_t format_from_name(const char *name, size_t len) +{ + int i; + for (i = 0; spa_type_audio_formati.name; i++) { + if (strncmp(name, spa_debug_type_short_name(spa_type_audio_formati.name), len) == 0) + return spa_type_audio_formati.type;
View file
pipewire-0.3.68.tar.gz/src/modules/module-rtp/stream.h
Added
@@ -0,0 +1,54 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_RTP_STREAM_H +#define PIPEWIRE_RTP_STREAM_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtp_stream; + +#define DEFAULT_FORMAT "S16BE" +#define DEFAULT_RATE 48000 +#define DEFAULT_CHANNELS 2 +#define DEFAULT_POSITION " FL FR " + +#define ERROR_MSEC 2 +#define DEFAULT_SESS_LATENCY 100 + +#define DEFAULT_MTU 1280 +#define DEFAULT_MIN_PTIME 2 +#define DEFAULT_MAX_PTIME 20 + +struct rtp_stream_events { +#define RTP_VERSION_STREAM_EVENTS 0 + uint32_t version; + + void (*destroy) (void *data); + + void (*state_changed) (void *data, bool started, const char *error); + + void (*send_packet) (void *data, struct iovec *iov, size_t iovlen); + + void (*send_feedback) (void *data, uint32_t senum); +}; + +struct rtp_stream *rtp_stream_new(struct pw_core *core, + enum pw_direction direction, struct pw_properties *props, + const struct rtp_stream_events *events, void *data); + +void rtp_stream_destroy(struct rtp_stream *s); + +int rtp_stream_receive_packet(struct rtp_stream *s, uint8_t *buffer, size_t len); + +uint64_t rtp_stream_get_time(struct rtp_stream *s, uint64_t *rate); + + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_RTP_STREAM_H */
View file
pipewire-0.3.67.tar.gz/src/modules/module-x11-bell.c -> pipewire-0.3.68.tar.gz/src/modules/module-x11-bell.c
Changed
@@ -230,10 +230,10 @@ static const struct spa_dict_item module_x11_bell_info = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, { PW_KEY_MODULE_DESCRIPTION, "X11 Bell interceptor" }, - { PW_KEY_MODULE_USAGE, "sink.name=<name for the sink> " - "sample.name=<the sample name> " - "x11.display=<the X11 display> " - "x11.xauthority=<the X11 XAuthority> " }, + { PW_KEY_MODULE_USAGE, "( sink.name=<name for the sink> ) " + "( sample.name=<the sample name> ) " + "( x11.display=<the X11 display> ) " + ".x11.xauthority=<the X11 XAuthority> )" }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; SPA_EXPORT
View file
pipewire-0.3.67.tar.gz/src/modules/module-zeroconf-discover.c -> pipewire-0.3.68.tar.gz/src/modules/module-zeroconf-discover.c
Changed
@@ -55,7 +55,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic -#define MODULE_USAGE "pulse.latency=<latency in msec> " +#define MODULE_USAGE "( pulse.latency=<latency in msec, default 200> ) " static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
View file
pipewire-0.3.67.tar.gz/src/pipewire/buffers.c -> pipewire-0.3.68.tar.gz/src/pipewire/buffers.c
Changed
@@ -292,6 +292,9 @@ align = SPA_MAX(align, qalign); types = qtypes; + if (SPA_FLAG_IS_SET(flags, PW_BUFFERS_FLAG_ASYNC)) + max_buffers = SPA_MAX(2u, max_buffers); + pw_log_debug("%p: %d %d %d %d %d %d -> %d %zd %zd %d %zd %d", result, qblocks, qminsize, qstride, qmax_buffers, qalign, qtypes, blocks, minsize, stride, max_buffers, align, types); @@ -300,6 +303,7 @@ minsize = 8192; max_buffers = 2; } + if (SPA_FLAG_IS_SET(flags, PW_BUFFERS_FLAG_SHARED_MEM)) { if (types != SPA_ID_INVALID) SPA_FLAG_CLEAR(types, 1<<SPA_DATA_MemPtr);
View file
pipewire-0.3.67.tar.gz/src/pipewire/buffers.h -> pipewire-0.3.68.tar.gz/src/pipewire/buffers.h
Changed
@@ -29,6 +29,7 @@ #define PW_BUFFERS_FLAG_DYNAMIC (1<<2) /**< buffers have dynamic data */ #define PW_BUFFERS_FLAG_SHARED_MEM (1<<3) /**< buffers need shared memory */ #define PW_BUFFERS_FLAG_IN_PRIORITY (1<<4) /**< input parameters have priority */ +#define PW_BUFFERS_FLAG_ASYNC (1<<5) /**< one of the nodes is async */ struct pw_buffers { struct pw_memblock *mem; /**< allocated buffer memory */
View file
pipewire-0.3.67.tar.gz/src/pipewire/context.c -> pipewire-0.3.68.tar.gz/src/pipewire/context.c
Changed
@@ -768,13 +768,89 @@ static int ensure_state(struct pw_impl_node *node, bool running) { enum pw_node_state state = node->info.state; - if (node->active && !SPA_FLAG_IS_SET(node->spa_flags, SPA_NODE_FLAG_NEED_CONFIGURE) && running) + if (node->active && node->runnable && + !SPA_FLAG_IS_SET(node->spa_flags, SPA_NODE_FLAG_NEED_CONFIGURE) && running) state = PW_NODE_STATE_RUNNING; else if (state > PW_NODE_STATE_IDLE) state = PW_NODE_STATE_IDLE; return pw_impl_node_set_state(node, state); } +/* From a node (that is runnable) follow all prepared links and groups to + * active nodes up to the driver and make them recursively runnable as well. + * + * We stop at driver nodes so that other paths linked to the driver will stay + * unrunnable when no other runnable path exists. + */ +static inline int run_nodes(struct pw_context *context, struct pw_impl_node *node, struct spa_list *nodes) +{ + struct pw_impl_node *t; + struct pw_impl_port *p; + struct pw_impl_link *l; + + if (!node->runnable) + return 0; + + pw_log_debug("node %p: '%s'", node, node->name); + + spa_list_for_each(p, &node->input_ports, link) { + spa_list_for_each(l, &p->links, input_link) { + t = l->output->node; + + if (!t->active || !l->prepared || t->runnable) + continue; + + pw_log_debug(" peer %p: '%s'", t, t->name); + t->runnable = true; + if (!t->driver) + run_nodes(context, t, nodes); + } + } + spa_list_for_each(p, &node->output_ports, link) { + spa_list_for_each(l, &p->links, output_link) { + t = l->input->node; + + if (!t->active || !l->prepared || t->runnable) + continue; + + pw_log_debug(" peer %p: '%s'", t, t->name); + t->runnable = true; + if (!t->driver) + run_nodes(context, t, nodes); + } + } + /* now go through all the nodes that have the same link group and + * that are not yet visited. Note how nodes with the same group + * don't get included here. They were added to the same driver but + * need to otherwise stay idle unless some non-passive link activates + * them. */ + if (node->link_group != NULL) { + spa_list_for_each(t, nodes, sort_link) { + if (t->exported || !t->active || t->runnable) + continue; + if (!spa_streq(t->link_group, node->link_group)) + continue; + + pw_log_debug(" group %p: '%s'", t, t->name); + t->runnable = true; + if (!t->driver) + run_nodes(context, t, nodes); + } + } + return 0; +} + +/* Follow all prepared links and groups from node, activate the links. + * If a non-passive link is found, we set the peer runnable flag. + * + * After this is done, we end up with a list of nodes in collect that are all + * linked to node. + * Some of the nodes have the runnable flag set. We then start from those nodes + * and make all linked nodes and groups runnable as well. (see run_nodes). + * + * This ensures that we only activate the paths from the runnable nodes to the + * driver nodes and leave the other nodes idle. + */ static int collect_nodes(struct pw_context *context, struct pw_impl_node *node, struct spa_list *collect) { struct spa_list queue; @@ -794,7 +870,6 @@ spa_list_consume(n, &queue, sort_link) { spa_list_remove(&n->sort_link); spa_list_append(collect, &n->sort_link); - n->runnable = n->always_process; pw_log_debug(" next node %p: '%s' runnable:%u", n, n->name, n->runnable); @@ -810,16 +885,13 @@ pw_impl_link_prepare(l); - if (!l->prepared) + if (!l->prepared || t->visited) continue; if (!l->passive) - n->runnable = true; - - if (!t->visited) { - t->visited = true; - spa_list_append(&queue, &t->sort_link); - } + t->runnable = true; + t->visited = true; + spa_list_append(&queue, &t->sort_link); } } spa_list_for_each(p, &n->output_ports, link) { @@ -831,33 +903,35 @@ pw_impl_link_prepare(l); - if (!l->prepared) + if (!l->prepared || t->visited) continue; if (!l->passive) - n->runnable = true; - - if (!t->visited) { - t->visited = true; - spa_list_append(&queue, &t->sort_link); - } + t->runnable = true; + t->visited = true; + spa_list_append(&queue, &t->sort_link); } } /* now go through all the nodes that have the same group and * that are not yet visited */ - if (n->group != NULL) { + if (n->group != NULL || n->link_group != NULL) { spa_list_for_each(t, &context->node_list, link) { if (t->exported || !t->active || t->visited) continue; - if (!spa_streq(t->group, n->group)) + if ((t->group == NULL || !spa_streq(t->group, n->group)) && + (t->link_group == NULL || !spa_streq(t->link_group, n->link_group))) continue; - pw_log_debug("%p: %s join group %s", - t, t->name, t->group); + pw_log_debug("%p: %s join group:%s link-group:%s", + t, t->name, n->group, n->link_group); t->visited = true; spa_list_append(&queue, &t->sort_link); } } + pw_log_debug(" next node %p: '%s' runnable:%u", n, n->name, n->runnable); } + spa_list_for_each(n, collect, sort_link) + run_nodes(context, n, collect); + return 0; } @@ -868,8 +942,7 @@ pw_log_debug("driver: %p %s runnable:%u", driver, driver->name, driver->runnable); spa_list_consume(n, nodes, sort_link) { spa_list_remove(&n->sort_link); - if (n->runnable) - driver->runnable = true; + pw_log_debug(" follower: %p %s runnable:%u driver-runnable:%u", n, n->name, n->runnable, driver->runnable); pw_impl_node_set_driver(n, driver); @@ -1104,6 +1177,12 @@ again: impl->recalc = true; + /* clean up the flags first */ + spa_list_for_each(n, &context->node_list, link) { + n->visited = false; + n->runnable = n->always_process; + } + get_quantums(context, &def_quantum, &min_quantum, &max_quantum, &lim_quantum, &rate_quantum); rates = get_rates(context, &def_rate, &n_rates, &global_force_rate); @@ -1181,6 +1260,7 @@ /* is any active and want a driver */ if (t->want_driver && t->active && t->runnable) { driver = target; + driver->runnable = true; break; } } @@ -1188,13 +1268,10 @@ /* driver needed for this group */
View file
pipewire-0.3.67.tar.gz/src/pipewire/core.c -> pipewire-0.3.68.tar.gz/src/pipewire/core.c
Changed
@@ -69,9 +69,8 @@ struct pw_proxy *proxy; pw_log_debug("%p: proxy id %u bound %u", this, id, global_id); - if ((proxy = pw_map_lookup(&this->objects, id)) != NULL) { + if ((proxy = pw_map_lookup(&this->objects, id)) != NULL) pw_proxy_set_bound_id(proxy, global_id); - } } static void core_event_add_mem(void *data, uint32_t id, uint32_t type, int fd, uint32_t flags) @@ -90,6 +89,16 @@ } } +static void core_event_bound_props(void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props) +{ + struct pw_core *this = data; + struct pw_proxy *proxy; + + pw_log_debug("%p: proxy id %u bound %u", this, id, global_id); + if ((proxy = pw_map_lookup(&this->objects, id)) != NULL) + pw_proxy_emit_bound_props(proxy, global_id, props); +} + static void core_event_remove_mem(void *data, uint32_t id) { struct pw_core *this = data; @@ -106,6 +115,7 @@ .bound_id = core_event_bound_id, .add_mem = core_event_add_mem, .remove_mem = core_event_remove_mem, + .bound_props = core_event_bound_props, }; SPA_EXPORT
View file
pipewire-0.3.67.tar.gz/src/pipewire/core.h -> pipewire-0.3.68.tar.gz/src/pipewire/core.h
Changed
@@ -34,7 +34,7 @@ #define PW_TYPE_INTERFACE_Core PW_TYPE_INFO_INTERFACE_BASE "Core" #define PW_TYPE_INTERFACE_Registry PW_TYPE_INFO_INTERFACE_BASE "Registry" -#define PW_VERSION_CORE 3 +#define PW_VERSION_CORE 4 struct pw_core; #define PW_VERSION_REGISTRY 3 struct pw_registry; @@ -80,21 +80,22 @@ /** Core */ -#define PW_CORE_EVENT_INFO 0 -#define PW_CORE_EVENT_DONE 1 -#define PW_CORE_EVENT_PING 2 -#define PW_CORE_EVENT_ERROR 3 -#define PW_CORE_EVENT_REMOVE_ID 4 -#define PW_CORE_EVENT_BOUND_ID 5 -#define PW_CORE_EVENT_ADD_MEM 6 +#define PW_CORE_EVENT_INFO 0 +#define PW_CORE_EVENT_DONE 1 +#define PW_CORE_EVENT_PING 2 +#define PW_CORE_EVENT_ERROR 3 +#define PW_CORE_EVENT_REMOVE_ID 4 +#define PW_CORE_EVENT_BOUND_ID 5 +#define PW_CORE_EVENT_ADD_MEM 6 #define PW_CORE_EVENT_REMOVE_MEM 7 -#define PW_CORE_EVENT_NUM 8 +#define PW_CORE_EVENT_BOUND_PROPS 8 +#define PW_CORE_EVENT_NUM 9 /** \struct pw_core_events * \brief Core events */ struct pw_core_events { -#define PW_VERSION_CORE_EVENTS 0 +#define PW_VERSION_CORE_EVENTS 1 uint32_t version; /** @@ -188,6 +189,8 @@ * \param id the memory id to remove */ void (*remove_mem) (void *data, uint32_t id); + + void (*bound_props) (void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props); }; #define PW_CORE_METHOD_ADD_LISTENER 0
View file
pipewire-0.3.67.tar.gz/src/pipewire/filter.c -> pipewire-0.3.68.tar.gz/src/pipewire/filter.c
Changed
@@ -144,7 +144,6 @@ unsigned int disconnecting:1; unsigned int disconnect_core:1; - unsigned int subscribe:1; unsigned int draining:1; unsigned int allow_mlock:1; unsigned int warn_mlock:1; @@ -1133,10 +1132,12 @@ PW_FILTER_STATE_ERROR, message); } -static void proxy_bound(void *_data, uint32_t global_id) +static void proxy_bound_props(void *_data, uint32_t global_id, const struct spa_dict *props) { struct pw_filter *filter = _data; filter->node_id = global_id; + if (props) + pw_properties_update(filter->properties, props); filter_set_state(filter, PW_FILTER_STATE_PAUSED, NULL); } @@ -1145,7 +1146,7 @@ .removed = proxy_removed, .destroy = proxy_destroy, .error = proxy_error, - .bound = proxy_bound, + .bound_props = proxy_bound_props, }; static void on_core_error(void *_data, uint32_t id, int seq, int res, const char *message) @@ -1191,6 +1192,8 @@ struct match match; int res; + ensure_loop(context->main_loop, return NULL); + impl = calloc(1, sizeof(struct filter)); if (impl == NULL) { res = -errno; @@ -1355,21 +1358,58 @@ return "invalid-state"; } +static int filter_disconnect(struct filter *impl) +{ + struct pw_filter *filter = &impl->this; + pw_log_debug("%p: disconnect", impl); + + if (impl->disconnecting) + return -EBUSY; + + impl->disconnecting = true; + + if (filter->proxy) { + pw_proxy_destroy(filter->proxy); + filter->proxy = NULL; + } + if (impl->disconnect_core) { + impl->disconnect_core = false; + spa_hook_remove(&filter->core_listener); + spa_list_remove(&filter->link); + pw_core_disconnect(filter->core); + filter->core = NULL; + } + return 0; +} + +static void free_port(struct filter *impl, struct port *port) +{ + spa_list_remove(&port->link); + spa_node_emit_port_info(&impl->hooks, port->direction, port->id, NULL); + pw_map_remove(&impl->portsport->direction, port->id); + clear_buffers(port); + clear_params(impl, port, SPA_ID_INVALID); + pw_properties_free(port->props); + free(port); +} + SPA_EXPORT void pw_filter_destroy(struct pw_filter *filter) { struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); struct port *p; + ensure_loop(impl->context->main_loop, return); + pw_log_debug("%p: destroy", filter); pw_filter_emit_destroy(filter); if (!impl->disconnecting) - pw_filter_disconnect(filter); + filter_disconnect(impl); spa_list_consume(p, &impl->port_list, link) - pw_filter_remove_port(p->user_data); + free_port(impl, p); if (filter->core) { spa_hook_remove(&filter->core_listener); @@ -1412,6 +1452,9 @@ void *data) { struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); + + ensure_loop(impl->context->main_loop); + spa_hook_list_append(&filter->listener_list, listener, events, data); if (events->process && impl->rt_callbacks.funcs == NULL) { impl->rt_callbacks = SPA_CALLBACKS_INIT(events, data); @@ -1460,6 +1503,8 @@ struct port *port = SPA_CONTAINER_OF(port_data, struct port, user_data); int changed = 0; + ensure_loop(impl->context->main_loop, return -EIO); + if (port_data) { changed = pw_properties_update(port->props, dict); port->info.props = &port->props->dict; @@ -1497,6 +1542,11 @@ uint32_t i; struct spa_dict_item items1; + ensure_loop(impl->context->main_loop, return -EIO); + + if (filter->proxy != NULL || filter->state != PW_FILTER_STATE_UNCONNECTED) + return -EBUSY; + pw_log_debug("%p: connect", filter); impl->flags = flags; @@ -1532,6 +1582,8 @@ } impl->disconnecting = false; + impl->draining = false; + impl->driving = false; filter_set_state(filter, PW_FILTER_STATE_CONNECTING, NULL); if (flags & PW_FILTER_FLAG_DRIVER) @@ -1585,22 +1637,8 @@ int pw_filter_disconnect(struct pw_filter *filter) { struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); - - pw_log_debug("%p: disconnect", filter); - impl->disconnecting = true; - - if (filter->proxy) { - pw_proxy_destroy(filter->proxy); - filter->proxy = NULL; - } - if (impl->disconnect_core) { - impl->disconnect_core = false; - spa_hook_remove(&filter->core_listener); - spa_list_remove(&filter->link); - pw_core_disconnect(filter->core); - filter->core = NULL; - } - return 0; + ensure_loop(impl->context->main_loop, return -EIO); + return filter_disconnect(impl); } static void add_port_params(struct filter *impl, struct port *port) @@ -1682,6 +1720,8 @@ struct port *p; const char *str; + ensure_loop(impl->context->main_loop, return NULL); + if (props == NULL) props = pw_properties_new(NULL, NULL); if (props == NULL) @@ -1738,22 +1778,14 @@ return NULL; } -static inline void free_port(struct filter *impl, struct port *port) -{ - spa_list_remove(&port->link); - spa_node_emit_port_info(&impl->hooks, port->direction, port->id, NULL); - pw_map_remove(&impl->portsport->direction, port->id); - clear_buffers(port); - clear_params(impl, port, SPA_ID_INVALID); - pw_properties_free(port->props); - free(port); -} - SPA_EXPORT int pw_filter_remove_port(void *port_data) { struct port *port = SPA_CONTAINER_OF(port_data, struct port, user_data); struct filter *impl = port->filter; + + ensure_loop(impl->context->main_loop, return -EIO); + free_port(impl, port); return 0;
View file
pipewire-0.3.67.tar.gz/src/pipewire/impl-core.c -> pipewire-0.3.68.tar.gz/src/pipewire/impl-core.c
Changed
@@ -5,9 +5,6 @@ #include "config.h" #include <unistd.h> -#ifndef ENODATA -#define ENODATA 9919 -#endif #include <spa/debug/types.h> #include <spa/utils/string.h> @@ -175,6 +172,8 @@ pw_log_debug("%p: hello %d from resource %p", context, version, resource); pw_map_for_each(&client->objects, destroy_resource, client); + resource->version = version; + pw_mempool_clear(client->pool); this->info.change_mask = PW_CORE_CHANGE_MASK_ALL;
View file
pipewire-0.3.67.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.68.tar.gz/src/pipewire/impl-link.c
Changed
@@ -1203,7 +1203,10 @@ /* passive means that this link does not make the nodes active */ str = pw_properties_get(properties, PW_KEY_LINK_PASSIVE); - this->passive = str ? spa_atob(str) : output->passive | input->passive; + this->passive = str ? spa_atob(str) : + (output->passive && input_node->can_suspend) || + (input->passive && output_node->can_suspend) || + (input->passive && output->passive); if (this->passive && str == NULL) pw_properties_set(properties, PW_KEY_LINK_PASSIVE, "true"); @@ -1256,10 +1259,9 @@ output_node, output->port_id, this->rt.out_mix.port.port_id, input_node, input->port_id, this->rt.in_mix.port.port_id); - if (asprintf(&this->name, "%d.%d -> %d.%d", + this->name = spa_aprintf("%d.%d -> %d.%d", output_node->info.id, output->port_id, - input_node->info.id, input->port_id) < 0) - this->name = NULL; + input_node->info.id, input->port_id); pw_log_info("(%s) (%s) -> (%s)", this->name, output_node->name, input_node->name); pw_impl_port_emit_link_added(output, this);
View file
pipewire-0.3.67.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.68.tar.gz/src/pipewire/impl-node.c
Changed
@@ -688,8 +688,9 @@ pw_log_debug("%p: set position %p", node, &node->rt.activation->position); node->rt.position = &node->rt.activation->position; - node->current_rate = node->rt.position->clock.rate; - node->current_quantum = node->rt.position->clock.duration; + node->target_rate = node->rt.position->clock.target_rate; + node->target_quantum = node->rt.position->clock.target_duration; + node->target_pending = false; } else if (node->driver) { pw_log_warn("%p: can't set position on driver", node); } @@ -804,8 +805,8 @@ pw_log_trace("%p: set position %p", node, &driver->rt.activation->position); node->rt.position = &driver->rt.activation->position; - node->current_rate = node->rt.position->clock.rate; - node->current_quantum = node->rt.position->clock.duration; + node->target_rate = node->rt.position->clock.target_rate; + node->target_quantum = node->rt.position->clock.target_duration; if (node->source.loop != NULL) { remove_node(node); @@ -841,14 +842,16 @@ remove_segment_owner(old, node->info.id); if (old != node && old->driving && driver->info.state < PW_NODE_STATE_RUNNING) { - driver->current_rate = old->current_rate; - driver->current_quantum = old->current_quantum; - driver->current_pending = true; - pw_log_info("move quantum:%"PRIu64" rate:%d (%s-%d -> %s-%d)", - driver->current_quantum, - driver->current_rate.denom, + pw_log_info("move quantum:%"PRIu64"->%"PRIu64" rate:%d->%d (%s-%d -> %s-%d)", + driver->target_quantum, + old->target_quantum, + driver->target_rate.denom, + old->target_rate.denom, old->name, old->info.id, driver->name, driver->info.id); + driver->target_rate = old->target_rate; + driver->target_quantum = old->target_quantum; + driver->target_pending = true; } was_driving = node->driving; node->driving = node->driver && driver == node; @@ -892,7 +895,7 @@ const char *str, *recalc_reason = NULL; struct spa_fraction frac; uint32_t value; - bool driver; + bool driver, trigger; if ((str = pw_properties_get(node->properties, PW_KEY_PRIORITY_DRIVER))) { node->priority_driver = pw_properties_parse_int(str); @@ -925,8 +928,14 @@ } /* not scheduled automatically so we add an additional required trigger */ - if (pw_properties_get_bool(node->properties, PW_KEY_NODE_TRIGGER, false)) - node->rt.activation->state0.required++; + trigger = pw_properties_get_bool(node->properties, PW_KEY_NODE_TRIGGER, false); + if (trigger != node->trigger) { + node->trigger = trigger; + if (trigger) + node->rt.activation->state0.required++; + else + node->rt.activation->state0.required--; + } /* group defines what nodes are scheduled together */ str = pw_properties_get(node->properties, PW_KEY_NODE_GROUP); @@ -947,6 +956,12 @@ recalc_reason = "link group changed"; } + if ((str = pw_properties_get(node->properties, PW_KEY_MEDIA_CLASS)) != NULL && + (strstr(str, "/Sink") != NULL || strstr(str, "/Source") != NULL)) { + node->can_suspend = true; + } else { + node->can_suspend = false; + } if ((str = pw_properties_get(node->properties, PW_KEY_NODE_PASSIVE)) == NULL) str = "false"; if (spa_streq(str, "out")) @@ -1009,11 +1024,16 @@ node->lock_rate = pw_properties_get_bool(node->properties, PW_KEY_NODE_LOCK_RATE, false); if ((str = pw_properties_get(node->properties, PW_KEY_NODE_FORCE_RATE))) { - if (spa_atou32(str, &value, 0) && - node->force_rate != value) { - node->force_rate = value; - node->stamp = ++context->stamp; - recalc_reason = "force rate changed"; + if (spa_atou32(str, &value, 0)) { + if (value == 0) + value = node->rate.denom; + if (node->force_rate != value) { + pw_log_info("(%s-%u) force-rate:%u -> %u", node->name, + node->info.id, node->force_rate, value); + node->force_rate = value; + node->stamp = ++context->stamp; + recalc_reason = "force rate changed"; + } } } @@ -1218,11 +1238,11 @@ uint32_t quantum = s->clock_force_quantum == 0 ? s->clock_quantum : s->clock_force_quantum; uint32_t rate = s->clock_force_rate == 0 ? s->clock_rate : s->clock_force_rate; - this->current_rate = SPA_FRACTION(1, rate); - this->current_quantum = quantum; + this->target_rate = SPA_FRACTION(1, rate); + this->target_quantum = quantum; - pos->clock.rate = this->current_rate; - pos->clock.duration = this->current_quantum; + pos->clock.rate = pos->clock.target_rate = this->target_rate; + pos->clock.duration = pos->clock.target_duration = this->target_quantum; pos->video.flags = SPA_IO_VIDEO_SIZE_VALID; pos->video.size = s->video_size; pos->video.stride = pos->video.size.width * 16; @@ -1681,10 +1701,15 @@ node->rt.target.signal_func(node->rt.target.data); } - if (node->current_pending) { - node->rt.position->clock.duration = node->current_quantum; - node->rt.position->clock.rate = node->current_rate; - node->current_pending = false; + /* This update is done too late, the driver should do this + * before calling the ready callback so that it can use the new target + * duration and rate to schedule the next update. We do this here to + * help drivers that don't support this yet */ + if (node->rt.position->clock.duration != node->rt.position->clock.target_duration || + node->rt.position->clock.rate.denom != node->rt.position->clock.target_rate.denom) { + pw_log_warn("driver %s did not update duration/rate", node->name); + node->rt.position->clock.duration = node->rt.position->clock.target_duration; + node->rt.position->clock.rate = node->rt.position->clock.target_rate; } sync_type = check_updates(node, &reposition_owner);
View file
pipewire-0.3.67.tar.gz/src/pipewire/impl-port.c -> pipewire-0.3.68.tar.gz/src/pipewire/impl-port.c
Changed
@@ -1574,9 +1574,12 @@ /* try dynamic data */ alloc_flags = PW_BUFFERS_FLAG_DYNAMIC; + if (SPA_FLAG_IS_SET(node->spa_flags, SPA_NODE_FLAG_ASYNC)) + alloc_flags |= PW_BUFFERS_FLAG_ASYNC; - pw_log_debug("%p: %d.%d negotiate %d buffers on node: %p", - port, port->direction, port->port_id, n_buffers, node->node); + pw_log_debug("%p: %d.%d negotiate %d buffers on node: %p flags:%08x", + port, port->direction, port->port_id, n_buffers, node->node, + alloc_flags); if (port->added) { pw_loop_invoke(node->data_loop, do_remove_port, SPA_ID_INVALID, NULL, 0, true, port);
View file
pipewire-0.3.67.tar.gz/src/pipewire/keys.h -> pipewire-0.3.68.tar.gz/src/pipewire/keys.h
Changed
@@ -149,7 +149,8 @@ #define PW_KEY_NODE_LOCK_RATE "node.lock-rate" /**< don't change rate when this node * is active */ #define PW_KEY_NODE_FORCE_RATE "node.force-rate" /**< force a rate while the node is - * active */ + * active. A value of 0 takes the denominator + * of node.rate */ #define PW_KEY_NODE_DONT_RECONNECT "node.dont-reconnect" /**< don't reconnect this node. The node is * initially linked to target.object or the
View file
pipewire-0.3.67.tar.gz/src/pipewire/loop.c -> pipewire-0.3.68.tar.gz/src/pipewire/loop.c
Changed
@@ -9,6 +9,7 @@ #include <spa/utils/result.h> #include <pipewire/pipewire.h> +#include <pipewire/private.h> #include <pipewire/loop.h> #include <pipewire/log.h> #include <pipewire/type.h> @@ -23,6 +24,9 @@ struct spa_handle *system_handle; struct spa_handle *loop_handle; + + void *user_data; + const struct pw_loop_callbacks *cb; }; /** \endcond */ @@ -140,3 +144,24 @@ pw_unload_spa_handle(impl->system_handle); free(impl); } + +void +pw_loop_set_callbacks(struct pw_loop *loop, const struct pw_loop_callbacks *cb, void *data) +{ + struct impl *impl = SPA_CONTAINER_OF(loop, struct impl, this); + + impl->user_data = data; + impl->cb = cb; +} + +SPA_EXPORT +int pw_loop_check(struct pw_loop *loop) +{ + struct impl *impl = SPA_CONTAINER_OF(loop, struct impl, this); + int res; + if (impl->cb && impl->cb->check) + res = impl->cb->check(impl->user_data, loop); + else + res = spa_loop_control_check(loop->control); + return res; +}
View file
pipewire-0.3.67.tar.gz/src/pipewire/private.h -> pipewire-0.3.68.tar.gz/src/pipewire/private.h
Changed
@@ -346,6 +346,7 @@ #define pw_core_resource_bound_id(r,...) pw_core_resource(r,bound_id,0,__VA_ARGS__) #define pw_core_resource_add_mem(r,...) pw_core_resource(r,add_mem,0,__VA_ARGS__) #define pw_core_resource_remove_mem(r,...) pw_core_resource(r,remove_mem,0,__VA_ARGS__) +#define pw_core_resource_bound_props(r,...) pw_core_resource(r,bound_props,1,__VA_ARGS__) static inline SPA_PRINTF_FUNC(5,0) void pw_core_resource_errorv(struct pw_resource *resource, uint32_t id, int seq, @@ -373,6 +374,29 @@ va_end(args); } +struct pw_loop_callbacks { +#define PW_VERSION_LOOP_CALLBACKS 0 + uint32_t version; + + int (*check) (void *data, struct pw_loop *loop); +}; + +void +pw_loop_set_callbacks(struct pw_loop *loop, const struct pw_loop_callbacks *cb, void *data); + +int pw_loop_check(struct pw_loop *loop); + +#define ensure_loop(loop,...) ({ \ + int res = pw_loop_check(loop); \ + if (res != 1) { \ + pw_log_warn("%s called from wrong context, check thread and locking: %s", \ + __func__, spa_strerror(res)); \ + fprintf(stderr, "*** %s called from wrong context, check thread and locking: %s\n",\ + __func__, spa_strerror(res)); \ + /* __VA_ARGS__ */ \ + } \ +}) + #define pw_context_driver_emit(c,m,v,...) spa_hook_list_call_simple(&c->driver_listener_list, struct pw_context_driver_events, m, v, ##__VA_ARGS__) #define pw_context_driver_emit_start(c,n) pw_context_driver_emit(c, start, 0, n) #define pw_context_driver_emit_xrun(c,n) pw_context_driver_emit(c, xrun, 0, n) @@ -695,12 +719,16 @@ unsigned int lock_quantum:1; /**< don't change graph quantum */ unsigned int lock_rate:1; /**< don't change graph rate */ unsigned int transport_sync:1; /**< supports transport sync */ - unsigned int current_pending:1; /**< a quantum/rate update is pending */ + unsigned int target_pending:1; /**< a quantum/rate update is pending */ unsigned int moved:1; /**< the node was moved drivers */ unsigned int added:1; /**< the node was add to graph */ unsigned int pause_on_idle:1; /**< Pause processing when IDLE */ unsigned int suspend_on_idle:1; unsigned int reconfigure:1; + unsigned int forced_rate:1; + unsigned int trigger:1; /**< has the TRIGGER property and needs an extra + * trigger to start processing. */ + unsigned int can_suspend:1; uint32_t port_user_data_size; /**< extra size for port user data */ @@ -748,10 +776,10 @@ struct ratelimit rate_limit; } rt; - struct spa_fraction current_rate; - uint64_t current_quantum; + struct spa_fraction target_rate; + uint64_t target_quantum; - void *user_data; /**< extra user data */ + void *user_data; /**< extra user data */ }; struct pw_impl_port_mix { @@ -965,6 +993,7 @@ #define pw_proxy_emit_removed(p) pw_proxy_emit(p, removed, 0) #define pw_proxy_emit_done(p,s) pw_proxy_emit(p, done, 0, s) #define pw_proxy_emit_error(p,s,r,m) pw_proxy_emit(p, error, 0, s, r, m) +#define pw_proxy_emit_bound_props(p,g,r) pw_proxy_emit(p, bound_props, 1, g, r) struct pw_proxy { struct spa_interface impl; /**< object implementation */
View file
pipewire-0.3.67.tar.gz/src/pipewire/proxy.h -> pipewire-0.3.68.tar.gz/src/pipewire/proxy.h
Changed
@@ -89,7 +89,7 @@ /** Proxy events, use \ref pw_proxy_add_listener */ struct pw_proxy_events { -#define PW_VERSION_PROXY_EVENTS 0 +#define PW_VERSION_PROXY_EVENTS 1 uint32_t version; /** The proxy is destroyed */ @@ -107,6 +107,8 @@ /** an error occurred on the proxy */ void (*error) (void *data, int seq, int res, const char *message); + + void (*bound_props) (void *data, uint32_t global_id, const struct spa_dict *props); }; /* Make a new proxy object. The id can be used to bind to a remote object and
View file
pipewire-0.3.67.tar.gz/src/pipewire/resource.c -> pipewire-0.3.68.tar.gz/src/pipewire/resource.c
Changed
@@ -194,9 +194,19 @@ struct pw_impl_client *client = resource->client; resource->bound_id = global_id; + if (client->core_resource != NULL) { - pw_log_debug("%p: %u global_id:%u", resource, resource->id, global_id); - pw_core_resource_bound_id(client->core_resource, resource->id, global_id); + struct pw_global *global = pw_map_lookup(&resource->context->globals, global_id); + const struct spa_dict *dict = global ? &global->properties->dict : NULL; + + pw_log_debug("%p: %u global_id:%u %d", resource, resource->id, global_id, + client->core_resource->version); + + if (client->core_resource->version >= 4) + pw_core_resource_bound_props(client->core_resource, resource->id, global_id, + dict); + else + pw_core_resource_bound_id(client->core_resource, resource->id, global_id); } return 0; }
View file
pipewire-0.3.67.tar.gz/src/pipewire/stream.c -> pipewire-0.3.68.tar.gz/src/pipewire/stream.c
Changed
@@ -17,6 +17,7 @@ #include <spa/pod/filter.h> #include <spa/pod/dynamic.h> #include <spa/debug/types.h> +#include <spa/debug/dict.h> #define PW_ENABLE_DEPRECATED @@ -1044,7 +1045,6 @@ impl->drained = false; io->buffer_id = b->id; res = io->status = SPA_STATUS_HAVE_DATA; - pw_log_trace_fp("%p: pop %d %p", stream, b->id, io); /* we have a buffer, if we are not rt and don't follow * any rate matching and there are no more * buffers queued and there is a buffer to dequeue, ask for @@ -1055,6 +1055,8 @@ ask_more = !impl->process_rt && impl->rate_match == NULL && queue_is_empty(impl, &impl->queued) && !queue_is_empty(impl, &impl->dequeued); + pw_log_trace_fp("%p: pop %d %p ask_more:%u %p", stream, b->id, io, + ask_more, impl->rate_match); } else if (impl->draining || impl->drained) { impl->draining = true; impl->drained = true; @@ -1137,10 +1139,12 @@ PW_STREAM_STATE_ERROR, message); } -static void proxy_bound(void *data, uint32_t global_id) +static void proxy_bound_props(void *data, uint32_t global_id, const struct spa_dict *props) { struct pw_stream *stream = data; stream->node_id = global_id; + if (props) + pw_properties_update(stream->properties, props); stream_set_state(stream, PW_STREAM_STATE_PAUSED, NULL); } @@ -1149,7 +1153,7 @@ .removed = proxy_removed, .destroy = proxy_destroy, .error = proxy_error, - .bound = proxy_bound, + .bound_props = proxy_bound_props, }; static struct control *find_control(struct pw_stream *stream, uint32_t id) @@ -1332,6 +1336,14 @@ return 0; } +static void node_event_destroy(void *data) +{ + struct pw_stream *stream = data; + struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); + spa_hook_remove(&stream->node_listener); + impl->node = NULL; +} + static void node_event_info(void *data, const struct pw_node_info *info) { struct pw_stream *stream = data; @@ -1359,6 +1371,7 @@ static const struct pw_impl_node_events node_events = { PW_VERSION_IMPL_NODE_EVENTS, + .destroy = node_event_destroy, .info_changed = node_event_info, }; @@ -1423,6 +1436,8 @@ struct match match; int res; + ensure_loop(context->main_loop, return NULL); + impl = calloc(1, sizeof(struct stream)); if (impl == NULL) { res = -errno; @@ -1596,18 +1611,52 @@ return "invalid-state"; } +static int stream_disconnect(struct stream *impl) +{ + struct pw_stream *stream = &impl->this; + + pw_log_debug("%p: disconnect", stream); + + if (impl->disconnecting) + return -EBUSY; + + impl->disconnecting = true; + + if (impl->node) + pw_impl_node_set_active(impl->node, false); + + if (stream->proxy) { + pw_proxy_destroy(stream->proxy); + stream->proxy = NULL; + } + + if (impl->node) + pw_impl_node_destroy(impl->node); + + if (impl->disconnect_core) { + impl->disconnect_core = false; + spa_hook_remove(&stream->core_listener); + spa_list_remove(&stream->link); + pw_core_disconnect(stream->core); + stream->core = NULL; + } + return 0; +} + SPA_EXPORT void pw_stream_destroy(struct pw_stream *stream) { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); struct control *c; + ensure_loop(impl->context->main_loop, return); + pw_log_debug("%p: destroy", stream); pw_stream_emit_destroy(stream); if (!impl->disconnecting) - pw_stream_disconnect(stream); + stream_disconnect(impl); if (stream->core) { spa_hook_remove(&stream->core_listener); @@ -1656,6 +1705,9 @@ void *data) { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); + + ensure_loop(impl->context->main_loop); + spa_hook_list_append(&stream->listener_list, listener, events, data); if (events->process && impl->rt_callbacks.funcs == NULL) { @@ -1692,6 +1744,8 @@ int changed, res = 0; struct match match; + ensure_loop(impl->context->main_loop, return -EIO); + changed = pw_properties_update(stream->properties, dict); if (!changed) return 0; @@ -1786,8 +1840,7 @@ } } -SPA_EXPORT -int +SPA_EXPORT int pw_stream_connect(struct pw_stream *stream, enum pw_direction direction, uint32_t target_id, @@ -1802,7 +1855,13 @@ uint32_t i; int res; + ensure_loop(impl->context->main_loop, return -EIO); + pw_log_debug("%p: connect target:%d", stream, target_id); + + if (impl->node != NULL || stream->state != PW_STREAM_STATE_UNCONNECTED) + return -EBUSY; + impl->direction = direction == PW_DIRECTION_INPUT ? SPA_DIRECTION_INPUT : SPA_DIRECTION_OUTPUT; impl->flags = flags; @@ -1879,6 +1938,11 @@ return res; impl->disconnecting = false; + impl->drained = false; + impl->draining = false; + impl->driving = false; + impl->trigger = false; + impl->using_trigger = false; stream_set_state(stream, PW_STREAM_STATE_CONNECTING, NULL); if ((str = getenv("PIPEWIRE_NODE")) != NULL) @@ -2022,40 +2086,18 @@ int pw_stream_disconnect(struct pw_stream *stream) { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); - - pw_log_debug("%p: disconnect", stream); - - if (impl->disconnecting) - return 0; - - impl->disconnecting = true;
View file
pipewire-0.3.67.tar.gz/src/pipewire/thread-loop.c -> pipewire-0.3.68.tar.gz/src/pipewire/thread-loop.c
Changed
@@ -9,6 +9,7 @@ #include <spa/support/thread.h> #include <spa/utils/result.h> +#include "private.h" #include "log.h" #include "thread.h" #include "thread-loop.h" @@ -32,6 +33,7 @@ pthread_cond_t accept_cond; pthread_t thread; + int recurse; struct spa_hook hook; @@ -44,22 +46,71 @@ }; /** \endcond */ -static void before(void *data) +static int do_lock(struct pw_thread_loop *this) +{ + int res; + if ((res = pthread_mutex_lock(&this->lock)) != 0) + pw_log_error("%p: thread:%lu: %s", this, pthread_self(), strerror(res)); + else + this->recurse++; + return -res; +} + +static int do_unlock(struct pw_thread_loop *this) +{ + int res; + spa_return_val_if_fail(this->recurse > 0, -EIO); + this->recurse--; + if ((res = pthread_mutex_unlock(&this->lock)) != 0) { + pw_log_error("%p: thread:%lu: %s", this, pthread_self(), strerror(res)); + this->recurse++; + } + return -res; +} + +static void impl_before(void *data) { struct pw_thread_loop *this = data; - pthread_mutex_unlock(&this->lock); + do_unlock(this); } -static void after(void *data) +static void impl_after(void *data) { struct pw_thread_loop *this = data; - pthread_mutex_lock(&this->lock); + do_lock(this); } static const struct spa_loop_control_hooks impl_hooks = { SPA_VERSION_LOOP_CONTROL_HOOKS, - before, - after, + .before = impl_before, + .after = impl_after, +}; + +static int impl_check(void *data, struct pw_loop *loop) +{ + struct pw_thread_loop *this = data; + int res; + + /* we are in the thread running the loop */ + if (spa_loop_control_check(this->loop->control) == 1) + return 1; + + /* if lock taken by something else, error */ + if ((res = pthread_mutex_trylock(&this->lock)) != 0) { + pw_log_debug("%p: thread:%lu: %s", this, pthread_self(), strerror(res)); + return -res; + } + /* we could take the lock, check if we actually locked it somewhere */ + res = this->recurse > 0 ? 1 : -EPERM; + if (res < 0) + pw_log_debug("%p: thread:%lu: recurse:%d", this, pthread_self(), this->recurse); + pthread_mutex_unlock(&this->lock); + return res; +} + +static const struct pw_loop_callbacks impl_callbacks = { + PW_VERSION_LOOP_CALLBACKS, + .check = impl_check, }; static void do_stop(void *data, uint64_t count) @@ -121,6 +172,7 @@ goto clean_acceptcond; } + pw_loop_set_callbacks(loop, &impl_callbacks, this); pw_loop_add_hook(loop, &this->hook, &impl_hooks, this); return this; @@ -225,7 +277,7 @@ struct pw_thread_loop *this = user_data; int res; - pthread_mutex_lock(&this->lock); + do_lock(this); pw_log_debug("%p: enter thread", this); pw_loop_enter(this->loop); @@ -239,7 +291,7 @@ } pw_log_debug("%p: leave thread", this); pw_loop_leave(this->loop); - pthread_mutex_unlock(&this->lock); + do_unlock(this); return NULL; } @@ -306,7 +358,7 @@ SPA_EXPORT void pw_thread_loop_lock(struct pw_thread_loop *loop) { - pthread_mutex_lock(&loop->lock); + do_lock(loop); pw_log_trace("%p", loop); } @@ -319,7 +371,7 @@ void pw_thread_loop_unlock(struct pw_thread_loop *loop) { pw_log_trace("%p", loop); - pthread_mutex_unlock(&loop->lock); + do_unlock(loop); } /** Signal the thread @@ -342,8 +394,11 @@ if (wait_for_accept) { loop->n_waiting_for_accept++; - while (loop->n_waiting_for_accept > 0) - pthread_cond_wait(&loop->accept_cond, &loop->lock); + while (loop->n_waiting_for_accept > 0) { + int res; + if ((res = pthread_cond_wait(&loop->accept_cond, &loop->lock)) != 0) + pw_log_error("%p: thread:%lu: %s", loop, pthread_self(), strerror(res)); + } } } @@ -355,9 +410,15 @@ SPA_EXPORT void pw_thread_loop_wait(struct pw_thread_loop *loop) { - pw_log_trace("%p, waiting %d", loop, loop->n_waiting); + int res; + + pw_log_trace("%p, waiting:%d recurse:%d", loop, loop->n_waiting, loop->recurse); + spa_return_if_fail(loop->recurse > 0); loop->n_waiting++; - pthread_cond_wait(&loop->cond, &loop->lock); + loop->recurse--; + if ((res = pthread_cond_wait(&loop->cond, &loop->lock)) != 0) + pw_log_error("%p: thread:%lu: %s", loop, pthread_self(), strerror(res)); + loop->recurse++; loop->n_waiting--; pw_log_trace("%p, waiting done %d", loop, loop->n_waiting); } @@ -418,8 +479,11 @@ int pw_thread_loop_timed_wait_full(struct pw_thread_loop *loop, const struct timespec *abstime) { int ret; + spa_return_val_if_fail(loop->recurse > 0, -EIO); loop->n_waiting++; + loop->recurse--; ret = pthread_cond_timedwait(&loop->cond, &loop->lock, abstime); + loop->recurse++; loop->n_waiting--; return -ret; } @@ -445,5 +509,5 @@ SPA_EXPORT bool pw_thread_loop_in_thread(struct pw_thread_loop *loop) { - return loop->running && pthread_self() == loop->thread; + return loop->running && pthread_equal(loop->thread, pthread_self()); }
View file
pipewire-0.3.67.tar.gz/src/pipewire/utils.h -> pipewire-0.3.68.tar.gz/src/pipewire/utils.h
Changed
@@ -16,6 +16,10 @@ # include <sys/mount.h> #endif +#ifndef ENODATA +#define ENODATA 9919 +#endif + #include <spa/utils/defs.h> #include <spa/pod/pod.h>
View file
pipewire-0.3.67.tar.gz/src/tests/test-interfaces.c -> pipewire-0.3.68.tar.gz/src/tests/test-interfaces.c
Changed
@@ -42,6 +42,7 @@ void (*bound_id) (void *data, uint32_t id, uint32_t global_id); void (*add_mem) (void *data, uint32_t id, uint32_t type, int fd, uint32_t flags); void (*remove_mem) (void *data, uint32_t id); + void (*bound_props) (void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props); } events = { PW_VERSION_CORE_EVENTS, }; struct pw_core_events e; @@ -68,7 +69,8 @@ TEST_FUNC(e, events, bound_id); TEST_FUNC(e, events, add_mem); TEST_FUNC(e, events, remove_mem); - spa_assert_se(PW_VERSION_CORE_EVENTS == 0); + TEST_FUNC(e, events, bound_props); + spa_assert_se(PW_VERSION_CORE_EVENTS == 1); spa_assert_se(sizeof(e) == sizeof(events)); }
View file
pipewire-0.3.67.tar.gz/src/tools/meson.build -> pipewire-0.3.68.tar.gz/src/tools/meson.build
Changed
@@ -58,16 +58,26 @@ 'pw-encplay', - executable('pw-cat', + pw_cat = executable('pw-cat', pwcat_sources, install: true, dependencies : pwcat_deps, pipewire_dep, mathlib, ) foreach alias : pwcat_aliases - dst = pipewire_bindir / alias - cmd = 'ln -fs @0@ $DESTDIR@1@'.format('pw-cat', dst) - meson.add_install_script('sh', '-c', cmd) + custom_target( + alias, + build_by_default: true, + install: false, + command: ln, '-sf', meson.project_build_root() + '/@INPUT@', '@OUTPUT@', + input: pw_cat, + output: alias, + ) + install_symlink( + alias, + pointing_to: pw_cat.name(), + install_dir: pipewire_bindir, + ) endforeach elif not sndfile_dep.found() and get_option('pw-cat').enabled() error('pw-cat is enabled but required dependency `sndfile` was not found.')
View file
pipewire-0.3.67.tar.gz/src/tools/pw-cli.c -> pipewire-0.3.68.tar.gz/src/tools/pw-cli.c
Changed
@@ -1502,7 +1502,7 @@ return global_port_found; } -static void create_link_with_properties(struct data *data, struct pw_properties *props) +static void create_link_with_properties(struct data *data, const struct pw_properties *props) { struct remote_data *rd = data->current; uint32_t id; @@ -1534,6 +1534,7 @@ char *a5; int n; struct pw_properties *props = NULL; + bool res = false; n = pw_split_ip(args, WHITESPACE, 5, a); if (n < 4) { @@ -1562,12 +1563,12 @@ global_out = find_global(rd, a0); if (global_out == NULL) { *error = spa_aprintf("%s: unknown global '%s'", cmd, a0); - return false; + goto done; } global_in = find_global(rd, a2); if (global_in == NULL) { *error = spa_aprintf("%s: unknown global '%s'", cmd, a2); - return false; + goto done; } pd_out = pw_proxy_get_user_data(global_out->proxy); @@ -1578,7 +1579,7 @@ if (n_output_ports != n_input_ports) { *error = spa_aprintf("%s: Number of ports don't match (%u != %u)", cmd, n_output_ports, n_input_ports); - return false; + goto done; } for (uint32_t i = 0; i < n_output_ports; i++) { @@ -1601,9 +1602,12 @@ } else create_link_with_properties(data, props); + res = true; + +done: pw_properties_free(props); - return true; + return res; } static bool do_export_node(struct data *data, const char *cmd, char *args, char **error) @@ -2336,8 +2340,10 @@ fprintf(stderr, "Error: \"%s\"\n", error); free(error); } - data.current->prompt_pending = pw_core_sync(data.current->core, 0, 0); - while (!data.quit && data.current) { + if (data.current != NULL) + data.current->prompt_pending = pw_core_sync(data.current->core, 0, 0); + + while (!data.quit && data.current != NULL) { pw_main_loop_run(data.loop); if (!monitor) break;
View file
pipewire-0.3.67.tar.gz/src/tools/pw-top.c -> pipewire-0.3.68.tar.gz/src/tools/pw-top.c
Changed
@@ -171,7 +171,8 @@ { uint32_t media_type, media_subtype; - spa_format_parse(param, &media_type, &media_subtype); + if (spa_format_parse(param, &media_type, &media_subtype) < 0) + goto done; switch(media_type) { case SPA_MEDIA_TYPE_audio:
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
.