Changes of Revision 25

pipewire-aptx.changes Changed
x
 
1
@@ -1,4 +1,9 @@
2
 -------------------------------------------------------------------
3
+Sat Apr  8 17:49:24 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 0.3.68
6
+
7
+-------------------------------------------------------------------
8
 Sat Mar 18 12:35:35 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
9
 
10
 - Update to version 0.3.67
11
pipewire-aptx.spec Changed
10
 
1
@@ -7,7 +7,7 @@
2
 %define soversion 0_2
3
 
4
 Name:           pipewire-aptx
5
-Version:        0.3.67
6
+Version:        0.3.68
7
 Release:        0
8
 Summary:        PipeWire Bluetooth aptX codec plugin
9
 License:        MIT
10
pipewire-0.3.67.tar.gz/.cirrus.yml Deleted
26
 
1
@@ -1,24 +0,0 @@
2
-task:
3
-  freebsd_instance:
4
-    matrix:
5
-      - image_family: freebsd-13-1-snap
6
-  env:
7
-    # /usr/ports/Mk/Uses/localbase.mk localbase:ldflags
8
-    LOCALBASE: /usr/local
9
-    CFLAGS: -isystem $LOCALBASE/include
10
-    CPPFLAGS: $CFLAGS
11
-    CXXFLAGS: $CFLAGS
12
-    LDFLAGS: -L$LOCALBASE/lib
13
-  deps_script:
14
-    - sed -i.bak -e 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
15
-    - 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
16
-    - sysrc dbus_enable=YES
17
-    - service dbus restart
18
-  build_script:
19
-    - mkdir build
20
-    - cd build
21
-    - 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 ..
22
-    - ninja
23
-  test_script:
24
-    - cd build
25
-    - ninja test
26
pipewire-0.3.67.tar.gz/.gitlab-ci.yml -> pipewire-0.3.68.tar.gz/.gitlab-ci.yml Changed
133
 
1
@@ -25,7 +25,7 @@
2
 .fedora:
3
   variables:
4
     # Update this tag when you want to trigger a rebuild
5
-    FDO_DISTRIBUTION_TAG: '2023-01-18.0'
6
+    FDO_DISTRIBUTION_TAG: '2023-03-09.0'
7
     FDO_DISTRIBUTION_VERSION: '35'
8
     FDO_DISTRIBUTION_PACKAGES: >-
9
       alsa-lib-devel
10
@@ -44,10 +44,12 @@
11
       gstreamer1-devel
12
       gstreamer1-plugins-base-devel
13
       jack-audio-connection-kit-devel
14
+      libasan
15
       libcanberra-devel
16
       libldac-devel
17
       libmysofa-devel
18
       libsndfile-devel
19
+      libubsan
20
       libusb-devel
21
       lilv-devel
22
       libv4l-devel
23
@@ -172,15 +174,15 @@
24
     - export XDG_RUNTIME_DIR="$(mktemp -p $PWD -d xdg-runtime-XXXXXX)"
25
     - |
26
       if  -n "$FDO_CI_CONCURRENT" ; then
27
-         NINJA_ARGS="-j$FDO_CI_CONCURRENT $NINJA_ARGS"
28
-         export NINJA_ARGS
29
+         COMPILE_ARGS="-j$FDO_CI_CONCURRENT"
30
+         export COMPILE_ARGS
31
       fi
32
   script:
33
     - echo "Building with meson options $MESON_OPTIONS"
34
-    - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS
35
-    - ninja $NINJA_ARGS -C "$BUILD_DIR"
36
-    - ninja $NINJA_ARGS -C "$BUILD_DIR" test
37
-    - ninja $NINJA_ARGS -C "$BUILD_DIR" install
38
+    - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
39
+    - meson compile -C "$BUILD_DIR" $COMPILE_ARGS
40
+    - meson test -C "$BUILD_DIR" --no-rebuild
41
+    - meson install -C "$BUILD_DIR" --no-rebuild
42
   artifacts:
43
     name: pipewire-$CI_COMMIT_SHA
44
     when: always
45
@@ -312,9 +314,18 @@
46
         MESON_OPTION_VALUE: enabled, disabled
47
   script:
48
     - echo "Building with -D$MESON_OPTION=$MESON_OPTION_VALUE"
49
-    - meson "$BUILD_DIR" . --prefix="$PREFIX" "-D$MESON_OPTION=$MESON_OPTION_VALUE" -Dsession-managers=
50
-    - ninja $NINJA_ARGS -C "$BUILD_DIR"
51
-    - ninja $NINJA_ARGS -C "$BUILD_DIR" test
52
+    - meson setup "$BUILD_DIR" --prefix="$PREFIX" "-D$MESON_OPTION=$MESON_OPTION_VALUE" -Dsession-managers=
53
+    - meson compile -C "$BUILD_DIR" $COMPILE_ARGS
54
+    - meson test -C "$BUILD_DIR" --no-rebuild
55
+
56
+build_with_asan_ubsan:
57
+  extends:
58
+    - .build_on_fedora
59
+  script:
60
+    - echo "Building with ASan and UBSan"
61
+    - meson setup "$BUILD_DIR" --prefix="$PREFIX" -D debug=true -D optimization=g -D b_sanitize=address,undefined -D session-managers=
62
+    - meson compile -C "$BUILD_DIR" $COMPILE_ARGS
63
+    - meson test -C "$BUILD_DIR" --no-rebuild
64
 
65
 # A release build with NDEBUG, all options on auto() but tests explicitly
66
 # enabled. This should show issues with tests failing due to different
67
@@ -333,9 +344,9 @@
68
     - .build_on_fedora
69
   script:
70
     - echo "Building with meson options $MESON_OPTIONS"
71
-    - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS
72
-    - ninja $NINJA_ARGS -C "$BUILD_DIR"
73
-    - ninja $NINJA_ARGS -C "$BUILD_DIR" install
74
+    - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
75
+    - meson compile -C "$BUILD_DIR" $COMPILE_ARGS
76
+    - meson install -C "$BUILD_DIR" --no-rebuild
77
   variables:
78
     MESON_OPTIONS: "-Dsession-managers=$SESSION_MANAGERS"
79
   parallel:
80
@@ -349,9 +360,9 @@
81
   script:
82
     - pip3 install --upgrade --pre meson
83
     - echo "Building with meson options $MESON_OPTIONS"
84
-    - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS
85
-    - ninja $NINJA_ARGS -C "$BUILD_DIR"
86
-    - ninja $NINJA_ARGS -C "$BUILD_DIR" install
87
+    - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
88
+    - meson compile -C "$BUILD_DIR" $COMPILE_ARGS
89
+    - meson install -C "$BUILD_DIR" --no-rebuild
90
   variables:
91
     MESON_OPTIONS: "-Dsession-managers=wireplumber,media-session"
92
   allow_failure: true
93
@@ -366,9 +377,9 @@
94
     - pip3 uninstall --yes meson
95
     - pip3 install "meson==$meson_version"
96
     - echo "Building with meson options $MESON_OPTIONS"
97
-    - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS
98
-    - ninja $NINJA_ARGS -C "$BUILD_DIR"
99
-    - ninja $NINJA_ARGS -C "$BUILD_DIR" install
100
+    - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
101
+    - meson compile -C "$BUILD_DIR" $COMPILE_ARGS
102
+    - meson install -C "$BUILD_DIR" --no-rebuild
103
   variables:
104
     MESON_OPTIONS: "-Dsession-managers="
105
 
106
@@ -377,7 +388,7 @@
107
     - .build_on_fedora
108
   script:
109
     - echo "Building with meson options $MESON_OPTIONS"
110
-    - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS
111
+    - meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
112
     - meson test -C "$BUILD_DIR" --setup=valgrind
113
   variables:
114
     MESON_OPTIONS: "-Dsession-managers="
115
@@ -391,7 +402,7 @@
116
   stage: analysis
117
   script:
118
     - export PATH=/opt/coverity/bin:$PATH
119
-    - meson "$BUILD_DIR" . --prefix="$PREFIX"
120
+    - meson setup "$BUILD_DIR" --prefix="$PREFIX"
121
         -Ddocs=disabled
122
         -Dbluez5-backend-hsphfpd=enabled
123
         -Daudiotestsrc=enabled
124
@@ -410,7 +421,7 @@
125
         --xml-option=append_arg@C:"replace/GLIB_(DEPRECATED|AVAILABLE)_ENUMERATOR_IN_\d_\d\d(_FOR\(\w+\)|)\s+=/ ="
126
         --xml-option=append_arg@C:--ppp_translator
127
         --xml-option=append_arg@C:"replace/(__has_builtin|_GLIBCXX_HAS_BUILTIN)\(\w+\)/1"
128
-    - cov-build --dir cov-int --config coverity_conf.xml ninja $NINJA_ARGS -C "$BUILD_DIR"
129
+    - cov-build --dir cov-int --config coverity_conf.xml meson compile -C "$BUILD_DIR" $COMPILE_ARGS
130
     - tar czf cov-int.tar.gz cov-int
131
     - curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
132
         --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
133
pipewire-0.3.67.tar.gz/NEWS -> pipewire-0.3.68.tar.gz/NEWS Changed
155
 
1
@@ -1,3 +1,143 @@
2
+# PipeWire 0.3.68 (2023-04-06)
3
+
4
+This is a bugfix release that is API and ABI compatible with previous
5
+0.3.x releases.
6
+
7
+This release contains a huge number of changes, some of which might cause
8
+regressions. Please report anything that seems to fail after the upgrade.
9
+UCM devices in particular might have changed names, profiles and ports that
10
+might require changes in custom scripts.
11
+
12
+## Highlights
13
+  - Symbolic links to the pipewire binary are now used instead of recompiling
14
+    the same binary multiple times.
15
+  - Changes to the graph scheduler related to quantum/rate updates and
16
+    calculation of the node states. Things should start and switch between
17
+    quantums and rates more smoothly now and especially virtual devices should
18
+    now only run when required.
19
+  - A new RTP session module was added. This uses the Apple MIDI protocol
20
+    to configure low-latency bidirectional MIDI (and with a PipeWire specific
21
+    extension, also audio) between machines. OPUS encoding was added to the
22
+    RTP formats. The SAP module was separated from the rtp-sink/source module
23
+    to make it more usable.
24
+  - A new runtime debug property was added to all streams and nodes to trigger
25
+    a save of the raw samples to a wav file. Support for this has also been
26
+    added to the echo-canceler to debug potential issues.
27
+  - Module pulse-tunnel has improved rate matching and synchronization
28
+    support. It should also not drift anymore for capture devices.
29
+  - The link-factory now ignores by default the link.passive property. This means
30
+    that tools like pw-link or jack clients and wireplumber can't make passive
31
+    links anymore. The reason is that there is now much more advanced logic in
32
+    PipeWire itself to handle passive links based on node and port properties.
33
+  - The RAOP sink was ported to new OpenSSL functions. Digest passwords are
34
+    handled correctly now and support for more devices was added.
35
+  - The ACP code was updated with new PulseAudio UCM code: "Create multiple
36
+    profiles per verb for conflicting devices". This might change the names
37
+    of devices, profiles and ports so scripts might need to be updated.
38
+  - Upmixing is disabled again by default. We now ship config files that
39
+    distros can install to enable upmixing again. The reason being that PipeWire
40
+    should not apply fancy DSP processing to audio by default.
41
+  - Many cleanups and bugfixes, including some crashes and memory corruption
42
+    bugs.
43
+
44
+## PipeWire
45
+  - Various FreeBSD compilation fixes.
46
+  - Don't crash when calling _connect twice in stream/filter. (#3091)
47
+  - Links are now installed instead of compiling the pipewire binary
48
+    multiple times.
49
+  - There is now a new core event bound_props that augments the bound_id event
50
+    with the global properties. This can be used to get the global.serial among
51
+    other global properties. It also makes it possible in the future to let the
52
+    server allocate unique names or uuids.
53
+  - Fix a bug where the server could go into an infinite reconfigure loop when
54
+    the samplerate of a driver would change.
55
+  - When a samplerate was forced, restore the previous best samplerate when the
56
+    samplerate is no longer forced. (#2133)
57
+  - Rework how the states of the nodes in the graph are calculated. A more
58
+    refined algorithm is now used that only runs nodes that need to run.
59
+  - Rework how the quantum change is applied to the graph. Drivers are now
60
+    responsible for using the new updated rate/quantum before starting a new
61
+    cycle. This avoids starting a cycle with an old quantum first.
62
+  - pw-stream and pw-filter will now ensure that the Trigger event is called
63
+    from the main thread.
64
+  - node.force-rate=0 will now force the node.rate on the graph, forcefully
65
+    switching the hardware into the new rate if possible. (#3026)
66
+  - Additional checks were added to the thread-loop to check locking order.
67
+  - Additional checks were added to pw-stream and pw-filter to check if methods
68
+    are called from the right thread context.
69
+
70
+## modules
71
+  - A new RTP session module was added. This uses the Apple MIDI protocol
72
+    to configure bidirectional MIDI (or audio) between machines.
73
+  - SAP support was removed from module-rtp-source and module-rtp-sink and
74
+    moved to a separate module. This makes it possible to use the RTP modules
75
+    without SAP support as well.
76
+  - The echo-cancel module now has support to save the signals to a wav
77
+    file for debugging purposes.
78
+  - The RTP modules now have support for the OPUS codec.
79
+  - The RAOP module was ported to new openssl encryption functions and handles
80
+    digest passwords correctly now.
81
+  - module-raop-discover now has match rules to be able to select the streams
82
+    and set properties.
83
+  - Module pulse-tunnel has improved rate matching and synchronization
84
+    support. (#3093)
85
+  - Fix potential memory corruption and infinite loops because
86
+    module-pulse-tunnel was unloaded from the wrong thread. 
87
+  - The link-factory now ignores by default the link.passive property. This means
88
+    that tools like pw-link or jack clients and wireplumber can't make passive
89
+    links anymore. The reason is that there is now much more advanced logic in
90
+    PipeWire itself to handle passive links based on node and port properties.
91
+  - module-echo-cancel will now clear its buffers after a suspend to avoid
92
+    playing stray samples.
93
+  - module-raop-sink will now handle 0 timing_port replies. (#3133)
94
+
95
+## SPA
96
+  - The adapter module now has support for saving the raw audio to a wav
97
+    file for debugging purposes.
98
+  - The ACP code was updated with new PulseAudio UCM code: "Create multiple
99
+    profiles per verb for conflicting devices". This might change the names
100
+    of devices, profiles and ports so scripts might need to be updated.
101
+  - Upmixing was disabled again by default. We now ship config files that
102
+    distros can install to enable upmixing again. (#3081)
103
+  - audioadapter and audioconvert have seen improvements in the experimental
104
+    non-DSP/passthrough mode.
105
+  - Fix a potential race where the dummy drivers could fail to stop a timer
106
+    and cause endless warnings in the logs.
107
+  - The ALSA plugin has experimental support for IRQ based scheduling. This
108
+    should decrease latency for some (mostly USB) drivers. This should bring
109
+    latency within JACK latency. More work on this will be done before the
110
+    1.0 release later this year.
111
+  - Audioconvert now has support for volume ramping. (#3046)
112
+  - A new loop method was added the check if a thread is currently running the
113
+    loop.
114
+  - channelmix.disable and resample.disable now generate an error when true
115
+    and channelmixing or resampling is required in the converter.
116
+
117
+## Bluetooth
118
+  - Fix a crash in some cases when a device was disconnected.
119
+  - Support async transport state changes. This avoids some lockups when the
120
+    bluetooth backend is having issues. (#3023)
121
+  - Align BAP sinks. This improves synchronization between earpieces.
122
+
123
+## ALSA
124
+  - Improve properties in pw-top and pavucontrol.
125
+
126
+## pulse-server
127
+  - Improve error handling from pulse-tunnel.
128
+  - Generate silence correctly for unsigned formats as well.
129
+  - Review buffer params. The streams should now just work with 1 or 2
130
+    buffers.
131
+  - module-rtp-send and module-rtp-recv now have support for the OPUS codec.
132
+
133
+# JACK
134
+  - Make sure we don't call any callbacks anymore when deactivating. (#2781)
135
+
136
+## GStreamer
137
+  - Sort the device by priority in deviceprovider. (#3072)
138
+
139
+
140
+Older versions:
141
+
142
 # PipeWire 0.3.67 (2023-03-09)
143
 
144
 This is a bugfix release that is API and ABI compatible with previous
145
@@ -92,9 +232,6 @@
146
   - The metadata plane count is now handled correctly in more cases.
147
   - Stream errors are now handled correctly to stop the GStreamer elements.
148
 
149
-Older versions:
150
-
151
-
152
 # PipeWire 0.3.66 (2023-02-16)
153
 
154
 This is a bugfix release that is API and ABI compatible with previous
155
pipewire-0.3.67.tar.gz/doc/index.dox -> pipewire-0.3.68.tar.gz/doc/index.dox Changed
8
 
1
@@ -41,5 +41,6 @@
2
 - PipeWire Wikipedia(https://en.wikipedia.org/wiki/PipeWire)
3
 - Bluetooth, PipeWire and Whatsapp calls(https://gjhenrique.com/pipewire.html)
4
 - Intoduction to PipeWire(https://bootlin.com/blog/an-introduction-to-pipewire/)
5
+- A custom PipeWire node(https://bootlin.com/blog/a-custom-pipewire-node/)
6
 
7
 */
8
pipewire-0.3.67.tar.gz/doc/pipewire-modules.dox -> pipewire-0.3.68.tar.gz/doc/pipewire-modules.dox Changed
12
 
1
@@ -74,8 +74,10 @@
2
 - \subpage page_module_raop_discover
3
 - \subpage page_module_roc_sink
4
 - \subpage page_module_roc_source
5
+- \subpage page_module_rtp_sap
6
 - \subpage page_module_rtp_sink
7
 - \subpage page_module_rtp_source
8
+- \subpage page_module_rtp_session
9
 - \subpage page_module_rt
10
 - \subpage page_module_session_manager
11
 - \subpage page_module_x11_bell
12
pipewire-0.3.67.tar.gz/meson.build -> pipewire-0.3.68.tar.gz/meson.build Changed
22
 
1
@@ -1,7 +1,7 @@
2
 project('pipewire', 'c' ,
3
-  version : '0.3.67',
4
+  version : '0.3.68',
5
   license :  'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ,
6
-  meson_version : '>= 0.59.0',
7
+  meson_version : '>= 0.61.1',
8
   default_options :  'warning_level=3',
9
                       'c_std=gnu11',
10
                       'cpp_std=c++17',
11
@@ -282,6 +282,10 @@
12
 endif
13
 cdata.set('HAVE_PW_CAT_FFMPEG_INTEGRATION', pw_cat_ffmpeg.allowed())
14
 
15
+opus_dep = dependency('opus', required : get_option('opus'))
16
+summary({'opus (Bluetooth, RTP)': opus_dep.found()}, bool_yn: true, section: 'Misc dependencies')
17
+cdata.set('HAVE_OPUS', opus_dep.found())
18
+
19
 summary({'readline (for pw-cli)': readline_dep.found()}, bool_yn: true, section: 'Misc dependencies')
20
 cdata.set('HAVE_READLINE', readline_dep.found())
21
 ncurses_dep = dependency('ncursesw', required : false)
22
pipewire-0.3.67.tar.gz/meson_options.txt -> pipewire-0.3.68.tar.gz/meson_options.txt Changed
9
 
1
@@ -318,3 +318,7 @@
2
        min: -20,
3
        max: -1,
4
        value: -19)
5
+option('opus',
6
+       description: 'Enable code that depends on opus',
7
+       type: 'feature',
8
+       value: 'auto')
9
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
18
 
1
@@ -1121,7 +1121,15 @@
2
        pw_properties_setf(pw->props, PW_KEY_APP_NAME, "PipeWire ALSA %s",
3
                pw_get_prgname());
4
    if (pw_properties_get(pw->props, PW_KEY_NODE_NAME) == NULL)
5
-       pw_properties_setf(pw->props, PW_KEY_NODE_NAME, "ALSA %s",
6
+       pw_properties_setf(pw->props, PW_KEY_NODE_NAME, "alsa_%s.%s",
7
+                  stream == SND_PCM_STREAM_PLAYBACK ? "playback" : "capture",
8
+                  pw_get_prgname());
9
+   if (pw_properties_get(pw->props, PW_KEY_NODE_DESCRIPTION) == NULL)
10
+       pw_properties_setf(pw->props, PW_KEY_NODE_DESCRIPTION, "ALSA %s %s",
11
+                  stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture",
12
+                  pw_get_prgname());
13
+   if (pw_properties_get(pw->props, PW_KEY_MEDIA_NAME) == NULL)
14
+       pw_properties_setf(pw->props, PW_KEY_MEDIA_NAME, "ALSA %s",
15
                   stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture");
16
    if (pw_properties_get(pw->props, PW_KEY_MEDIA_TYPE) == NULL)
17
        pw_properties_set(pw->props, PW_KEY_MEDIA_TYPE, "Audio");
18
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
55
 
1
@@ -898,17 +898,19 @@
2
    spa_hook_remove(&client->node_listener);
3
 }
4
 
5
-static void on_node_bound(void *data, uint32_t global_id)
6
+static void on_node_bound_props(void *data, uint32_t global_id, const struct spa_dict *props)
7
 {
8
    struct client *client = data;
9
    client->node_id = global_id;
10
+   if (props)
11
+       pw_properties_update(client->props, props);
12
 }
13
 
14
 static const struct pw_proxy_events node_proxy_events = {
15
    PW_VERSION_PROXY_EVENTS,
16
    .removed = on_node_removed,
17
    .destroy = on_node_destroy,
18
-   .bound = on_node_bound,
19
+   .bound_props = on_node_bound_props,
20
 };
21
 
22
 static struct link *find_activation(struct spa_list *links, uint32_t node_id)
23
@@ -3798,6 +3800,8 @@
24
        return 0;
25
 
26
    pw_thread_loop_lock(c->context.loop);
27
+   c->active = false;
28
+
29
    pw_data_loop_stop(c->loop);
30
 
31
    pw_client_node_set_active(c->node, false);
32
@@ -3816,12 +3820,7 @@
33
 
34
    pw_thread_loop_unlock(c->context.loop);
35
 
36
-   if (res < 0)
37
-       return res;
38
-
39
-   c->active = false;
40
-
41
-   return 0;
42
+   return res;
43
 }
44
 
45
 SPA_EXPORT
46
@@ -5414,6 +5413,8 @@
47
    latency = SPA_LATENCY_INFO(direction);
48
 
49
    nframes = jack_get_buffer_size((jack_client_t*)c);
50
+   if (nframes == 0)
51
+       nframes = 1;
52
 
53
    latency.min_rate = range->min;
54
    if (latency.min_rate >= nframes) {
55
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
14
 
1
@@ -800,8 +800,10 @@
2
    if ((file = find_file_by_dev(dev_id)) != NULL) {
3
        res = do_dup(file->fd, 0);
4
        unref_file(file);
5
-       if (res >= 0)
6
-           fcntl(res, F_SETFL, oflag);
7
+       if (res < 0)
8
+           return res;
9
+       if (fcntl(res, F_SETFL, oflag) < 0)
10
+           pw_log_warn("fd:%d failed to set flags: %m", res);
11
        return res;
12
    }
13
 
14
pipewire-0.3.67.tar.gz/po/be.po -> pipewire-0.3.68.tar.gz/po/be.po Changed
201
 
1
@@ -2,7 +2,8 @@
2
 # Copyright (C) 2016 PipeWire's COPYRIGHT HOLDER
3
 # This file is distributed under the same license as the PipeWire package.
4
 #
5
-# Viktar Vaŭčkievič <victorenator@gmail.com>, 2016.
6
+#
7
+# Viktar Vaŭčkievič <victorenator@gmail.com>, 2016, 2023.
8
 #
9
 msgid ""
10
 msgstr ""
11
@@ -10,16 +11,16 @@
12
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
13
 "issues/new\n"
14
 "POT-Creation-Date: 2021-04-18 16:54+0800\n"
15
-"PO-Revision-Date: 2016-07-19 11:06+0300\n"
16
+"PO-Revision-Date: 2023-03-11 01:14+0300\n"
17
 "Last-Translator: Viktar Vaŭčkievič <victorenator@gmail.com>\n"
18
 "Language-Team: Belarusian <>\n"
19
 "Language: be\n"
20
 "MIME-Version: 1.0\n"
21
 "Content-Type: text/plain; charset=UTF-8\n"
22
 "Content-Transfer-Encoding: 8bit\n"
23
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
24
-"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
25
-"X-Generator: Lokalize 2.0\n"
26
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
27
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
28
+"X-Generator: Gtranslator 42.0\n"
29
 
30
 #: src/daemon/pipewire.c:43
31
 #, c-format
32
@@ -29,14 +30,19 @@
33
 "      --version                         Show version\n"
34
 "  -c, --config                          Load config (Default %s)\n"
35
 msgstr ""
36
+"%s параметры\n"
37
+"  -h, --help                            Паказаць гэту даведку\n"
38
+"      --version                         Паказаць версію\n"
39
+"  -c, --config                          Загрузіць канфігурацыю (Агаданая "
40
+"%s)\n"
41
 
42
 #: src/daemon/pipewire.desktop.in:4
43
 msgid "PipeWire Media System"
44
-msgstr ""
45
+msgstr "Медыйная сістэма PipeWire"
46
 
47
 #: src/daemon/pipewire.desktop.in:5
48
 msgid "Start the PipeWire Media System"
49
-msgstr ""
50
+msgstr "Старт медыйнай сістэмы PipeWire"
51
 
52
 #: src/examples/media-session/alsa-monitor.c:526
53
 #: spa/plugins/alsa/acp/compat.c:187
54
@@ -50,7 +56,7 @@
55
 
56
 #: src/examples/media-session/alsa-monitor.c:539
57
 msgid "Unknown device"
58
-msgstr ""
59
+msgstr "Невядомая прылада"
60
 
61
 #: src/tools/pw-cat.c:991
62
 #, c-format
63
@@ -61,6 +67,11 @@
64
 "  -v, --verbose                         Enable verbose operations\n"
65
 "\n"
66
 msgstr ""
67
+"%s параметры <file>\n"
68
+"  -h, --help                            Паказаць гэту даведку\n"
69
+"      --version                         Паказаць версію\n"
70
+"  -v, --verbose                         Уключыць падрабязнасці\n"
71
+"\n"
72
 
73
 #: src/tools/pw-cat.c:998
74
 #, c-format
75
@@ -79,6 +90,21 @@
76
 "      --list-targets                    List available targets for --target\n"
77
 "\n"
78
 msgstr ""
79
+"  -R, --remote                          Назва адаленага сэрвіса\n"
80
+"      --media-type                      Задаць тып медыя (агаданы %s)\n"
81
+"      --media-category                  Задаць катэгорыю медыя (агаданая "
82
+"%s)\n"
83
+"      --media-role                      Задаць ролю медыя (агаданая %s)\n"
84
+"      --target                          Задаць мэтавы вузел (агаданы %s)\n"
85
+"                                          0 азначае не спасылацца\n"
86
+"      --latency                         Задаць затрымку вузла (агаданая %s)\n"
87
+"                                          Xадзінка (адзінка = s, ms, us, "
88
+"ns)\n"
89
+"                                          ці наўпрост сэмплы (256)\n"
90
+"                                          з частатой аднаго з уваходных "
91
+"файлаў\n"
92
+"      --list-targets                    Ліставаць наяўныя мэты для --target\n"
93
+"\n"
94
 
95
 #: src/tools/pw-cat.c:1016
96
 #, c-format
97
@@ -99,6 +125,22 @@
98
 "%d)\n"
99
 "\n"
100
 msgstr ""
101
+"      --rate                            Частата сэмплаў (для запісу) "
102
+"(агаданая %u)\n"
103
+"      --channels                        Колькасць каналаў (для запісу) "
104
+"(агаданая %u)\n"
105
+"      --channel-map                     Мапа каналаў\n"
106
+"                                            адзін з: «stereo», "
107
+"«surround-51»,... ці\n"
108
+"                                            назвы каналаў праз коску: пр. "
109
+"«FL,FR»\n"
110
+"      --format                          Фармат сэмплаў %s (req. for rec) "
111
+"(агаданы %s)\n"
112
+"      --volume                          Гучнасць патоку 0-1.0 (агаданая "
113
+"%.3f)\n"
114
+"  -q  --quality                         Якасць перадыскрэтызацыі (0 - 15) "
115
+"(агаданая %d)\n"
116
+"\n"
117
 
118
 #: src/tools/pw-cat.c:1033
119
 msgid ""
120
@@ -107,6 +149,10 @@
121
 "  -m, --midi                            Midi mode\n"
122
 "\n"
123
 msgstr ""
124
+"  -p, --playback                        Рэжым прайгравальніка\n"
125
+"  -r, --record                          Рэжым запісу\n"
126
+"  -m, --midi                            Midi-рэжым\n"
127
+"\n"
128
 
129
 #: src/tools/pw-cli.c:2932
130
 #, c-format
131
@@ -118,10 +164,17 @@
132
 "  -r, --remote                          Remote daemon name\n"
133
 "\n"
134
 msgstr ""
135
+"%s параметры каманда\n"
136
+"  -h, --help                            Паказаць гэту даведку\n"
137
+"      --version                         Паказаць версію\n"
138
+"  -d, --daemon                          Запусціць як фонавы сэрвіс (Default "
139
+"false)\n"
140
+"  -r, --remote                          Назва адаленага сэрвіса\n"
141
+"\n"
142
 
143
 #: spa/plugins/alsa/acp/acp.c:290
144
 msgid "Pro Audio"
145
-msgstr ""
146
+msgstr "Pro Audio"
147
 
148
 #: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704
149
 #: spa/plugins/bluez5/bluez5-device.c:1000
150
@@ -247,14 +300,12 @@
151
 msgstr "Аналагавы выхад"
152
 
153
 #: spa/plugins/alsa/acp/alsa-mixer.c:2809
154
-#, fuzzy
155
 msgid "Headphones 2"
156
-msgstr "Навушнікі"
157
+msgstr "Навушнікі 2"
158
 
159
 #: spa/plugins/alsa/acp/alsa-mixer.c:2810
160
-#, fuzzy
161
 msgid "Headphones Mono Output"
162
-msgstr "Аналагавы монавыхад"
163
+msgstr "Навушнікавы монавыхад"
164
 
165
 #: spa/plugins/alsa/acp/alsa-mixer.c:2811
166
 msgid "Line Out"
167
@@ -289,39 +340,33 @@
168
 msgstr "Шматканальны выхад"
169
 
170
 #: spa/plugins/alsa/acp/alsa-mixer.c:2819
171
-#, fuzzy
172
 msgid "Game Output"
173
-msgstr "%s выхад"
174
+msgstr "Гульнявы выхад"
175
 
176
 #: spa/plugins/alsa/acp/alsa-mixer.c:2820
177
 #: spa/plugins/alsa/acp/alsa-mixer.c:2821
178
-#, fuzzy
179
 msgid "Chat Output"
180
-msgstr "%s выхад"
181
+msgstr "Чатавы выхад"
182
 
183
 #: spa/plugins/alsa/acp/alsa-mixer.c:2822
184
-#, fuzzy
185
 msgid "Chat Input"
186
-msgstr "%s уваход"
187
+msgstr "Чатавы уваход"
188
 
189
 #: spa/plugins/alsa/acp/alsa-mixer.c:2823
190
-#, fuzzy
191
 msgid "Virtual Surround 7.1"
192
-msgstr "Віртуальны абʼёмны прыёмнік"
193
+msgstr "Віртуальны абʼёмны 7.1"
194
 
195
 #: spa/plugins/alsa/acp/alsa-mixer.c:4527
196
 msgid "Analog Mono"
197
 msgstr "Аналагавы мона"
198
 
199
 #: spa/plugins/alsa/acp/alsa-mixer.c:4528
200
-#, fuzzy
201
pipewire-0.3.67.tar.gz/po/ru.po -> pipewire-0.3.68.tar.gz/po/ru.po Changed
201
 
1
@@ -10,19 +10,19 @@
2
 "Project-Id-Version: pipewire\n"
3
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
4
 "issues\n"
5
-"POT-Creation-Date: 2021-05-16 13:13+0000\n"
6
-"PO-Revision-Date: 2021-06-30 13:26+0300\n"
7
-"Last-Translator: Alexey Rubtsov <rushills@gmail.com>\n"
8
+"POT-Creation-Date: 2023-04-06 03:27+0000\n"
9
+"PO-Revision-Date: 2023-04-06 10:51+0300\n"
10
+"Last-Translator: Aleksandr Melman <Alexmelman88@gmail.com>\n"
11
 "Language-Team: ru\n"
12
 "Language: ru\n"
13
 "MIME-Version: 1.0\n"
14
 "Content-Type: text/plain; charset=UTF-8\n"
15
 "Content-Transfer-Encoding: 8bit\n"
16
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
17
-"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
18
-"X-Generator: Poedit 3.0\n"
19
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
20
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
21
+"X-Generator: Poedit 3.2.2\n"
22
 
23
-#: src/daemon/pipewire.c:45
24
+#: src/daemon/pipewire.c:26
25
 #, c-format
26
 msgid ""
27
 "%s options\n"
28
@@ -33,8 +33,8 @@
29
 "%s опции\n"
30
 "  -h, --help                            Показать справку\n"
31
 "      --version                         Информация о версии\n"
32
-"  -c, --config                          Указать файл конфигурации (По "
33
-"умолчанию %s)\n"
34
+"  -c, --config                          Загрузить конфигурацию (По умолчанию "
35
+"%s)\n"
36
 
37
 #: src/daemon/pipewire.desktop.in:4
38
 msgid "PipeWire Media System"
39
@@ -44,72 +44,66 @@
40
 msgid "Start the PipeWire Media System"
41
 msgstr "Запустить PipeWire"
42
 
43
-#: src/examples/media-session/alsa-monitor.c:585
44
-#: spa/plugins/alsa/acp/compat.c:187
45
-msgid "Built-in Audio"
46
-msgstr "Встроенное аудио"
47
-
48
-#: src/examples/media-session/alsa-monitor.c:589
49
-#: spa/plugins/alsa/acp/compat.c:192
50
-msgid "Modem"
51
-msgstr "Модем"
52
-
53
-#: src/examples/media-session/alsa-monitor.c:598
54
-#: src/modules/module-zeroconf-discover.c:290
55
-msgid "Unknown device"
56
-msgstr "Неизвестное устройство"
57
-
58
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:182
59
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:182
60
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
61
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
62
 #, c-format
63
 msgid "Tunnel to %s/%s"
64
 msgstr "Туннель на %s/%s"
65
 
66
-#: src/modules/module-pulse-tunnel.c:511
67
+#: src/modules/module-fallback-sink.c:31
68
+msgid "Dummy Output"
69
+msgstr "Холостой выход"
70
+
71
+#: src/modules/module-pulse-tunnel.c:688
72
 #, c-format
73
 msgid "Tunnel for %s@%s"
74
 msgstr "Туннель для %s@%s"
75
 
76
-#: src/modules/module-zeroconf-discover.c:302
77
+#: src/modules/module-zeroconf-discover.c:315
78
+msgid "Unknown device"
79
+msgstr "Неизвестное устройство"
80
+
81
+#: src/modules/module-zeroconf-discover.c:327
82
 #, c-format
83
 msgid "%s on %s@%s"
84
 msgstr "%s на %s@%s"
85
 
86
-#: src/modules/module-zeroconf-discover.c:306
87
+#: src/modules/module-zeroconf-discover.c:331
88
 #, c-format
89
 msgid "%s on %s"
90
 msgstr "%s по %s"
91
 
92
-#: src/tools/pw-cat.c:991
93
+#: src/tools/pw-cat.c:974
94
 #, c-format
95
 msgid ""
96
-"%s options <file>\n"
97
+"%s options <file>|-\n"
98
 "  -h, --help                            Show this help\n"
99
 "      --version                         Show version\n"
100
 "  -v, --verbose                         Enable verbose operations\n"
101
 "\n"
102
 msgstr ""
103
-"%s опции <файл>\n"
104
+"%s опции <file>|-\n"
105
 "  -h, --help                            Показать справку\n"
106
 "      --version                         Информация о версии\n"
107
 "  -v, --verbose                         Включить показ подробной информации\n"
108
 "\n"
109
 
110
-#: src/tools/pw-cat.c:998
111
+#: src/tools/pw-cat.c:981
112
 #, c-format
113
 msgid ""
114
 "  -R, --remote                          Remote daemon name\n"
115
 "      --media-type                      Set media type (default %s)\n"
116
 "      --media-category                  Set media category (default %s)\n"
117
 "      --media-role                      Set media role (default %s)\n"
118
-"      --target                          Set node target (default %s)\n"
119
+"      --target                          Set node target serial or name "
120
+"(default %s)\n"
121
 "                                          0 means don't link\n"
122
 "      --latency                         Set node latency (default %s)\n"
123
 "                                          Xunit (unit = s, ms, us, ns)\n"
124
 "                                          or direct samples (256)\n"
125
 "                                          the rate is the one of the source "
126
 "file\n"
127
-"      --list-targets                    List available targets for --target\n"
128
+"  -P  --properties                      Set node properties\n"
129
 "\n"
130
 msgstr ""
131
 "  -R, --remote                          Имя удаленного фоновой службы\n"
132
@@ -119,7 +113,8 @@
133
 "умолчанию %s)\n"
134
 "      --media-role                      Задать роль мультимедиа (по "
135
 "умолчанию %s)\n"
136
-"      --target                          Задать цель узла (по умолчанию %s)\n"
137
+"      --target                         Задать серийный номер или имя "
138
+"целевого узла (по умолчанию %s)\n"
139
 "                                          0 значит не связывать\n"
140
 "      --latency                         Задать задежку узла (по умолчанию "
141
 "%s)\n"
142
@@ -128,11 +123,10 @@
143
 "                                          or direct samples (256)\n"
144
 "                                          частота такая же как у источника "
145
 "file\n"
146
-"      --list-targets                    Перечислить доступные цели для --"
147
-"target\n"
148
+"  -P  --properties                      Задать свойства узла\n"
149
 "\n"
150
 
151
-#: src/tools/pw-cat.c:1016
152
+#: src/tools/pw-cat.c:999
153
 #, c-format
154
 msgid ""
155
 "      --rate                            Sample rate (req. for rec) (default "
156
@@ -167,19 +161,23 @@
157
 "умолчанию %d)\n"
158
 "\n"
159
 
160
-#: src/tools/pw-cat.c:1033
161
+#: src/tools/pw-cat.c:1016
162
 msgid ""
163
 "  -p, --playback                        Playback mode\n"
164
 "  -r, --record                          Recording mode\n"
165
 "  -m, --midi                            Midi mode\n"
166
+"  -d, --dsd                             DSD mode\n"
167
+"  -o, --encoded                         Encoded mode\n"
168
 "\n"
169
 msgstr ""
170
 "  -p, --playback                        Режим проигрывания\n"
171
 "  -r, --record                          Режим записи\n"
172
 "  -m, --midi                            Режим MIDI\n"
173
+"  -d, --dsd                             Режим DSD\n"
174
+"  -o, --encoded                         Режим кодирования\n"
175
 "\n"
176
 
177
-#: src/tools/pw-cli.c:2959
178
+#: src/tools/pw-cli.c:2220
179
 #, c-format
180
 msgid ""
181
 "%s options command\n"
182
@@ -187,6 +185,7 @@
183
 "      --version                         Show version\n"
184
 "  -d, --daemon                          Start as daemon (Default false)\n"
185
 "  -r, --remote                          Remote daemon name\n"
186
+"  -m, --monitor                         Monitor activity\n"
187
 "\n"
188
 msgstr ""
189
 "%s опции команда\n"
190
@@ -195,197 +194,198 @@
191
 "  -d, --daemon                          Запустить в режиме фоновой службы "
192
 "(По умолчанию false)\n"
193
 "  -r, --remote                          Имя удаленного фоновой службы\n"
194
+"  -m, --monitor                         Контроль активности\n"
195
 "\n"
196
 
197
-#: spa/plugins/alsa/acp/acp.c:291
198
+#: spa/plugins/alsa/acp/acp.c:303
199
 msgid "Pro Audio"
200
 msgstr "Pro Audio"
201
pipewire-0.3.67.tar.gz/spa/examples/adapter-control.c -> pipewire-0.3.68.tar.gz/spa/examples/adapter-control.c Changed
201
 
1
@@ -24,6 +24,7 @@
2
 #include <errno.h>
3
 #include <pthread.h>
4
 #include <poll.h>
5
+#include <getopt.h>
6
 
7
 #include <spa/control/control.h>
8
 #include <spa/graph/graph.h>
9
@@ -45,6 +46,22 @@
10
 #define MIN_LATENCY        1024
11
 #define CONTROL_BUFFER_SIZE 32768
12
 
13
+#define DEFAULT_RAMP_SAMPLES (64*1*1024)
14
+#define DEFAULT_RAMP_STEP_SAMPLES 200
15
+
16
+#define DEFAULT_RAMP_TIME 2000 // 2 seconds
17
+#define DEFAULT_RAMP_STEP_TIME 5000 // 5 milli seconds
18
+
19
+#define DEFAULT_DEVICE "hw:0,0"
20
+
21
+#define LINEAR "linear"
22
+#define CUBIC "cubic"
23
+#define DEFAULT_SCALE SPA_AUDIO_VOLUME_RAMP_LINEAR
24
+
25
+#define NON_NATIVE "non-native"
26
+#define NATIVE "native"
27
+#define DEFAULT_MODE NON_NATIVE
28
+
29
 
30
 struct buffer {
31
    struct spa_buffer buffer;
32
@@ -91,11 +108,22 @@
33
    double volume_accum;
34
    uint32_t volume_offs;
35
 
36
+   const char *alsa_device;
37
+
38
+   const char *mode;
39
+   enum spa_audio_volume_ramp_scale scale;
40
+
41
+   uint32_t volume_ramp_samples;
42
+   uint32_t volume_ramp_step_samples;
43
+   uint32_t volume_ramp_time;
44
+   uint32_t volume_ramp_step_time;
45
+
46
    bool running;
47
    pthread_t thread;
48
 };
49
 
50
-static int load_handle(struct data *data, struct spa_handle **handle, const char *lib, const char *name)
51
+static int load_handle (struct data *data, struct spa_handle **handle, const
52
+   char *lib, const char *name, struct spa_dict *info)
53
 {
54
    int res;
55
    void *hnd;
56
@@ -134,7 +162,7 @@
57
 
58
        *handle = calloc(1, spa_handle_factory_get_size(factory, NULL));
59
        if ((res = spa_handle_factory_init(factory, *handle,
60
-                       NULL, data->support,
61
+                       info, data->support,
62
                        data->n_support)) < 0) {
63
            printf("can't make factory instance: %d\n", res);
64
            goto exit_cleanup;
65
@@ -153,6 +181,8 @@
66
    int res;
67
    const char *str;
68
    struct spa_handle *handle = NULL;
69
+   struct spa_dict_item items 2;
70
+   struct spa_dict info;
71
    void *iface;
72
 
73
    if ((str = getenv("SPA_PLUGIN_DIR")) == NULL)
74
@@ -167,14 +197,23 @@
75
    /* init the graph */
76
    spa_graph_init(&data->graph, &data->graph_state);
77
 
78
-   /* set the default log */
79
-   data->log = &default_log.log;
80
+   /* enable the debug messages in SPA */
81
+   items 0 = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_TIMESTAMP, "true");
82
+   info = SPA_DICT_INIT(items, 1);
83
+   if ((res = load_handle (data, &handle, "support/libspa-support.so",
84
+           SPA_NAME_SUPPORT_LOG, &info)) < 0)
85
+       return res;
86
+   if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Log, &iface)) < 0) {
87
+       printf("can't get System interface %d\n", res);
88
+       return res;
89
+   }
90
+   data->log = iface;
91
    data->supportdata->n_support++ = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, data->log);
92
 
93
    /* load and set support system */
94
    if ((res = load_handle(data, &handle,
95
            "support/libspa-support.so",
96
-           SPA_NAME_SUPPORT_SYSTEM)) < 0)
97
+           SPA_NAME_SUPPORT_SYSTEM, NULL)) < 0)
98
        return res;
99
    if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_System, &iface)) < 0) {
100
        printf("can't get System interface %d\n", res);
101
@@ -187,7 +226,7 @@
102
    /* load and set support loop and loop control */
103
    if ((res = load_handle(data, &handle,
104
            "support/libspa-support.so",
105
-           SPA_NAME_SUPPORT_LOOP)) < 0)
106
+           SPA_NAME_SUPPORT_LOOP, NULL)) < 0)
107
        return res;
108
 
109
    if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Loop, &iface)) < 0) {
110
@@ -270,68 +309,159 @@
111
    return res;
112
 }
113
 
114
-static int fade_in(struct data *data)
115
+static int get_ramp_samples(struct data *data)
116
 {
117
-   struct spa_pod_builder b;
118
-   struct spa_pod_frame f1;
119
-   void *buffer = data->control_buffer->datas0.data;
120
-   uint32_t buffer_size = data->control_buffer->datas0.maxsize;
121
-   data->control_buffer->datas0.chunk0.size = buffer_size;
122
+   int samples = -1;
123
+   if (data->volume_ramp_samples)
124
+       samples = data->volume_ramp_samples;
125
+   else if (data->volume_ramp_time) {
126
+       samples = (data->volume_ramp_time * 48000) / 1000;
127
+   }
128
+   if (!samples)
129
+       samples = -1;
130
 
131
-   printf ("fading in\n");
132
+   return samples;
133
+}
134
 
135
-   spa_pod_builder_init(&b, buffer, buffer_size);
136
-   spa_pod_builder_push_sequence(&b, &f0, 0);
137
-   data->volume_offs = 0;
138
-   do {
139
-       spa_pod_builder_control(&b, data->volume_offs, SPA_CONTROL_Properties);
140
+static int get_ramp_step_samples(struct data *data)
141
+{
142
+   int samples = -1;
143
+   if (data->volume_ramp_step_samples)
144
+       samples = data->volume_ramp_step_samples;
145
+   else if (data->volume_ramp_step_time) {
146
+       /* convert the step time which is in nano seconds to seconds */
147
+       samples = (data->volume_ramp_step_time / 1000) * (48000 / 1000);
148
+   }
149
+   if (!samples)
150
+       samples = -1;
151
+
152
+   return samples;
153
+}
154
+
155
+static double get_volume_at_scale(struct data *data)
156
+{
157
+   if (data->scale == SPA_AUDIO_VOLUME_RAMP_LINEAR)
158
+       return data->volume_accum;
159
+   else if (data->scale == SPA_AUDIO_VOLUME_RAMP_CUBIC)
160
+       return (data->volume_accum * data->volume_accum * data->volume_accum);
161
+
162
+   return 0.0;
163
+}
164
+
165
+static int fade_in(struct data *data)
166
+{
167
+   printf("fading in\n");
168
+   if (spa_streq (data->mode, NON_NATIVE)) {
169
+       struct spa_pod_builder b;
170
+       struct spa_pod_frame f1;
171
+       void *buffer = data->control_buffer->datas0.data;
172
+       int ramp_samples = get_ramp_samples(data);
173
+       int ramp_step_samples = get_ramp_step_samples(data);
174
+       double step_size = ((double) ramp_step_samples / (double) ramp_samples);
175
+       uint32_t buffer_size = data->control_buffer->datas0.maxsize;
176
+       data->control_buffer->datas0.chunk0.size = buffer_size;
177
+
178
+       spa_pod_builder_init(&b, buffer, buffer_size);
179
+       spa_pod_builder_push_sequence(&b, &f0, 0);
180
+       data->volume_offs = 0;
181
+       do {
182
+           // printf("volume level %f offset %d\n", get_volume_at_scale(data), data->volume_offs);
183
+           spa_pod_builder_control(&b, data->volume_offs, SPA_CONTROL_Properties);
184
            spa_pod_builder_add_object(&b,
185
+               SPA_TYPE_OBJECT_Props, 0,
186
+               SPA_PROP_volume, SPA_POD_Float(get_volume_at_scale(data)));
187
+           data->volume_accum += step_size;
188
+           data->volume_offs += ramp_step_samples;
189
+       } while (data->volume_accum < 1.0);
190
+       spa_pod_builder_pop(&b, &f0);
191
+   }
192
+   else {
193
+       struct spa_pod_builder b;
194
+       struct spa_pod *props;
195
+       int res = 0;
196
+       uint8_t buffer1024;
197
+       spa_pod_builder_init(&b, buffer, sizeof(buffer));
198
+       props = spa_pod_builder_add_object(&b,
199
            SPA_TYPE_OBJECT_Props, 0,
200
-           SPA_PROP_volume, SPA_POD_Float(data->volume_accum));
201
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
16
 
1
@@ -124,7 +124,13 @@
2
                      *  positive for capture, negative for playback */
3
    double rate_diff;       /**< rate difference between clock and monotonic time */
4
    uint64_t next_nsec;     /**< estimated next wakeup time in nanoseconds */
5
-   uint32_t padding8;
6
+
7
+   struct spa_fraction target_rate;    /**< target rate of next cycle */
8
+   uint64_t target_duration;       /**< target duration of next cycle */
9
+   uint32_t target_seq;            /**< seq counter. must be equal at start and
10
+                         *  end of read and lower bit must be 0 */
11
+
12
+   uint32_t padding3;
13
 };
14
 
15
 /* the size of the video in this cycle */
16
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
17
 
1
@@ -28,6 +28,7 @@
2
 #include <spa/param/audio/alac.h>
3
 #include <spa/param/audio/flac.h>
4
 #include <spa/param/audio/ape.h>
5
+#include <spa/param/audio/opus.h>
6
 
7
 struct spa_audio_info {
8
    uint32_t media_type;
9
@@ -46,6 +47,7 @@
10
        struct spa_audio_info_alac alac;
11
        struct spa_audio_info_flac flac;
12
        struct spa_audio_info_ape ape;
13
+       struct spa_audio_info_ape opus;
14
    } info;
15
 };
16
 
17
pipewire-0.3.68.tar.gz/spa/include/spa/param/audio/opus.h Added
31
 
1
@@ -0,0 +1,29 @@
2
+/* Simple Plugin API */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_AUDIO_OPUS_H
7
+#define SPA_AUDIO_OPUS_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+#include <spa/param/audio/raw.h>
14
+
15
+struct spa_audio_info_opus {
16
+   uint32_t rate;              /*< sample rate */
17
+   uint32_t channels;          /*< number of channels */
18
+};
19
+
20
+#define SPA_AUDIO_INFO_OPUS_INIT(...)      ((struct spa_audio_info_opus) { __VA_ARGS__ })
21
+
22
+/**
23
+ * \}
24
+ */
25
+
26
+#ifdef __cplusplus
27
+}  /* extern "C" */
28
+#endif
29
+
30
+#endif /* SPA_AUDIO_OPUS_H */
31
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
14
 
1
@@ -261,6 +261,12 @@
2
    SPA_AUDIO_CHANNEL_START_Custom  = 0x10000,
3
 };
4
 
5
+enum spa_audio_volume_ramp_scale {
6
+   SPA_AUDIO_VOLUME_RAMP_INVALID,
7
+   SPA_AUDIO_VOLUME_RAMP_LINEAR,
8
+   SPA_AUDIO_VOLUME_RAMP_CUBIC,
9
+};
10
+
11
 /** Extra audio flags */
12
 #define SPA_AUDIO_FLAG_NONE        (0)     /*< no valid flag */
13
 #define SPA_AUDIO_FLAG_UNPOSITIONED    (1 << 0)    /*< the position array explicitly
14
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
9
 
1
@@ -63,6 +63,7 @@
2
    { SPA_MEDIA_SUBTYPE_alac, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "alac", NULL },
3
    { SPA_MEDIA_SUBTYPE_flac, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "flac", NULL },
4
    { SPA_MEDIA_SUBTYPE_ape, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "ape", NULL },
5
+   { SPA_MEDIA_SUBTYPE_opus, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "opus", NULL },
6
    /* video subtypes */
7
    { SPA_MEDIA_SUBTYPE_h264, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "h264", NULL },
8
    { SPA_MEDIA_SUBTYPE_mjpg, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mjpg", NULL },
9
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
9
 
1
@@ -51,6 +51,7 @@
2
    SPA_MEDIA_SUBTYPE_alac,     /** since 0.3.65 */
3
    SPA_MEDIA_SUBTYPE_flac,     /** since 0.3.65 */
4
    SPA_MEDIA_SUBTYPE_ape,      /** since 0.3.65 */
5
+   SPA_MEDIA_SUBTYPE_opus,     /** since 0.3.68 */
6
 
7
    SPA_MEDIA_SUBTYPE_START_Video   = 0x20000,
8
    SPA_MEDIA_SUBTYPE_h264,
9
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
13
 
1
@@ -58,6 +58,11 @@
2
    { SPA_PROP_softMute, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "softMute", NULL },
3
    { SPA_PROP_softVolumes, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "softVolumes", spa_type_prop_float_array },
4
    { SPA_PROP_iec958Codecs, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "iec958Codecs", spa_type_prop_iec958_codec },
5
+   { SPA_PROP_volumeRampSamples, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampSamples", NULL },
6
+   { SPA_PROP_volumeRampStepSamples, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampStepSamples", NULL },
7
+   { SPA_PROP_volumeRampTime, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampTime", NULL },
8
+   { SPA_PROP_volumeRampStepTime, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampStepTime", NULL },
9
+   { SPA_PROP_volumeRampScale, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "volumeRampScale", NULL },
10
 
11
    { SPA_PROP_brightness, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "brightness", NULL },
12
    { SPA_PROP_contrast, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "contrast", NULL },
13
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
25
 
1
@@ -59,7 +59,7 @@
2
    SPA_PROP_START_Audio    = 0x10000,  /**< audio related properties */
3
    SPA_PROP_waveType,
4
    SPA_PROP_frequency,
5
-   SPA_PROP_volume,            /**< a volume (Float), 0.0 silence, 1.0 normal */
6
+   SPA_PROP_volume,                /**< a volume (Float), 0.0 silence, 1.0 normal */
7
    SPA_PROP_mute,              /**< mute (Bool) */
8
    SPA_PROP_patternType,
9
    SPA_PROP_ditherType,
10
@@ -80,6 +80,14 @@
11
 
12
    SPA_PROP_iec958Codecs,          /**< enabled IEC958 (S/PDIF) codecs,
13
                          *  (Array (Id enum spa_audio_iec958_codec) */
14
+   SPA_PROP_volumeRampSamples,     /**< Samples to ramp the volume over */
15
+   SPA_PROP_volumeRampStepSamples,     /**< Step or incremental Samples to ramp
16
+                         *  the volume over */
17
+   SPA_PROP_volumeRampTime,        /**< Time in millisec to ramp the volume over */
18
+   SPA_PROP_volumeRampStepTime,        /**< Step or incremental Time in nano seconds
19
+                         *  to ramp the */
20
+   SPA_PROP_volumeRampScale,       /**< the scale or graph to used to ramp the
21
+                         *  volume */
22
 
23
    SPA_PROP_START_Video    = 0x20000,  /**< video related properties */
24
    SPA_PROP_brightness,
25
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
44
 
1
@@ -28,7 +28,7 @@
2
 struct spa_loop { struct spa_interface iface; };
3
 
4
 #define SPA_TYPE_INTERFACE_LoopControl SPA_TYPE_INFO_INTERFACE_BASE "LoopControl"
5
-#define SPA_VERSION_LOOP_CONTROL   0
6
+#define SPA_VERSION_LOOP_CONTROL   1
7
 struct spa_loop_control { struct spa_interface iface; };
8
 
9
 #define SPA_TYPE_INTERFACE_LoopUtils   SPA_TYPE_INFO_INTERFACE_BASE "LoopUtils"
10
@@ -140,7 +140,7 @@
11
 struct spa_loop_control_methods {
12
    /* the version of this structure. This can be used to expand this
13
     * structure in the future */
14
-#define SPA_VERSION_LOOP_CONTROL_METHODS   0
15
+#define SPA_VERSION_LOOP_CONTROL_METHODS   1
16
    uint32_t version;
17
 
18
    int (*get_fd) (void *object);
19
@@ -182,6 +182,16 @@
20
     * The number of dispatched fds is returned.
21
     */
22
    int (*iterate) (void *object, int timeout);
23
+
24
+   /** Check context of the loop
25
+    * \param ctrl the control
26
+    *
27
+    * This function will check if the current thread is currently the
28
+    * one that did the enter call. Since version 1:1.
29
+    *
30
+    * returns 1 on success, 0 or negative errno value on error.
31
+    */
32
+   int (*check) (void *object);
33
 };
34
 
35
 #define spa_loop_control_method_v(o,method,version,...)            \
36
@@ -207,6 +217,7 @@
37
 #define spa_loop_control_enter(l)      spa_loop_control_method_v(l,enter,0)
38
 #define spa_loop_control_leave(l)      spa_loop_control_method_v(l,leave,0)
39
 #define spa_loop_control_iterate(l,...)        spa_loop_control_method_r(l,iterate,0,__VA_ARGS__)
40
+#define spa_loop_control_check(l)      spa_loop_control_method_r(l,check,1)
41
 
42
 typedef void (*spa_source_io_func_t) (void *data, int fd, uint32_t mask);
43
 typedef void (*spa_source_idle_func_t) (void *data);
44
pipewire-0.3.67.tar.gz/spa/meson.build -> pipewire-0.3.68.tar.gz/spa/meson.build Changed
16
 
1
@@ -74,10 +74,13 @@
2
       endif
3
     endif
4
     summary({'LC3plus': lc3plus_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
5
-    opus_dep = dependency('opus', required : get_option('bluez5-codec-opus'))
6
+    if get_option('bluez5-codec-opus').enabled() and not opus_dep.found()
7
+        error('bluez5-codec-opus enabled, but opus dependency not found')
8
+    endif
9
     summary({'Opus': opus_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
10
     lc3_dep = dependency('lc3', required : get_option('bluez5-codec-lc3'))
11
     summary({'LC3': lc3_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
12
+    cdata.set('HAVE_BLUETOOTH_BAP', get_option('bluez5-codec-lc3').allowed() and lc3_dep.found())
13
     if get_option('bluez5-backend-hsp-native').allowed() or get_option('bluez5-backend-hfp-native').allowed()
14
       mm_dep = dependency('ModemManager', version : '>= 1.10.0', required : get_option('bluez5-backend-native-mm'))
15
       summary({'ModemManager': mm_dep.found()}, bool_yn: true, section: 'Bluetooth backends')
16
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
140
 
1
@@ -362,7 +362,7 @@
2
                            devstr, NULL, &m->sample_spec,
3
                            &m->channel_map, SND_PCM_STREAM_PLAYBACK,
4
                            &try_period_size, &try_buffer_size,
5
-                           0, NULL, NULL, false))) {
6
+                           0, NULL, NULL, NULL, NULL, false))) {
7
                pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm);
8
                pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index);
9
                pa_alsa_close(&m->output_pcm);
10
@@ -392,7 +392,7 @@
11
                            devstr, NULL, &m->sample_spec,
12
                            &m->channel_map, SND_PCM_STREAM_CAPTURE,
13
                            &try_period_size, &try_buffer_size,
14
-                           0, NULL, NULL, false))) {
15
+                           0, NULL, NULL, NULL, NULL, false))) {
16
                pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm);
17
                pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index);
18
                pa_alsa_close(&m->input_pcm);
19
@@ -449,8 +449,8 @@
20
                    pa_dynarray_append(&impl->out.devices, dev);
21
                }
22
                if (impl->use_ucm) {
23
-                   if (m->ucm_context.ucm_devices) {
24
-                       pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context,
25
+                   if (m->ucm_context.ucm_device) {
26
+                       pa_alsa_ucm_add_port(NULL, &m->ucm_context,
27
                            true, impl->ports, ap, NULL);
28
                        pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context,
29
                            true, impl, dev->pcm_handle, impl->profile_set->ignore_dB);
30
@@ -473,8 +473,8 @@
31
                }
32
 
33
                if (impl->use_ucm) {
34
-                   if (m->ucm_context.ucm_devices) {
35
-                       pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context,
36
+                   if (m->ucm_context.ucm_device) {
37
+                       pa_alsa_ucm_add_port(NULL, &m->ucm_context,
38
                            false, impl->ports, ap, NULL);
39
                        pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context,
40
                            false, impl, dev->pcm_handle, impl->profile_set->ignore_dB);
41
@@ -608,7 +608,7 @@
42
 static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask)
43
 {
44
    pa_card *impl = snd_mixer_elem_get_callback_private(melem);
45
-   snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
46
+   snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem;
47
    snd_ctl_elem_value_t *elem_value;
48
    bool plugged_in, any_input_port_available;
49
    void *state;
50
@@ -618,6 +618,8 @@
51
    enum acp_available active_available = ACP_AVAILABLE_UNKNOWN;
52
    size_t size;
53
 
54
+   pa_assert(_elem);
55
+   elem = *_elem;
56
 #if 0
57
    /* Changing the jack state may cause a port change, and a port change will
58
     * make the sink or source change the mixer settings. If there are multiple
59
@@ -886,13 +888,17 @@
60
 static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask)
61
 {
62
    pa_card *impl = snd_mixer_elem_get_callback_private(melem);
63
-   snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
64
-   int device = snd_hctl_elem_get_device(elem);
65
+   snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem;
66
+   int device;
67
    const char *old_monitor_name;
68
    pa_device_port *p;
69
    pa_hdmi_eld eld;
70
    bool changed = false;
71
 
72
+   pa_assert(_elem);
73
+   elem = *_elem;
74
+   device = snd_hctl_elem_get_device(elem);
75
+
76
    if (mask == SND_CTL_EVENT_MASK_REMOVE)
77
        return 0;
78
 
79
@@ -1253,8 +1259,7 @@
80
    * will be NULL, but the UCM device enable sequence will still need to be
81
    * executed. */
82
    if (dev->active_port && dev->ucm_context) {
83
-       if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port,
84
-                   dev->direction == PA_ALSA_DIRECTION_OUTPUT)) < 0)
85
+       if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port)) < 0)
86
            return res;
87
    }
88
 
89
@@ -1429,8 +1434,7 @@
90
    /* if UCM is available for this card then update the verb */
91
    if (impl->use_ucm && !(np->profile.flags & ACP_PROFILE_PRO)) {
92
        if ((res = pa_alsa_ucm_set_profile(&impl->ucm, impl,
93
-           np->profile.flags & ACP_PROFILE_OFF ? NULL : np->profile.name,
94
-           op ? op->profile.name : NULL)) < 0) {
95
+           np->profile.flags & ACP_PROFILE_OFF ? NULL : np, op)) < 0) {
96
            return res;
97
        }
98
    }
99
@@ -1439,8 +1443,8 @@
100
        PA_IDXSET_FOREACH(am, np->output_mappings, idx) {
101
            if (impl->use_ucm) {
102
                /* Update ports priorities */
103
-               if (am->ucm_context.ucm_devices) {
104
-                   pa_alsa_ucm_add_ports_combination(am->output.ports, &am->ucm_context,
105
+               if (am->ucm_context.ucm_device) {
106
+                   pa_alsa_ucm_add_port(am->output.ports, &am->ucm_context,
107
                        true, impl->ports, np, NULL);
108
                }
109
            }
110
@@ -1452,8 +1456,8 @@
111
        PA_IDXSET_FOREACH(am, np->input_mappings, idx) {
112
            if (impl->use_ucm) {
113
                /* Update ports priorities */
114
-               if (am->ucm_context.ucm_devices) {
115
-                   pa_alsa_ucm_add_ports_combination(am->input.ports, &am->ucm_context,
116
+               if (am->ucm_context.ucm_device) {
117
+                   pa_alsa_ucm_add_port(am->input.ports, &am->ucm_context,
118
                        false, impl->ports, np, NULL);
119
                }
120
            }
121
@@ -1589,7 +1593,7 @@
122
 
123
    res = impl->use_ucm ? pa_alsa_ucm_query_profiles(&impl->ucm, card->index) : -1;
124
    if (res == -PA_ALSA_ERR_UCM_LINKED) {
125
-       res = -ENOENT;
126
+       res = -EEXIST;
127
        goto error;
128
    }
129
    if (res == 0) {
130
@@ -1844,8 +1848,7 @@
131
        mixer_volume_init(impl, d);
132
 
133
        sync_mixer(d, p);
134
-       res = pa_alsa_ucm_set_port(d->ucm_context, p,
135
-                   dev->direction == ACP_DIRECTION_PLAYBACK);
136
+       res = pa_alsa_ucm_set_port(d->ucm_context, p);
137
    } else {
138
        pa_alsa_port_data *data;
139
 
140
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
86
 
1
@@ -4734,35 +4734,29 @@
2
                                                        PA_ELEMENTSOF(well_known_descriptions)));
3
 
4
     if (!p->description) {
5
+        pa_strbuf *sb;
6
         uint32_t idx;
7
         pa_alsa_mapping *m;
8
-   char *ptr;
9
-   size_t size;
10
-   FILE *f;
11
-   int count = 0;
12
-
13
-   f = open_memstream(&ptr, &size);
14
-   if (f == NULL) {
15
-            pa_log("failed to open memstream: %m");
16
-            return -1;
17
-   }
18
+
19
+        sb = pa_strbuf_new();
20
 
21
         if (p->output_mappings)
22
             PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
23
-                if (count++ > 0)
24
-                    fprintf(f, " + ");
25
-                fprintf(f, _("%s Output"), m->description);
26
+                if (!pa_strbuf_isempty(sb))
27
+                    pa_strbuf_puts(sb, " + ");
28
+
29
+                pa_strbuf_printf(sb, _("%s Output"), m->description);
30
             }
31
 
32
         if (p->input_mappings)
33
             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
34
-                if (count++ > 0)
35
-                    fprintf(f, " + ");
36
-                fprintf(f, _("%s Input"), m->description);
37
+                if (!pa_strbuf_isempty(sb))
38
+                    pa_strbuf_puts(sb, " + ");
39
+
40
+                pa_strbuf_printf(sb, _("%s Input"), m->description);
41
             }
42
 
43
-   fclose(f);
44
-        p->description = ptr;
45
+        p->description = pa_strbuf_to_string_free(sb);
46
     }
47
 
48
     return 0;
49
@@ -4814,23 +4808,17 @@
50
     pa_assert(db_fix);
51
 
52
     if (db_fix->db_values) {
53
+        pa_strbuf *buf;
54
         unsigned long i, nsteps;
55
-   FILE *f;
56
-   char *ptr;
57
-   size_t size;
58
-
59
-   f = open_memstream(&ptr, &size);
60
-   if (f == NULL)
61
-       return;
62
 
63
         pa_assert(db_fix->min_step <= db_fix->max_step);
64
         nsteps = db_fix->max_step - db_fix->min_step + 1;
65
 
66
+        buf = pa_strbuf_new();
67
         for (i = 0; i < nsteps; ++i)
68
-            fprintf(f, "%li:%0.2f ", i + db_fix->min_step, db_fix->db_valuesi / 100.0);
69
+            pa_strbuf_printf(buf, "%li:%0.2f ", i + db_fix->min_step, db_fix->db_valuesi / 100.0);
70
 
71
-   fclose(f);
72
-        db_values = ptr;
73
+        db_values = pa_strbuf_to_string_free(buf);
74
     }
75
 
76
     pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
77
@@ -5012,7 +5000,7 @@
78
     handle = pa_alsa_open_by_template(
79
                               m->device_strings, dev_id, NULL, &try_ss,
80
                               &try_map, mode, &try_period_size,
81
-                              &try_buffer_size, 0, NULL, NULL, exact_channels);
82
+                              &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels);
83
     if (handle && !exact_channels && m->channel_map.channels != try_map.channels) {
84
         char bufPA_CHANNEL_MAP_SNPRINT_MAX;
85
         pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
86
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
20
 
1
@@ -354,7 +354,7 @@
2
     pa_alsa_device output;
3
     pa_alsa_device input;
4
 
5
-    /* ucm device context*/
6
+    /* ucm device context */
7
     pa_alsa_ucm_mapping_context ucm_context;
8
 };
9
 
10
@@ -381,6 +381,9 @@
11
     pa_idxset *input_mappings;
12
     pa_idxset *output_mappings;
13
 
14
+    /* ucm device context */
15
+    pa_alsa_ucm_profile_context ucm_context;
16
+
17
     struct {
18
    pa_dynarray devices;
19
     } out;
20
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
201
 
1
@@ -72,9 +72,8 @@
2
 
3
 
4
 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
5
-                               pa_alsa_ucm_device **devices, unsigned n_devices);
6
+                               pa_alsa_ucm_device *device);
7
 static void ucm_port_data_free(pa_device_port *port);
8
-static void ucm_port_update_available(pa_alsa_ucm_port_data *port);
9
 
10
 static struct ucm_type types = {
11
     {"None", PA_DEVICE_PORT_TYPE_UNKNOWN},
12
@@ -170,17 +169,6 @@
13
     return (char *)value;
14
 }
15
 
16
-static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
17
-    pa_alsa_ucm_device *d;
18
-    uint32_t idx;
19
-
20
-    PA_IDXSET_FOREACH(d, idxset, idx)
21
-        if (d == dev)
22
-            return 1;
23
-
24
-    return 0;
25
-}
26
-
27
 static void ucm_add_devices_to_idxset(
28
         pa_idxset *idxset,
29
         pa_alsa_ucm_device *me,
30
@@ -506,10 +494,10 @@
31
     n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
32
     pa_xfree(id);
33
 
34
+    device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
35
     if (n_confdev <= 0)
36
         pa_log_debug("No %s for device %s", "_conflictingdevs", device_name);
37
     else {
38
-        device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
39
         ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev);
40
         snd_use_case_free_list(devices, n_confdev);
41
     }
42
@@ -518,10 +506,10 @@
43
     n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
44
     pa_xfree(id);
45
 
46
+    device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
47
     if (n_suppdev <= 0)
48
         pa_log_debug("No %s for device %s", "_supporteddevs", device_name);
49
     else {
50
-        device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
51
         ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev);
52
         snd_use_case_free_list(devices, n_suppdev);
53
     }
54
@@ -530,10 +518,16 @@
55
 };
56
 
57
 /* Create a property list for this ucm modifier */
58
-static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) {
59
+static int ucm_get_modifier_property(
60
+        pa_alsa_ucm_modifier *modifier,
61
+        snd_use_case_mgr_t *uc_mgr,
62
+        pa_alsa_ucm_verb *verb,
63
+        const char *modifier_name) {
64
     const char *value;
65
     char *id;
66
     int i;
67
+    const char **devices;
68
+    int n_confdev, n_suppdev;
69
 
70
     for (i = 0; itemi.id; i++) {
71
         int err;
72
@@ -550,16 +544,28 @@
73
     }
74
 
75
     id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
76
-    modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices);
77
+    n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
78
     pa_xfree(id);
79
-    if (modifier->n_confdev < 0)
80
+
81
+    modifier->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
82
+    if (n_confdev <= 0)
83
         pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name);
84
+    else {
85
+        ucm_add_devices_to_idxset(modifier->conflicting_devices, NULL, verb->devices, devices, n_confdev);
86
+        snd_use_case_free_list(devices, n_confdev);
87
+    }
88
 
89
     id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
90
-    modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices);
91
+    n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
92
     pa_xfree(id);
93
-    if (modifier->n_suppdev < 0)
94
+
95
+    modifier->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
96
+    if (n_suppdev <= 0)
97
         pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name);
98
+    else {
99
+        ucm_add_devices_to_idxset(modifier->supported_devices, NULL, verb->devices, devices, n_suppdev);
100
+        snd_use_case_free_list(devices, n_suppdev);
101
+    }
102
 
103
     return 0;
104
 };
105
@@ -596,6 +602,59 @@
106
     return 0;
107
 };
108
 
109
+static long ucm_device_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
110
+    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
111
+    char *devstatus;
112
+    long status = 0;
113
+
114
+    devstatus = pa_sprintf_malloc("_devstatus/%s", dev_name);
115
+    if (snd_use_case_geti(ucm->ucm_mgr, devstatus, &status) < 0) {
116
+        pa_log_debug("Failed to get status for UCM device %s", dev_name);
117
+        status = -1;
118
+    }
119
+    pa_xfree(devstatus);
120
+
121
+    return status;
122
+}
123
+
124
+static int ucm_device_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
125
+    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
126
+
127
+    /* If any of dev's conflicting devices is enabled, trying to disable
128
+     * dev gives an error despite the fact that it's already disabled.
129
+     * Check that dev is enabled to avoid this error. */
130
+    if (ucm_device_status(ucm, dev) == 0) {
131
+        pa_log_debug("UCM device %s is already disabled", dev_name);
132
+        return 0;
133
+    }
134
+
135
+    pa_log_debug("Disabling UCM device %s", dev_name);
136
+    if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) < 0) {
137
+        pa_log("Failed to disable UCM device %s", dev_name);
138
+        return -1;
139
+    }
140
+
141
+    return 0;
142
+}
143
+
144
+static int ucm_device_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
145
+    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
146
+
147
+    /* We don't need to enable devices that are already enabled */
148
+    if (ucm_device_status(ucm, dev) > 0) {
149
+        pa_log_debug("UCM device %s is already enabled", dev_name);
150
+        return 0;
151
+    }
152
+
153
+    pa_log_debug("Enabling UCM device %s", dev_name);
154
+    if (snd_use_case_set(ucm->ucm_mgr, "_enadev", dev_name) < 0) {
155
+        pa_log("Failed to enable UCM device %s", dev_name);
156
+        return -1;
157
+    }
158
+
159
+    return 0;
160
+}
161
+
162
 static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
163
     const char **mod_list;
164
     int num_mod, i;
165
@@ -626,6 +685,57 @@
166
     return 0;
167
 };
168
 
169
+static long ucm_modifier_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
170
+    const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
171
+    char *modstatus;
172
+    long status = 0;
173
+
174
+    modstatus = pa_sprintf_malloc("_modstatus/%s", mod_name);
175
+    if (snd_use_case_geti(ucm->ucm_mgr, modstatus, &status) < 0) {
176
+        pa_log_debug("Failed to get status for UCM modifier %s", mod_name);
177
+        status = -1;
178
+    }
179
+    pa_xfree(modstatus);
180
+
181
+    return status;
182
+}
183
+
184
+static int ucm_modifier_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
185
+    const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
186
+
187
+    /* We don't need to disable modifiers that are already disabled */
188
+    if (ucm_modifier_status(ucm, mod) == 0) {
189
+        pa_log_debug("UCM modifier %s is already disabled", mod_name);
190
+        return 0;
191
+    }
192
+
193
+    pa_log_debug("Disabling UCM modifier %s", mod_name);
194
+    if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
195
+        pa_log("Failed to disable UCM modifier %s", mod_name);
196
+        return -1;
197
+    }
198
+
199
+    return 0;
200
+}
201
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
78
 
1
@@ -142,12 +142,13 @@
2
 typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
3
 typedef struct pa_alsa_ucm_config pa_alsa_ucm_config;
4
 typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
5
+typedef struct pa_alsa_ucm_profile_context pa_alsa_ucm_profile_context;
6
 typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data;
7
 typedef struct pa_alsa_ucm_volume pa_alsa_ucm_volume;
8
 
9
 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index);
10
 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
11
-int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile);
12
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile);
13
 
14
 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);
15
 
16
@@ -159,14 +160,14 @@
17
         pa_card *card,
18
         snd_pcm_t *pcm_handle,
19
         bool ignore_dB);
20
-void pa_alsa_ucm_add_ports_combination(
21
+void pa_alsa_ucm_add_port(
22
         pa_hashmap *hash,
23
         pa_alsa_ucm_mapping_context *context,
24
         bool is_sink,
25
         pa_hashmap *ports,
26
         pa_card_profile *cp,
27
         pa_core *core);
28
-int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink);
29
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port);
30
 
31
 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm);
32
 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context);
33
@@ -223,11 +224,8 @@
34
 
35
     pa_proplist *proplist;
36
 
37
-    int n_confdev;
38
-    int n_suppdev;
39
-
40
-    const char **conflicting_devices;
41
-    const char **supported_devices;
42
+    pa_idxset *conflicting_devices;
43
+    pa_idxset *supported_devices;
44
 
45
     pa_direction_t action_direction;
46
 
47
@@ -270,21 +268,23 @@
48
     pa_alsa_ucm_config *ucm;
49
     pa_direction_t direction;
50
 
51
-    pa_idxset *ucm_devices;
52
-    pa_idxset *ucm_modifiers;
53
+    pa_alsa_ucm_device *ucm_device;
54
+    pa_alsa_ucm_modifier *ucm_modifier;
55
+};
56
+
57
+struct pa_alsa_ucm_profile_context {
58
+    pa_alsa_ucm_verb *verb;
59
 };
60
 
61
 struct pa_alsa_ucm_port_data {
62
     pa_alsa_ucm_config *ucm;
63
     pa_device_port *core_port;
64
 
65
-    /* A single port will be associated with multiple devices if it represents
66
-     * a combination of devices. */
67
-    pa_dynarray *devices; /* pa_alsa_ucm_device */
68
+    pa_alsa_ucm_device *device;
69
 
70
-    /* profile name -> pa_alsa_path for volume control */
71
+    /* verb name -> pa_alsa_path for volume control */
72
     pa_hashmap *paths;
73
-    /* Current path, set when activating profile */
74
+    /* Current path, set when activating verb */
75
     pa_alsa_path *path;
76
 
77
     /* ELD info */
78
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
201
 
1
@@ -505,6 +505,8 @@
2
         snd_pcm_uframes_t tsched_size,
3
         bool *use_mmap,
4
         bool *use_tsched,
5
+        pa_sample_format_t **query_supported_formats,
6
+        unsigned int **query_supported_rates,
7
         pa_alsa_profile_set *ps,
8
         pa_alsa_mapping **mapping) {
9
 
10
@@ -543,6 +545,8 @@
11
                 tsched_size,
12
                 use_mmap,
13
                 use_tsched,
14
+                query_supported_formats,
15
+                query_supported_rates,
16
                 m);
17
 
18
         if (pcm_handle) {
19
@@ -570,6 +574,8 @@
20
                 tsched_size,
21
                 use_mmap,
22
                 use_tsched,
23
+                query_supported_formats,
24
+                query_supported_rates,
25
                 m);
26
 
27
         if (pcm_handle) {
28
@@ -594,6 +600,8 @@
29
             tsched_size,
30
             use_mmap,
31
             use_tsched,
32
+            query_supported_formats,
33
+            query_supported_rates,
34
             false);
35
     pa_xfree(d);
36
 
37
@@ -615,6 +623,8 @@
38
         snd_pcm_uframes_t tsched_size,
39
         bool *use_mmap,
40
         bool *use_tsched,
41
+        pa_sample_format_t **query_supported_formats,
42
+        unsigned int **query_supported_rates,
43
         pa_alsa_mapping *m) {
44
 
45
     snd_pcm_t *pcm_handle;
46
@@ -644,6 +654,8 @@
47
             tsched_size,
48
             use_mmap,
49
             use_tsched,
50
+            query_supported_formats,
51
+            query_supported_rates,
52
             pa_channel_map_valid(&m->channel_map) /* Query the channel count if we don't know what we want */);
53
 
54
     if (!pcm_handle)
55
@@ -681,6 +693,8 @@
56
         snd_pcm_uframes_t tsched_size,
57
         bool *use_mmap,
58
         bool *use_tsched,
59
+        pa_sample_format_t **query_supported_formats,
60
+        unsigned int **query_supported_rates,
61
         bool require_exact_channel_number) {
62
 
63
     int err;
64
@@ -708,6 +722,12 @@
65
         pa_log_info("ALSA device open '%s' %s: %p", d,
66
            mode == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", pcm_handle);
67
 
68
+        if (query_supported_formats)
69
+            *query_supported_formats = pa_alsa_get_supported_formats(pcm_handle, ss->format);
70
+
71
+        if (query_supported_rates)
72
+            *query_supported_rates = pa_alsa_get_supported_rates(pcm_handle, ss->rate);
73
+
74
         if ((err = pa_alsa_set_hw_params(
75
                      pcm_handle,
76
                      ss,
77
@@ -781,6 +801,8 @@
78
         snd_pcm_uframes_t tsched_size,
79
         bool *use_mmap,
80
         bool *use_tsched,
81
+        pa_sample_format_t **query_supported_formats,
82
+        unsigned int **query_supported_rates,
83
         bool require_exact_channel_number) {
84
 
85
     snd_pcm_t *pcm_handle;
86
@@ -802,6 +824,8 @@
87
                 tsched_size,
88
                 use_mmap,
89
                 use_tsched,
90
+                query_supported_formats,
91
+                query_supported_rates,
92
                 require_exact_channel_number);
93
 
94
         pa_xfree(d);
95
@@ -1411,6 +1435,24 @@
96
 
97
     return pa_sprintf_malloc("Audio%i", i);
98
 }
99
+#endif
100
+
101
+static void dump_supported_rates(unsigned int* values)
102
+{
103
+    pa_strbuf *buf;
104
+    char *str;
105
+    int i;
106
+
107
+    buf = pa_strbuf_new();
108
+
109
+    for (i = 0; valuesi; i++) {
110
+        pa_strbuf_printf(buf, " %u", valuesi);
111
+    }
112
+
113
+    str = pa_strbuf_to_string_free(buf);
114
+    pa_log_debug("Supported rates:%s", str);
115
+    pa_xfree(str);
116
+}
117
 
118
 unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate) {
119
     static unsigned int all_rates = { 8000, 11025, 12000,
120
@@ -1418,7 +1460,8 @@
121
                                         32000, 44100, 48000,
122
                                         64000, 88200, 96000,
123
                                         128000, 176400, 192000,
124
-                                        384000 };
125
+                                        352800, 384000,
126
+                                        705600, 768000 };
127
     bool supportedPA_ELEMENTSOF(all_rates) = { false, };
128
     snd_pcm_hw_params_t *hwparams;
129
     unsigned int i, j, n, *rates = NULL;
130
@@ -1460,39 +1503,40 @@
131
         rates1 = 0;
132
     }
133
 
134
+    dump_supported_rates(rates);
135
     return rates;
136
 }
137
 
138
 pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_format_t fallback_format) {
139
-    static const snd_pcm_format_t format_trans_to_pa = {
140
-        SND_PCM_FORMAT_U8 = PA_SAMPLE_U8,
141
-        SND_PCM_FORMAT_A_LAW = PA_SAMPLE_ALAW,
142
-        SND_PCM_FORMAT_MU_LAW = PA_SAMPLE_ULAW,
143
-        SND_PCM_FORMAT_S16_LE = PA_SAMPLE_S16LE,
144
-        SND_PCM_FORMAT_S16_BE = PA_SAMPLE_S16BE,
145
-        SND_PCM_FORMAT_FLOAT_LE = PA_SAMPLE_FLOAT32LE,
146
-        SND_PCM_FORMAT_FLOAT_BE = PA_SAMPLE_FLOAT32BE,
147
-        SND_PCM_FORMAT_S32_LE = PA_SAMPLE_S32LE,
148
-        SND_PCM_FORMAT_S32_BE = PA_SAMPLE_S32BE,
149
-        SND_PCM_FORMAT_S24_3LE = PA_SAMPLE_S24LE,
150
-        SND_PCM_FORMAT_S24_3BE = PA_SAMPLE_S24BE,
151
-        SND_PCM_FORMAT_S24_LE = PA_SAMPLE_S24_32LE,
152
-        SND_PCM_FORMAT_S24_BE = PA_SAMPLE_S24_32BE,
153
+    static const snd_pcm_format_t format_trans_to_pcm = {
154
+        PA_SAMPLE_U8 = SND_PCM_FORMAT_U8,
155
+        PA_SAMPLE_ALAW = SND_PCM_FORMAT_A_LAW,
156
+        PA_SAMPLE_ULAW = SND_PCM_FORMAT_MU_LAW,
157
+        PA_SAMPLE_S16LE = SND_PCM_FORMAT_S16_LE,
158
+        PA_SAMPLE_S16BE = SND_PCM_FORMAT_S16_BE,
159
+        PA_SAMPLE_FLOAT32LE = SND_PCM_FORMAT_FLOAT_LE,
160
+        PA_SAMPLE_FLOAT32BE = SND_PCM_FORMAT_FLOAT_BE,
161
+        PA_SAMPLE_S32LE = SND_PCM_FORMAT_S32_LE,
162
+        PA_SAMPLE_S32BE = SND_PCM_FORMAT_S32_BE,
163
+        PA_SAMPLE_S24LE = SND_PCM_FORMAT_S24_3LE,
164
+        PA_SAMPLE_S24BE = SND_PCM_FORMAT_S24_3BE,
165
+        PA_SAMPLE_S24_32LE = SND_PCM_FORMAT_S24_LE,
166
+        PA_SAMPLE_S24_32BE = SND_PCM_FORMAT_S24_BE,
167
     };
168
-    static const snd_pcm_format_t all_formats = {
169
-        SND_PCM_FORMAT_U8,
170
-        SND_PCM_FORMAT_A_LAW,
171
-        SND_PCM_FORMAT_MU_LAW,
172
-        SND_PCM_FORMAT_S16_LE,
173
-        SND_PCM_FORMAT_S16_BE,
174
-        SND_PCM_FORMAT_FLOAT_LE,
175
-        SND_PCM_FORMAT_FLOAT_BE,
176
-        SND_PCM_FORMAT_S32_LE,
177
-        SND_PCM_FORMAT_S32_BE,
178
-        SND_PCM_FORMAT_S24_3LE,
179
-        SND_PCM_FORMAT_S24_3BE,
180
-        SND_PCM_FORMAT_S24_LE,
181
-        SND_PCM_FORMAT_S24_BE,
182
+    static const pa_sample_format_t all_formats = {
183
+        PA_SAMPLE_U8,
184
+        PA_SAMPLE_ALAW,
185
+        PA_SAMPLE_ULAW,
186
+        PA_SAMPLE_S16LE,
187
+        PA_SAMPLE_S16BE,
188
+        PA_SAMPLE_FLOAT32LE,
189
+        PA_SAMPLE_FLOAT32BE,
190
+        PA_SAMPLE_S32LE,
191
+        PA_SAMPLE_S32BE,
192
+        PA_SAMPLE_S24LE,
193
+        PA_SAMPLE_S24BE,
194
+        PA_SAMPLE_S24_32LE,
195
+        PA_SAMPLE_S24_32BE,
196
     };
197
     bool supportedPA_ELEMENTSOF(all_formats) = {
198
         false,
199
@@ -1510,7 +1554,7 @@
200
     }
201
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
37
 
1
@@ -64,6 +64,8 @@
2
         snd_pcm_uframes_t tsched_size,
3
         bool *use_mmap,                   /* modified at return */
4
         bool *use_tsched,                 /* modified at return */
5
+        pa_sample_format_t **query_supported_formats, /* modified at return */
6
+        unsigned int **query_supported_rates,         /* modified at return */
7
         pa_alsa_profile_set *ps,
8
         pa_alsa_mapping **mapping);       /* modified at return */
9
 #endif
10
@@ -80,6 +82,8 @@
11
         snd_pcm_uframes_t tsched_size,
12
         bool *use_mmap,                   /* modified at return */
13
         bool *use_tsched,                 /* modified at return */
14
+        pa_sample_format_t **query_supported_formats, /* modified at return */
15
+        unsigned int **query_supported_rates,         /* modified at return */
16
         pa_alsa_mapping *mapping);
17
 
18
 /* Opens the explicit ALSA device */
19
@@ -94,6 +98,8 @@
20
         snd_pcm_uframes_t tsched_size,
21
         bool *use_mmap,                   /* modified at return */
22
         bool *use_tsched,                 /* modified at return */
23
+        pa_sample_format_t **query_supported_formats, /* modified at return */
24
+        unsigned int **query_supported_rates,         /* modified at return */
25
         bool require_exact_channel_number);
26
 
27
 /* Opens the explicit ALSA device with a fallback list */
28
@@ -109,6 +115,8 @@
29
         snd_pcm_uframes_t tsched_size,
30
         bool *use_mmap,                   /* modified at return */
31
         bool *use_tsched,                 /* modified at return */
32
+        pa_sample_format_t **query_supported_formats, /* modified at return */
33
+        unsigned int **query_supported_rates,        /* modified at return */
34
         bool require_exact_channel_number);
35
 
36
 #if 0
37
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
72
 
1
@@ -47,10 +47,12 @@
2
 #define PA_LIKELY(x) (__builtin_expect(!!(x),1))
3
 #define PA_UNLIKELY(x) (__builtin_expect(!!(x),0))
4
 #define PA_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
5
+#define PA_UNUSED __attribute__ ((unused))
6
 #else
7
 #define PA_LIKELY(x) (x)
8
 #define PA_UNLIKELY(x) (x)
9
 #define PA_PRINTF_FUNC(fmt, arg1)
10
+#define PA_UNUSED
11
 #endif
12
 
13
 #define PA_MIN(a,b)                    \
14
@@ -96,7 +98,7 @@
15
    PA_AVAILABLE_YES = 2,
16
 } pa_available_t;
17
 
18
-#define PA_RATE_MAX (48000U*8U)
19
+#define PA_RATE_MAX (48000U*16U)
20
 
21
 typedef enum pa_sample_format {
22
    PA_SAMPLE_U8,       /**< Unsigned 8 Bit PCM */
23
@@ -348,6 +350,48 @@
24
     pa_xfreev((void**)a);
25
 }
26
 
27
+typedef struct {
28
+   size_t size;
29
+   char *ptr;
30
+   FILE *f;
31
+} pa_strbuf;
32
+
33
+static inline pa_strbuf *pa_strbuf_new(void)
34
+{
35
+   pa_strbuf *s = pa_xnew0(pa_strbuf,1);
36
+   s->f = open_memstream(&s->ptr, &s->size);
37
+   return s;
38
+}
39
+
40
+static PA_PRINTF_FUNC(2,3) inline size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...)
41
+{
42
+   int ret;
43
+   va_list args;
44
+   va_start(args, format);
45
+   ret = vfprintf(sb->f, format, args);
46
+   va_end(args);
47
+   return ret > 0 ? ret : 0;
48
+}
49
+
50
+static inline void pa_strbuf_puts(pa_strbuf *sb, const char *t)
51
+{
52
+   fputs(t, sb->f);
53
+}
54
+
55
+static inline bool pa_strbuf_isempty(pa_strbuf *sb)
56
+{
57
+   fflush(sb->f);
58
+   return sb->size == 0;
59
+}
60
+
61
+static inline char *pa_strbuf_to_string_free(pa_strbuf *sb)
62
+{
63
+   char *ptr;
64
+   fclose(sb->f);
65
+   ptr = sb->ptr;
66
+   free(sb);
67
+   return ptr;
68
+}
69
 
70
 #define pa_cstrerror   strerror
71
 
72
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
141
 
1
@@ -74,7 +74,13 @@
2
 {
3
    pa_idxset_item *item;
4
    pa_array_for_each(item, &s->array) {
5
-       if (item->ptr == ptr)
6
+       if (item->ptr == NULL) {
7
+           if (ptr == NULL)
8
+               return item;
9
+           else
10
+               continue;
11
+       }
12
+       if (s->compare_func(item->ptr, ptr) == 0)
13
            return item;
14
    }
15
    return NULL;
16
@@ -124,13 +130,25 @@
17
    return count;
18
 }
19
 
20
-static inline void *pa_idxset_search(pa_idxset *s, uint32_t *idx)
21
+static inline pa_idxset_item *pa_idxset_search(pa_idxset *s, uint32_t *idx)
22
 {
23
         pa_idxset_item *item;
24
    for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item);
25
         pa_array_check(&s->array, item); item++, (*idx)++) {
26
        if (item->ptr != NULL)
27
-           return item->ptr;
28
+           return item;
29
+   }
30
+   *idx = PA_IDXSET_INVALID;
31
+   return NULL;
32
+}
33
+
34
+static inline pa_idxset_item *pa_idxset_reverse_search(pa_idxset *s, uint32_t *idx)
35
+{
36
+        pa_idxset_item *item;
37
+   for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item);
38
+        pa_array_check(&s->array, item); item--, (*idx)--) {
39
+       if (item->ptr != NULL)
40
+           return item;
41
    }
42
    *idx = PA_IDXSET_INVALID;
43
    return NULL;
44
@@ -138,29 +156,93 @@
45
 
46
 static inline void *pa_idxset_next(pa_idxset *s, uint32_t *idx)
47
 {
48
+   pa_idxset_item *item;
49
    (*idx)++;;
50
-   return pa_idxset_search(s, idx);
51
+   item = pa_idxset_search(s, idx);
52
+   return item ? item->ptr : NULL;
53
 }
54
 
55
 static inline void* pa_idxset_first(pa_idxset *s, uint32_t *idx)
56
 {
57
    uint32_t i = 0;
58
-   void *ptr = pa_idxset_search(s, &i);
59
+   pa_idxset_item *item = pa_idxset_search(s, &i);
60
    if (idx)
61
        *idx = i;
62
+   return item ? item->ptr : NULL;
63
+}
64
+
65
+static inline void* pa_idxset_last(pa_idxset *s, uint32_t *idx)
66
+{
67
+   uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1;
68
+   pa_idxset_item *item = pa_idxset_reverse_search(s, &i);
69
+   if (idx)
70
+       *idx = i;
71
+   return item ? item->ptr : NULL;
72
+}
73
+
74
+static inline void* pa_idxset_steal_last(pa_idxset *s, uint32_t *idx)
75
+{
76
+   uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1;
77
+   void *ptr = NULL;
78
+   pa_idxset_item *item = pa_idxset_reverse_search(s, &i);
79
+   if (idx)
80
+       *idx = i;
81
+   if (item) {
82
+       ptr = item->ptr;
83
+       item->ptr = NULL;
84
+       pa_array_remove(&s->array, item);
85
+   }
86
    return ptr;
87
 }
88
 
89
 static inline void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx)
90
 {
91
    pa_idxset_item *item = pa_idxset_find(s, p);
92
-   if (item == NULL)
93
+   if (item == NULL) {
94
+       if (idx)
95
+           *idx = PA_IDXSET_INVALID;
96
        return NULL;
97
+   }
98
    if (idx)
99
        *idx = item - (pa_idxset_item*)s->array.data;
100
    return item->ptr;
101
 }
102
 
103
+static inline bool pa_idxset_contains(pa_idxset *s, const void *p)
104
+{
105
+   return pa_idxset_get_by_data(s, p, NULL) == p;
106
+}
107
+
108
+static inline bool pa_idxset_isdisjoint(pa_idxset *s, pa_idxset *t)
109
+{
110
+   pa_idxset_item *item;
111
+   pa_array_for_each(item, &s->array) {
112
+       if (item->ptr && pa_idxset_contains(t, item->ptr))
113
+           return false;
114
+   }
115
+   return true;
116
+}
117
+
118
+static inline bool pa_idxset_issubset(pa_idxset *s, pa_idxset *t)
119
+{
120
+   pa_idxset_item *item;
121
+   pa_array_for_each(item, &s->array) {
122
+       if (item->ptr && !pa_idxset_contains(t, item->ptr))
123
+           return false;
124
+   }
125
+   return true;
126
+}
127
+
128
+static inline bool pa_idxset_issuperset(pa_idxset *s, pa_idxset *t)
129
+{
130
+    return pa_idxset_issubset(t, s);
131
+}
132
+
133
+static inline bool pa_idxset_equals(pa_idxset *s, pa_idxset *t)
134
+{
135
+    return pa_idxset_issubset(s, t) && pa_idxset_issuperset(s, t);
136
+}
137
+
138
 static inline void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx)
139
 {
140
         pa_idxset_item *item;
141
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
27
 
1
@@ -703,14 +703,23 @@
2
        }
3
    }
4
 
5
-   check_position_and_clock_config(this);
6
+   if (SPA_LIKELY(this->node_position_io != NULL)) {
7
+       this->cycle_duration = this->node_position_io->clock.target_duration;
8
+       this->cycle_rate = this->node_position_io->clock.target_rate.denom;
9
+   } else {
10
+       /* This can happen at the very beginning if node_position_io
11
+        * isn't passed to this node in time. */
12
+       this->cycle_duration = 1024;
13
+       this->cycle_rate = 48000;
14
+   }
15
 
16
    current_time = this->next_driver_time;
17
 
18
    this->next_driver_time += ((uint64_t)(this->cycle_duration)) * 1000000000ULL / this->cycle_rate;
19
    if (this->node_clock_io != NULL) {
20
        this->node_clock_io->nsec = current_time;
21
-       this->node_clock_io->position += this->cycle_duration;
22
+       this->node_clock_io->rate = this->node_clock_io->target_rate;
23
+       this->node_clock_io->position += this->node_clock_io->duration;
24
        this->node_clock_io->duration = this->cycle_duration;
25
        this->node_clock_io->delay = 0;
26
        this->node_clock_io->rate_diff = 1.0;
27
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
9
 
1
@@ -630,6 +630,7 @@
2
        this->port_info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
3
        this->port_paramsPORT_Latency.user++;
4
        emit_port_info(this, false);
5
+       res = 0;
6
        break;
7
    }
8
    default:
9
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
201
 
1
@@ -122,6 +122,8 @@
2
        state->disable_mmap = spa_atob(s);
3
    } else if (spa_streq(k, "api.alsa.disable-batch")) {
4
        state->disable_batch = spa_atob(s);
5
+   } else if (spa_streq(k, "api.alsa.disable-tsched")) {
6
+       state->disable_tsched = spa_atob(s);
7
    } else if (spa_streq(k, "api.alsa.use-chmap")) {
8
        state->props.use_chmap = spa_atob(s);
9
    } else if (spa_streq(k, "api.alsa.multi-rate")) {
10
@@ -284,12 +286,20 @@
11
    case 11:
12
        param = spa_pod_builder_add_object(b,
13
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
14
+           SPA_PROP_INFO_name, SPA_POD_String("api.alsa.disable-tsched"),
15
+           SPA_PROP_INFO_description, SPA_POD_String("Disable timer based scheduling"),
16
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->disable_tsched),
17
+           SPA_PROP_INFO_params, SPA_POD_Bool(true));
18
+       break;
19
+   case 12:
20
+       param = spa_pod_builder_add_object(b,
21
+           SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
22
            SPA_PROP_INFO_name, SPA_POD_String("api.alsa.use-chmap"),
23
            SPA_PROP_INFO_description, SPA_POD_String("Use the driver channelmap"),
24
            SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->props.use_chmap),
25
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
26
        break;
27
-   case 12:
28
+   case 13:
29
        param = spa_pod_builder_add_object(b,
30
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
31
            SPA_PROP_INFO_name, SPA_POD_String("api.alsa.multi-rate"),
32
@@ -297,7 +307,7 @@
33
            SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(state->multi_rate),
34
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
35
        break;
36
-   case 13:
37
+   case 14:
38
        param = spa_pod_builder_add_object(b,
39
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
40
            SPA_PROP_INFO_name, SPA_POD_String("latency.internal.rate"),
41
@@ -306,7 +316,7 @@
42
                0, 65536),
43
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
44
        break;
45
-   case 14:
46
+   case 15:
47
        param = spa_pod_builder_add_object(b,
48
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
49
            SPA_PROP_INFO_name, SPA_POD_String("latency.internal.ns"),
50
@@ -315,7 +325,7 @@
51
                0LL, 2 * SPA_NSEC_PER_SEC),
52
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
53
        break;
54
-   case 15:
55
+   case 16:
56
        param = spa_pod_builder_add_object(b,
57
            SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
58
            SPA_PROP_INFO_name, SPA_POD_String("clock.name"),
59
@@ -375,6 +385,9 @@
60
    spa_pod_builder_string(b, "api.alsa.disable-batch");
61
    spa_pod_builder_bool(b, state->disable_batch);
62
 
63
+   spa_pod_builder_string(b, "api.alsa.disable-tsched");
64
+   spa_pod_builder_bool(b, state->disable_tsched);
65
+
66
    spa_pod_builder_string(b, "api.alsa.use-chmap");
67
    spa_pod_builder_bool(b, state->props.use_chmap);
68
 
69
@@ -557,11 +570,16 @@
70
            device_name,
71
            state->stream == SND_PCM_STREAM_CAPTURE ? "capture" : "playback");
72
 
73
-   if ((err = spa_system_timerfd_create(state->data_system,
74
-           CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0)
75
-       goto error_exit_close;
76
+   if (!state->disable_tsched) {
77
+       if ((err = spa_system_timerfd_create(state->data_system,
78
+               CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0)
79
+           goto error_exit_close;
80
 
81
-   state->timerfd = err;
82
+       state->timerfd = err;
83
+   } else {
84
+       /* ALSA pollfds may only be ready after setting swparams, so
85
+        * these are initialised in spa_alsa_start() */
86
+   }
87
 
88
    if (state->clock)
89
        spa_scnprintf(state->clock->name, sizeof(state->clock->name),
90
@@ -593,7 +611,10 @@
91
        spa_log_warn(state->log, "%s: close failed: %s", state->props.device,
92
                snd_strerror(err));
93
 
94
-   spa_system_close(state->data_system, state->timerfd);
95
+   if (!state->disable_tsched)
96
+       spa_system_close(state->data_system, state->timerfd);
97
+   else
98
+       state->n_fds = 0;
99
 
100
    if (state->have_format)
101
        state->card->format_ref--;
102
@@ -1555,14 +1576,15 @@
103
         * the period smaller and add one period of headroom. Limit the
104
         * period size to our default so that we don't create too much
105
         * headroom. */
106
-       period_size = SPA_MIN(period_size, DEFAULT_PERIOD) / 2;
107
+       if (!state->disable_tsched)
108
+           period_size = SPA_MIN(period_size, DEFAULT_PERIOD) / 2;
109
        spa_log_info(state->log, "%s: batch mode, period_size:%ld",
110
            state->props.device, period_size);
111
    } else {
112
        if (period_size == 0)
113
            period_size = DEFAULT_PERIOD;
114
-       /* disable ALSA wakeups, we use a timer */
115
-       if (snd_pcm_hw_params_can_disable_period_wakeup(params))
116
+       /* disable ALSA wakeups, if we use a timer */
117
+       if (!state->disable_tsched && snd_pcm_hw_params_can_disable_period_wakeup(params))
118
            CHECK(snd_pcm_hw_params_set_period_wakeup(hndl, params, 0), "set_period_wakeup");
119
    }
120
 
121
@@ -1594,7 +1616,9 @@
122
    }
123
 
124
    state->headroom = state->default_headroom;
125
-   if (is_batch)
126
+   /* If tsched is disabled, we know the pointers are updated when we wake
127
+    * up, so we don't need the additional headroom */
128
+   if (is_batch && !state->disable_tsched)
129
        state->headroom += period_size;
130
 
131
    state->max_delay = state->buffer_frames / 2;
132
@@ -1613,14 +1637,15 @@
133
 
134
    spa_log_info(state->log, "%s (%s): format:%s access:%s-%s rate:%d channels:%d "
135
            "buffer frames %lu, period frames %lu, periods %u, frame_size %zd "
136
-           "headroom %u start-delay:%u",
137
+           "headroom %u start-delay:%u tsched:%u",
138
            state->props.device,
139
            state->stream == SND_PCM_STREAM_CAPTURE ? "capture" : "playback",
140
            snd_pcm_format_name(state->format),
141
            state->use_mmap ? "mmap" : "rw",
142
            planar ? "planar" : "interleaved",
143
            state->rate, state->channels, state->buffer_frames, state->period_frames,
144
-           periods, state->frame_size, state->headroom, state->start_delay);
145
+           periods, state->frame_size, state->headroom, state->start_delay,
146
+           !state->disable_tsched);
147
 
148
    /* write the parameters to device */
149
    CHECK(snd_pcm_hw_params(hndl, params), "set_hw_params");
150
@@ -1653,7 +1678,21 @@
151
    /* start the transfer */
152
    CHECK(snd_pcm_sw_params_set_start_threshold(hndl, params, LONG_MAX), "set_start_threshold");
153
 
154
-   CHECK(snd_pcm_sw_params_set_period_event(hndl, params, 0), "set_period_event");
155
+   CHECK(snd_pcm_sw_params_set_period_event(hndl, params, state->disable_tsched), "set_period_event");
156
+
157
+   if (state->disable_tsched) {
158
+       snd_pcm_uframes_t avail_min;
159
+
160
+       if (state->stream == SND_PCM_STREAM_PLAYBACK) {
161
+           /* wake up when buffer has target frames or less data (will underrun soon) */
162
+           avail_min = state->buffer_frames - state->threshold;
163
+       } else {
164
+           /* wake up when there's target frames or more (enough for us to read and push a buffer) */
165
+           avail_min = state->threshold;
166
+       }
167
+
168
+       CHECK(snd_pcm_sw_params_set_avail_min(hndl, params, avail_min), "set_avail_min");
169
+   }
170
 
171
    /* write the parameters to the playback device */
172
    CHECK(snd_pcm_sw_params(hndl, params), "sw_params");
173
@@ -1671,6 +1710,9 @@
174
 {
175
    struct itimerspec ts;
176
 
177
+   if (state->disable_tsched)
178
+       return 0;
179
+
180
    ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC;
181
    ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC;
182
    ts.it_interval.tv_sec = 0;
183
@@ -1952,7 +1994,8 @@
184
 
185
    if (SPA_LIKELY(!follower && state->clock)) {
186
        state->clock->nsec = current_time;
187
-       state->clock->position += state->duration;
188
+       state->clock->rate = state->clock->target_rate;
189
+       state->clock->position += state->clock->duration;
190
        state->clock->duration = state->duration;
191
        state->clock->delay = delay + state->delay;
192
        state->clock->rate_diff = corr;
193
@@ -1993,20 +2036,39 @@
194
    return 0;
195
 }
196
 
197
-static inline void check_position_config(struct state *state)
198
+static void update_sources(struct state *state, bool active)
199
 {
200
+   if (state->disable_tsched && state->source0.data != NULL) {
201
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
30
 
1
@@ -47,6 +47,7 @@
2
 };
3
 
4
 #define MAX_BUFFERS 32
5
+#define MAX_POLL 16
6
 
7
 struct buffer {
8
    uint32_t id;
9
@@ -131,6 +132,7 @@
10
    struct channel_map default_pos;
11
    unsigned int disable_mmap;
12
    unsigned int disable_batch;
13
+   unsigned int disable_tsched;
14
    char clock_name64;
15
    uint32_t quantum_limit;
16
 
17
@@ -171,8 +173,11 @@
18
    size_t ready_offset;
19
 
20
    bool started;
21
-   struct spa_source source;
22
+   /* Either a single source for tsched, or a set of pollfds from ALSA */
23
+   struct spa_source sourceMAX_POLL;
24
    int timerfd;
25
+   struct pollfd pfdsMAX_POLL;
26
+   int n_fds;
27
    uint32_t threshold;
28
    uint32_t last_threshold;
29
    uint32_t headroom;
30
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
39
 
1
@@ -684,7 +684,7 @@
2
 
3
 static void update_position(struct seq_state *state)
4
 {
5
-   if (state->position) {
6
+   if (SPA_LIKELY(state->position)) {
7
        struct spa_io_clock *clock = &state->position->clock;
8
        state->rate = clock->rate;
9
        if (state->rate.num == 0 || state->rate.denom == 0)
10
@@ -744,7 +744,8 @@
11
 
12
    if (!follower && state->clock) {
13
        state->clock->nsec = nsec;
14
-       state->clock->position += state->duration;
15
+       state->clock->rate = state->rate;
16
+       state->clock->position += state->clock->duration;
17
        state->clock->duration = state->duration;
18
        state->clock->delay = state->duration * corr;
19
        state->clock->rate_diff = corr;
20
@@ -793,7 +794,17 @@
21
 
22
    spa_log_trace(state->log, "timeout %"PRIu64, state->current_time);
23
 
24
-   update_position(state);
25
+   if (SPA_LIKELY(state->position)) {
26
+       struct spa_io_clock *clock = &state->position->clock;
27
+       state->rate = clock->target_rate;
28
+       if (state->rate.num == 0 || state->rate.denom == 0)
29
+           state->rate = SPA_FRACTION(1, 48000);
30
+       state->duration = clock->target_duration;
31
+   } else {
32
+       state->rate = SPA_FRACTION(1, 48000);
33
+       state->duration = 1024;
34
+   }
35
+   state->threshold = state->duration;
36
 
37
    update_time(state, state->current_time, false);
38
 
39
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
20
 
1
@@ -21,16 +21,14 @@
2
 General
3
 auto-profiles = yes
4
 
5
-Mapping analog-stereo-front
6
-description = Analog Stereo Front
7
+Mapping analog-stereo-headset
8
 device-strings = hw:%f,1
9
 channel-map = left,right
10
 paths-output = analog-output analog-output-headphones
11
 paths-input = analog-input-mic analog-input-headphone-mic analog-input-headset-mic
12
 priority = 15
13
 
14
-Mapping analog-stereo-rear
15
-description = Analog Stereo Rear
16
+Mapping analog-stereo
17
 device-strings = hw:%f,0
18
 channel-map = left,right
19
 paths-output = analog-output analog-output-speaker
20
pipewire-0.3.67.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/audioadapter.c Changed
201
 
1
@@ -177,6 +177,7 @@
2
                    SPA_PARAM_PORT_CONFIG_mode,      SPA_POD_Id(
3
                        SPA_PARAM_PORT_CONFIG_MODE_passthrough));
4
                result.next++;
5
+               res = 1;
6
                break;
7
            default:
8
                return 0;
9
@@ -226,30 +227,40 @@
10
 static int link_io(struct impl *this)
11
 {
12
    int res;
13
-
14
-   if (this->convert == NULL)
15
-       return 0;
16
+   struct spa_io_rate_match *rate_match;
17
+   size_t rate_match_size;
18
 
19
    spa_log_debug(this->log, "%p: controls", this);
20
 
21
    spa_zero(this->io_rate_match);
22
    this->io_rate_match.rate = 1.0;
23
 
24
+   if (this->follower == this->target) {
25
+       rate_match = NULL;
26
+       rate_match_size = 0;
27
+   } else {
28
+       rate_match = &this->io_rate_match;
29
+       rate_match_size = sizeof(this->io_rate_match);
30
+   }
31
+
32
    if ((res = spa_node_port_set_io(this->follower,
33
            this->direction, 0,
34
            SPA_IO_RateMatch,
35
-           &this->io_rate_match, sizeof(this->io_rate_match))) < 0) {
36
+           rate_match, rate_match_size)) < 0) {
37
        spa_log_debug(this->log, "%p: set RateMatch on follower disabled %d %s", this,
38
            res, spa_strerror(res));
39
    }
40
    else if ((res = spa_node_port_set_io(this->convert,
41
            SPA_DIRECTION_REVERSE(this->direction), 0,
42
            SPA_IO_RateMatch,
43
-           &this->io_rate_match, sizeof(this->io_rate_match))) < 0) {
44
+           rate_match, rate_match_size)) < 0) {
45
        spa_log_warn(this->log, "%p: set RateMatch on convert failed %d %s", this,
46
            res, spa_strerror(res));
47
    }
48
 
49
+   if (this->follower == this->target)
50
+       return 0;
51
+
52
    this->io_buffers = SPA_IO_BUFFERS_INIT;
53
 
54
    if ((res = spa_node_port_set_io(this->follower,
55
@@ -355,11 +366,11 @@
56
    struct spa_data *datas;
57
    uint64_t follower_flags, conv_flags;
58
 
59
-   spa_log_debug(this->log, "%p: n_buffers:%d", this, this->n_buffers);
60
-
61
    if (this->target == this->follower)
62
        return 0;
63
 
64
+   spa_log_debug(this->log, "%p: n_buffers:%d", this, this->n_buffers);
65
+
66
    if (this->n_buffers > 0)
67
        return 0;
68
 
69
@@ -487,7 +498,7 @@
70
        format = fmt;
71
    }
72
 
73
-   if (this->target != this->follower && this->convert) {
74
+   if (this->target != this->follower) {
75
        if ((res = spa_node_port_set_param(this->convert,
76
                       SPA_DIRECTION_REVERSE(this->direction), 0,
77
                       SPA_PARAM_Format, flags,
78
@@ -565,12 +576,13 @@
79
        } else {
80
            /* add converter ports */
81
            configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_dsp);
82
-           link_io(this);
83
        }
84
+       link_io(this);
85
    }
86
-
87
    this->info.change_mask |= SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PARAMS;
88
-   this->info.flags &= ~SPA_NODE_FLAG_NEED_CONFIGURE;
89
+   SPA_FLAG_CLEAR(this->info.flags, SPA_NODE_FLAG_NEED_CONFIGURE);
90
+   SPA_FLAG_UPDATE(this->info.flags, SPA_NODE_FLAG_ASYNC,
91
+           this->async && this->follower == this->target);
92
    this->paramsIDX_Props.user++;
93
 
94
    emit_node_info(this, false);
95
@@ -746,17 +758,16 @@
96
    struct spa_pod_builder b = { 0 };
97
    int res;
98
 
99
+   if (this->target == this->follower)
100
+       return 0;
101
+
102
    spa_log_debug(this->log, "%p: have_format:%d", this, this->have_format);
103
 
104
    if (this->have_format)
105
        return 0;
106
 
107
-   if (this->target == this->follower)
108
-       return 0;
109
-
110
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
111
 
112
-
113
    spa_node_send_command(this->follower,
114
            &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_ParamBegin));
115
 
116
@@ -774,18 +785,16 @@
117
            goto done;
118
        }
119
    }
120
-   if (this->convert) {
121
-       state = 0;
122
-       if ((res = spa_node_port_enum_params_sync(this->convert,
123
-                   SPA_DIRECTION_REVERSE(this->direction), 0,
124
-                   SPA_PARAM_EnumFormat, &state,
125
-                   format, &format, &b)) != 1) {
126
-           debug_params(this, this->convert,
127
-                   SPA_DIRECTION_REVERSE(this->direction), 0,
128
-                   SPA_PARAM_EnumFormat, format, "convert format", res);
129
-           res = -ENOTSUP;
130
-           goto done;
131
-       }
132
+   state = 0;
133
+   if ((res = spa_node_port_enum_params_sync(this->convert,
134
+               SPA_DIRECTION_REVERSE(this->direction), 0,
135
+               SPA_PARAM_EnumFormat, &state,
136
+               format, &format, &b)) != 1) {
137
+       debug_params(this, this->convert,
138
+               SPA_DIRECTION_REVERSE(this->direction), 0,
139
+               SPA_PARAM_EnumFormat, format, "convert format", res);
140
+       res = -ENOTSUP;
141
+       goto done;
142
    }
143
    if (format == NULL) {
144
        res = -ENOTSUP;
145
@@ -998,6 +1007,8 @@
146
        this->info.flags |= SPA_NODE_FLAG_OUT_PORT_CONFIG;
147
        this->info.max_output_ports = MAX_PORTS;
148
    }
149
+   SPA_FLAG_UPDATE(this->info.flags, SPA_NODE_FLAG_ASYNC,
150
+           this->async && this->follower == this->target);
151
 
152
    spa_log_debug(this->log, "%p: follower info %s", this,
153
            this->direction == SPA_DIRECTION_INPUT ?
154
@@ -1229,7 +1240,7 @@
155
    int res;
156
    struct impl *this = data;
157
 
158
-   if (this->target != this->follower && this->convert)
159
+   if (this->target != this->follower)
160
        res = spa_node_port_reuse_buffer(this->convert, port_id, buffer_id);
161
    else
162
        res = spa_node_call_reuse_buffer(&this->callbacks, port_id, buffer_id);
163
@@ -1272,11 +1283,10 @@
164
        spa_node_add_listener(this->follower, &l, &follower_node_events, this);
165
        spa_hook_remove(&l);
166
 
167
-       if (this->convert) {
168
-           spa_zero(l);
169
-           spa_node_add_listener(this->convert, &l, &convert_node_events, this);
170
-           spa_hook_remove(&l);
171
-       }
172
+       spa_zero(l);
173
+       spa_node_add_listener(this->convert, &l, &convert_node_events, this);
174
+       spa_hook_remove(&l);
175
+
176
        this->add_listener = false;
177
 
178
        emit_node_info(this, true);
179
@@ -1466,7 +1476,7 @@
180
         * First we run the converter to process the input for the follower
181
         * then if it produced data, we run the follower. */
182
        while (retry--) {
183
-           status = this->convert ? spa_node_process(this->convert) : 0;
184
+           status = spa_node_process(this->convert);
185
            /* schedule the follower when the converter needed
186
             * a recycled buffer */
187
            if (status == -EPIPE || status == 0)
188
@@ -1498,7 +1508,7 @@
189
            /* output node (source). First run the converter to make
190
             * sure we push out any queued data. Then when it needs
191
             * more data, schedule the follower. */
192
-           status = this->convert ? spa_node_process(this->convert) : 0;
193
+           status = spa_node_process(this->convert);
194
            if (status == 0)
195
                status = SPA_STATUS_NEED_DATA;
196
            else if (status < 0)
197
@@ -1660,6 +1670,9 @@
198
                info, support, n_support);
199
 
200
    spa_handle_get_interface(this->hnd_convert, SPA_TYPE_INTERFACE_Node, &iface);
201
pipewire-0.3.67.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/audioconvert.c Changed
201
 
1
@@ -23,12 +23,14 @@
2
 #include <spa/param/param.h>
3
 #include <spa/param/latency-utils.h>
4
 #include <spa/pod/filter.h>
5
+#include <spa/pod/dynamic.h>
6
 #include <spa/debug/types.h>
7
 
8
 #include "volume-ops.h"
9
 #include "fmt-ops.h"
10
 #include "channelmix-ops.h"
11
 #include "resample.h"
12
+#include "wavfile.h"
13
 
14
 #undef SPA_LOG_TOPIC_DEFAULT
15
 #define SPA_LOG_TOPIC_DEFAULT log_topic
16
@@ -60,18 +62,29 @@
17
        vol->volumesi = DEFAULT_VOLUME;
18
 }
19
 
20
+struct volume_ramp_params {
21
+   unsigned int volume_ramp_samples;
22
+   unsigned int volume_ramp_step_samples;
23
+   unsigned int volume_ramp_time;
24
+   unsigned int volume_ramp_step_time;
25
+   enum spa_audio_volume_ramp_scale scale;
26
+};
27
+
28
 struct props {
29
    float volume;
30
+   float prev_volume;
31
    uint32_t n_channels;
32
    uint32_t channel_mapSPA_AUDIO_MAX_CHANNELS;
33
    struct volumes channel;
34
    struct volumes soft;
35
    struct volumes monitor;
36
+   struct volume_ramp_params vrp;
37
    unsigned int have_soft_volume:1;
38
    unsigned int mix_disabled:1;
39
    unsigned int resample_disabled:1;
40
    unsigned int resample_quality;
41
    double rate;
42
+   char wav_path512;
43
 };
44
 
45
 static void props_reset(struct props *props)
46
@@ -89,6 +102,7 @@
47
    props->resample_disabled = false;
48
    props->resample_quality = RESAMPLE_DEFAULT_QUALITY;
49
    props->rate = 1.0;
50
+   spa_zero(props->wav_path);
51
 }
52
 
53
 struct buffer {
54
@@ -192,6 +206,8 @@
55
    struct resample resample;
56
    struct volume volume;
57
    double rate_scale;
58
+   struct spa_pod_sequence *vol_ramp_sequence;
59
+   uint32_t vol_ramp_offset;
60
 
61
    uint32_t in_offset;
62
    uint32_t out_offset;
63
@@ -199,6 +215,7 @@
64
    unsigned int setup:1;
65
    unsigned int resample_peaks:1;
66
    unsigned int is_passthrough:1;
67
+   unsigned int ramp_volume:1;
68
    unsigned int drained:1;
69
    unsigned int rate_adjust:1;
70
 
71
@@ -207,6 +224,8 @@
72
    float *scratch;
73
    float *tmp2;
74
    float *tmp_datas2MAX_PORTS;
75
+
76
+   struct wav_file *wav_file;
77
 };
78
 
79
 #define CHECK_PORT(this,d,p)       ((p) < this->dird.n_ports)
80
@@ -637,6 +656,14 @@
81
            spa_pod_builder_pop(&b, &f1);
82
            param = spa_pod_builder_pop(&b, &f0);
83
            break;
84
+       case 24:
85
+           param = spa_pod_builder_add_object(&b,
86
+               SPA_TYPE_OBJECT_PropInfo, id,
87
+               SPA_PROP_INFO_name, SPA_POD_String("debug.wav-path"),
88
+               SPA_PROP_INFO_description, SPA_POD_String("Path to WAV file"),
89
+               SPA_PROP_INFO_type, SPA_POD_String(p->wav_path),
90
+               SPA_PROP_INFO_params, SPA_POD_Bool(true));
91
+           break;
92
        default:
93
            return 0;
94
        }
95
@@ -709,6 +736,8 @@
96
            spa_pod_builder_int(&b, this->dir1.conv.noise_bits);
97
            spa_pod_builder_string(&b, "dither.method");
98
            spa_pod_builder_string(&b, dither_method_infothis->dir1.conv.method.label);
99
+           spa_pod_builder_string(&b, "debug.wav-path");
100
+           spa_pod_builder_string(&b, p->wav_path);
101
            spa_pod_builder_pop(&b, &f1);
102
            param = spa_pod_builder_pop(&b, &f0);
103
            break;
104
@@ -782,6 +811,10 @@
105
        spa_atou32(s, &this->dir1.conv.noise_bits, 0);
106
    else if (spa_streq(k, "dither.method"))
107
        this->dir1.conv.method = dither_method_from_label(s);
108
+   else if (spa_streq(k, "debug.wav-path")) {
109
+       spa_scnprintf(this->props.wav_path,
110
+               sizeof(this->props.wav_path), "%s", s ? s : "");
111
+   }
112
    else
113
        return 0;
114
    return 1;
115
@@ -837,6 +870,127 @@
116
    return changed;
117
 }
118
 
119
+static int get_ramp_samples(struct impl *this)
120
+{
121
+   struct volume_ramp_params *vrp = &this->props.vrp;
122
+   int samples = -1;
123
+
124
+   if (vrp->volume_ramp_samples)
125
+       samples = vrp->volume_ramp_samples;
126
+   else if (vrp->volume_ramp_time) {
127
+       struct dir *d = &this->dirSPA_DIRECTION_OUTPUT;
128
+       unsigned int sample_rate = d->format.info.raw.rate;
129
+       samples = (vrp->volume_ramp_time * sample_rate) / 1000;
130
+       spa_log_info(this->log, "volume ramp samples calculated from time is %d", samples);
131
+   }
132
+   if (!samples)
133
+       samples = -1;
134
+
135
+   return samples;
136
+
137
+}
138
+
139
+static int get_ramp_step_samples(struct impl *this)
140
+{
141
+   struct volume_ramp_params *vrp = &this->props.vrp;
142
+   int samples = -1;
143
+
144
+   if (vrp->volume_ramp_step_samples)
145
+       samples = vrp->volume_ramp_step_samples;
146
+   else if (vrp->volume_ramp_step_time) {
147
+       struct dir *d = &this->dirSPA_DIRECTION_OUTPUT;
148
+       int sample_rate = d->format.info.raw.rate;
149
+       /* convert the step time which is in nano seconds to seconds */
150
+       samples = (vrp->volume_ramp_step_time/1000) * (sample_rate/1000);
151
+       spa_log_debug(this->log, "volume ramp step samples calculated from time is %d", samples);
152
+   }
153
+   if (!samples)
154
+       samples = -1;
155
+
156
+   return samples;
157
+
158
+}
159
+
160
+static double get_volume_at_scale(struct impl *this, double value)
161
+{
162
+   struct volume_ramp_params *vrp = &this->props.vrp;
163
+   if (vrp->scale == SPA_AUDIO_VOLUME_RAMP_LINEAR || vrp->scale == SPA_AUDIO_VOLUME_RAMP_INVALID)
164
+       return value;
165
+   else if (vrp->scale == SPA_AUDIO_VOLUME_RAMP_CUBIC)
166
+       return (value * value * value);
167
+
168
+   return 0.0;
169
+}
170
+
171
+static struct spa_pod *generate_ramp_up_seq(struct impl *this)
172
+{
173
+   struct spa_pod_dynamic_builder b;
174
+   struct spa_pod_frame f1;
175
+   struct props *p = &this->props;
176
+   double volume_accum = p->prev_volume;
177
+   int ramp_samples = get_ramp_samples(this);
178
+   int ramp_step_samples = get_ramp_step_samples(this);
179
+   double volume_step = ((p->volume - p->prev_volume) / (ramp_samples / ramp_step_samples));
180
+   uint32_t volume_offs = 0;
181
+
182
+   spa_pod_dynamic_builder_init(&b, NULL, 0, 4096);
183
+
184
+   spa_pod_builder_push_sequence(&b.b, &f0, 0);
185
+   spa_log_info(this->log, "generating ramp up sequence from %f to %f with a"
186
+       " step value %f at scale %d", p->prev_volume, p->volume, volume_step, p->vrp.scale);
187
+   do {
188
+       // spa_log_debug(this->log, "volume accum %f", get_volume_at_scale(this, volume_accum));
189
+       spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties);
190
+       spa_pod_builder_add_object(&b.b,
191
+               SPA_TYPE_OBJECT_Props, 0,
192
+               SPA_PROP_volume,
193
+               SPA_POD_Float(get_volume_at_scale(this, volume_accum)));
194
+       volume_accum += volume_step;
195
+       volume_offs += ramp_step_samples;
196
+   } while (volume_accum < p->volume);
197
+   return spa_pod_builder_pop(&b.b, &f0);
198
+}
199
+
200
+static struct spa_pod *generate_ramp_down_seq(struct impl *this)
201
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
48
 
1
@@ -629,8 +629,12 @@
2
 
3
    for (jc = 0, ic = 0, i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
4
        float sum = 0.0f;
5
-       char str1024, str21024;
6
-       int idx = 0, idx2 = 0;
7
+       char str11024, str21024;
8
+       struct spa_strbuf sb1, sb2;
9
+
10
+       spa_strbuf_init(&sb1, str1, sizeof(str1));
11
+       spa_strbuf_init(&sb2, str2, sizeof(str2));
12
+
13
        if ((dst_paired & (1UL << i)) == 0)
14
            continue;
15
        for (jc = 0, j = 0; j < SPA_AUDIO_MAX_CHANNELS; j++) {
16
@@ -640,7 +644,7 @@
17
                continue;
18
 
19
            if (ic == 0)
20
-               idx2 += snprintf(str2 + idx2, sizeof(str2) - idx2, "%-4.4s  ",
21
+               spa_strbuf_append(&sb2, "%-4.4s  ",
22
                        src_mask == 0 ? "UNK" :
23
                        spa_debug_type_find_short_name(spa_type_audio_channel, j + _SH));
24
 
25
@@ -648,17 +652,17 @@
26
            sum += fabs(matrixij);
27
 
28
            if (matrixij == 0.0f)
29
-               idx += snprintf(str + idx, sizeof(str) - idx, "      ");
30
+               spa_strbuf_append(&sb1, "      ");
31
            else
32
-               idx += snprintf(str + idx, sizeof(str) - idx, "%1.3f ", matrixij);
33
+               spa_strbuf_append(&sb1, "%1.3f ", matrixij);
34
        }
35
-       if (idx2 > 0)
36
+       if (sb2.pos > 0)
37
            spa_log_info(mix->log, "     %s", str2);
38
-       if (idx > 0) {
39
+       if (sb1.pos > 0) {
40
            spa_log_info(mix->log, "%-4.4s %s   %f",
41
                    dst_mask == 0 ? "UNK" :
42
                    spa_debug_type_find_short_name(spa_type_audio_channel, i + _SH),
43
-                   str, sum);
44
+                   str1, sum);
45
        }
46
 
47
        maxsum = SPA_MAX(maxsum, sum);
48
pipewire-0.3.67.tar.gz/spa/plugins/audioconvert/meson.build -> pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/meson.build Changed
9
 
1
@@ -104,6 +104,7 @@
2
     'peaks-ops.c',
3
     'resample-native.c',
4
     'resample-peaks.c',
5
+    'wavfile.c',
6
     'volume-ops.c' ,
7
   c_args :  simd_cargs, '-O3',
8
   link_with : simd_dependencies,
9
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
27
 
1
@@ -53,7 +53,7 @@
2
    size_t size;
3
    int res;
4
    struct spa_support support1;
5
-   struct spa_dict_item items2;
6
+   struct spa_dict_item items6;
7
    const struct spa_handle_factory *factory;
8
    void *iface;
9
 
10
@@ -70,10 +70,15 @@
11
    spa_assert_se(ctx->convert_handle != NULL);
12
 
13
    items0 = SPA_DICT_ITEM_INIT("clock.quantum-limit", "8192");
14
+   items1 = SPA_DICT_ITEM_INIT("channelmix.upmix", "true");
15
+   items2 = SPA_DICT_ITEM_INIT("channelmix.upmix-method", "psd");
16
+   items3 = SPA_DICT_ITEM_INIT("channelmix.lfe-cutoff", "150");
17
+   items4 = SPA_DICT_ITEM_INIT("channelmix.fc-cutoff", "12000");
18
+   items5 = SPA_DICT_ITEM_INIT("channelmix.rear-delay", "12.0");
19
 
20
    res = spa_handle_factory_init(factory,
21
            ctx->convert_handle,
22
-           &SPA_DICT_INIT(items, 1),
23
+           &SPA_DICT_INIT(items, 6),
24
            support, 1);
25
    spa_assert_se(res >= 0);
26
 
27
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
39
 
1
@@ -546,13 +546,17 @@
2
 }
3
 static void test_lossless_s16(void)
4
 {
5
-   int16_t i;
6
+   int32_t i;
7
 
8
    fprintf(stderr, "test %s:\n", __func__);
9
-   for (i = S16_MIN; i < S16_MAX; i+=3) {
10
-       float v = S16_TO_F32(i);
11
+   for (i = S16_MIN; i <= S16_MAX; i+=3) {
12
+       float v = S16_TO_F32((int16_t)i);
13
        int16_t t = F32_TO_S16(v);
14
        spa_assert_se(i == t);
15
+
16
+       int32_t t2 = F32_TO_S32(v);
17
+       spa_assert_se(i<<16 == t2);
18
+       spa_assert_se(i == t2>>16);
19
    }
20
 }
21
 
22
@@ -561,10 +565,14 @@
23
    uint32_t i;
24
 
25
    fprintf(stderr, "test %s:\n", __func__);
26
-   for (i = U16_MIN; i < U16_MAX; i+=3) {
27
-       float v = U16_TO_F32(i);
28
+   for (i = U16_MIN; i <= U16_MAX; i+=3) {
29
+       float v = U16_TO_F32((uint16_t)i);
30
        uint16_t t = F32_TO_U16(v);
31
        spa_assert_se(i == t);
32
+
33
+       uint32_t t2 = F32_TO_U32(v);
34
+       spa_assert_se(i<<16 == t2);
35
+       spa_assert_se(i == t2>>16);
36
    }
37
 }
38
 
39
pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/wavfile.c Added
201
 
1
@@ -0,0 +1,250 @@
2
+/* PipeWire
3
+ *
4
+ * Copyright © 2022 Wim Taymans
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a
7
+ * copy of this software and associated documentation files (the "Software"),
8
+ * to deal in the Software without restriction, including without limitation
9
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ * and/or sell copies of the Software, and to permit persons to whom the
11
+ * Software is furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice (including the next
14
+ * paragraph) shall be included in all copies or substantial portions of the
15
+ * Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23
+ * DEALINGS IN THE SOFTWARE.
24
+ */
25
+
26
+#include <errno.h>
27
+#include <fcntl.h>
28
+#include <unistd.h>
29
+
30
+#include <spa/utils/string.h>
31
+
32
+#include "wavfile.h"
33
+
34
+#define BLOCK_SIZE 4096
35
+
36
+struct wav_file {
37
+   struct spa_audio_info info;
38
+   int fd;
39
+   const struct format_info *fi;
40
+
41
+   uint32_t length;
42
+
43
+   uint32_t stride;
44
+   uint32_t blocks;
45
+};
46
+
47
+static inline ssize_t write_data(struct wav_file *wf, const void *data, size_t size)
48
+{
49
+   ssize_t len;
50
+   len = write(wf->fd, data, size);
51
+   if (len > 0)
52
+       wf->length += len;
53
+   return len;
54
+}
55
+
56
+static ssize_t writei(struct wav_file *wf, const void **data, size_t samples)
57
+{
58
+   return write_data(wf, data0, samples * wf->stride);
59
+}
60
+
61
+typedef struct {
62
+   uint8_t v3;
63
+} __attribute__ ((packed)) uint24_t;
64
+
65
+#define MAKE_WRITEN_FUNC(name, type)                       \
66
+static ssize_t name (struct wav_file *wf, const void **data, size_t samples)   \
67
+{                                      \
68
+   uint32_t b, n, k, blocks = wf->blocks, chunk;               \
69
+   uint8_t bufBLOCK_SIZE;                      \
70
+   ssize_t res = 0;                            \
71
+   type **d = (type**)data;                        \
72
+   uint32_t chunk_size = sizeof(buf) / (blocks * sizeof(type));        \
73
+   for (n = 0; n < samples; ) {                        \
74
+       type *p = (type*)buf;                       \
75
+       chunk = SPA_MIN(samples - n, chunk_size);           \
76
+       for (k = 0; k < chunk; k++, n++) {              \
77
+           for (b = 0; b < blocks; b++)                \
78
+               *p++ = dbn;                 \
79
+       }                               \
80
+       res += write_data(wf, buf,                  \
81
+               chunk * blocks * sizeof(type));         \
82
+   }                                   \
83
+   return res;                             \
84
+}
85
+
86
+MAKE_WRITEN_FUNC(writen_8, uint8_t);
87
+MAKE_WRITEN_FUNC(writen_16, uint16_t);
88
+MAKE_WRITEN_FUNC(writen_24, uint24_t);
89
+MAKE_WRITEN_FUNC(writen_32, uint32_t);
90
+MAKE_WRITEN_FUNC(writen_64, uint64_t);
91
+
92
+static inline int write_n(int fd, const void *buf, int count)
93
+{
94
+   return write(fd, buf, count) == (ssize_t)count ? count : -errno;
95
+}
96
+
97
+static inline int write_le16(int fd, uint16_t val)
98
+{
99
+   uint8_t buf2 = { val, val >> 8 };
100
+   return write_n(fd, buf, 2);
101
+}
102
+
103
+static inline int write_le32(int fd, uint32_t val)
104
+{
105
+   uint8_t buf4 = { val, val >> 8, val >> 16, val >> 24 };
106
+   return write_n(fd, buf, 4);
107
+}
108
+
109
+#define MAKE_AUDIO_RAW(format,bits,planar,fmt,...) \
110
+   { SPA_MEDIA_TYPE_audio, SPA_MEDIA_SUBTYPE_raw, format, bits, planar, fmt, __VA_ARGS__ }
111
+
112
+static struct format_info {
113
+   uint32_t media_type;
114
+   uint32_t media_subtype;
115
+   uint32_t format;
116
+   uint32_t bits;
117
+   bool planar;
118
+   uint32_t fmt;
119
+   ssize_t (*write) (struct wav_file *wf, const void **data, size_t samples);
120
+} format_info = {
121
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_U8P,         8, true, 1, writen_8),
122
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_U8,      8, false, 1, writei),
123
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S16P,       16, true, 1, writen_16),
124
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S16_LE,     16, false, 1, writei),
125
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S24P,       24, true, 1, writen_24),
126
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S24_LE,     24, false, 1, writei),
127
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S24_32P,    32, true, 1, writen_32),
128
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S32P,       32, true, 1, writen_32),
129
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S24_32_LE,  32, false, 1, writei),
130
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_S32_LE,     32, false, 1, writei),
131
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_F32P,       32, true, 3, writen_32),
132
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_F32_LE,     32, false, 3, writei),
133
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_F64P,       64, true, 3, writen_64),
134
+   MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_F64_LE,     32, false, 3, writei),
135
+};
136
+
137
+#define CHECK_RES(expr) if ((res = (expr)) < 0) return res
138
+
139
+static int write_headers(struct wav_file *wf)
140
+{
141
+   int res;
142
+   uint32_t channels, rate, bps, bits;
143
+   const struct format_info *fi = wf->fi;
144
+
145
+   lseek(wf->fd, 0, SEEK_SET);
146
+
147
+   rate = wf->info.info.raw.rate;
148
+   channels = wf->info.info.raw.channels;
149
+   bits = fi->bits;
150
+   bps = channels * bits / 8;
151
+
152
+   CHECK_RES(write_n(wf->fd, "RIFF", 4));
153
+   CHECK_RES(write_le32(wf->fd, wf->length == 0 ? (uint32_t)-1 : wf->length + 12 + 8 + 16));
154
+   CHECK_RES(write_n(wf->fd, "WAVE", 4));
155
+   CHECK_RES(write_n(wf->fd, "fmt ", 4));
156
+   CHECK_RES(write_le32(wf->fd, 16));
157
+   CHECK_RES(write_le16(wf->fd, fi->fmt));         /* format */
158
+   CHECK_RES(write_le16(wf->fd, channels));        /* channels */
159
+   CHECK_RES(write_le32(wf->fd, rate));            /* rate */
160
+   CHECK_RES(write_le32(wf->fd, bps * rate));      /* bytes per sec */
161
+   CHECK_RES(write_le16(wf->fd, bps));         /* bytes per samples */
162
+   CHECK_RES(write_le16(wf->fd, bits));            /* bits per sample */
163
+   CHECK_RES(write_n(wf->fd, "data", 4));
164
+   CHECK_RES(write_le32(wf->fd, wf->length == 0 ? (uint32_t)-1 : wf->length));
165
+
166
+   return 0;
167
+}
168
+
169
+static const struct format_info *find_info(struct wav_file_info *info)
170
+{
171
+   uint32_t i;
172
+
173
+   if (info->info.media_type != SPA_MEDIA_TYPE_audio ||
174
+       info->info.media_subtype != SPA_MEDIA_SUBTYPE_raw)
175
+       return NULL;
176
+
177
+   for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) {
178
+       if (format_infoi.format == info->info.info.raw.format)
179
+           return &format_infoi;
180
+   }
181
+   return NULL;
182
+}
183
+
184
+static int open_write(struct wav_file *wf, const char *filename, struct wav_file_info *info)
185
+{
186
+   int res;
187
+   const struct format_info *fi;
188
+
189
+   fi = find_info(info);
190
+   if (fi == NULL)
191
+       return -ENOTSUP;
192
+
193
+   if ((wf->fd = open(filename, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0660)) < 0) {
194
+       res = -errno;
195
+       goto exit;
196
+   }
197
+   wf->info = info->info;
198
+   wf->fi = fi;
199
+   if (fi->planar) {
200
+       wf->stride = fi->bits / 8;
201
pipewire-0.3.68.tar.gz/spa/plugins/audioconvert/wavfile.h Added
41
 
1
@@ -0,0 +1,39 @@
2
+/* PipeWire
3
+ *
4
+ * Copyright © 2022 Wim Taymans
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a
7
+ * copy of this software and associated documentation files (the "Software"),
8
+ * to deal in the Software without restriction, including without limitation
9
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ * and/or sell copies of the Software, and to permit persons to whom the
11
+ * Software is furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice (including the next
14
+ * paragraph) shall be included in all copies or substantial portions of the
15
+ * Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23
+ * DEALINGS IN THE SOFTWARE.
24
+ */
25
+
26
+#include <spa/utils/defs.h>
27
+#include <spa/param/audio/format.h>
28
+
29
+struct wav_file;
30
+
31
+struct wav_file_info {
32
+   struct spa_audio_info info;
33
+};
34
+
35
+struct wav_file *
36
+wav_file_open(const char *filename, const char *mode, struct wav_file_info *info);
37
+
38
+int wav_file_close(struct wav_file *wf);
39
+
40
+ssize_t wav_file_write(struct wav_file *wf, const void **data, size_t size);
41
pipewire-0.3.67.tar.gz/spa/plugins/audiomixer/audiomixer.c -> pipewire-0.3.68.tar.gz/spa/plugins/audiomixer/audiomixer.c Changed
23
 
1
@@ -770,9 +770,9 @@
2
        size = SPA_MIN(bd->maxsize - offs, bd->chunk->size);
3
        maxsize = SPA_MIN(size, maxsize);
4
 
5
-       spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d", this,
6
+       spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d/%d", this,
7
                i, inio, outio, inio->status, inio->buffer_id,
8
-               offs, size);
9
+               offs, size, this->stride);
10
 
11
        if (!SPA_FLAG_IS_SET(bd->chunk->flags, SPA_CHUNK_FLAG_EMPTY)) {
12
            datasn_buffers = SPA_PTROFF(bd->data, offs, void);
13
@@ -783,7 +783,8 @@
14
 
15
    outb = dequeue_buffer(this, outport);
16
         if (SPA_UNLIKELY(outb == NULL)) {
17
-                spa_log_trace(this->log, "%p: out of buffers", this);
18
+                spa_log_trace(this->log, "%p: out of buffers (%d)", this,
19
+               outport->n_buffers);
20
                 return -EPIPE;
21
         }
22
 
23
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
13
 
1
@@ -706,9 +706,9 @@
2
        size = SPA_MIN(bd->maxsize - offs, bd->chunk->size);
3
        maxsize = SPA_MIN(maxsize, size);
4
 
5
-       spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d", this,
6
+       spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d/%d", this,
7
                i, inio, outio, inio->status, inio->buffer_id,
8
-               offs, size);
9
+               offs, size, (int)sizeof(float));
10
 
11
        if (!SPA_FLAG_IS_SET(bd->chunk->flags, SPA_CHUNK_FLAG_EMPTY)) {
12
            datasn_buffers = SPA_PTROFF(bd->data, offs, void);
13
pipewire-0.3.67.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c -> pipewire-0.3.68.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c Changed
25
 
1
@@ -345,6 +345,14 @@
2
    return 0;
3
 }
4
 
5
+static void update_target(struct impl *this)
6
+{
7
+   if (this->position) {
8
+       this->position->clock.duration = this->position->clock.target_duration;
9
+       this->position->clock.rate = this->position->clock.target_rate;
10
+   }
11
+}
12
+
13
 static int make_buffer(struct impl *this)
14
 {
15
    struct buffer *b;
16
@@ -424,6 +432,8 @@
17
    struct impl *this = source->data;
18
    int res;
19
 
20
+   update_target(this);
21
+
22
    res = make_buffer(this);
23
 
24
    if (res == SPA_STATUS_HAVE_DATA)
25
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
135
 
1
@@ -832,6 +832,17 @@
2
            state->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
3
 }
4
 
5
+static void update_position(struct state *state)
6
+{
7
+   if (state->position) {
8
+       state->duration = state->position->clock.duration;
9
+       state->rate_denom = state->position->clock.rate.denom;
10
+   } else {
11
+       state->duration = 1024;
12
+       state->rate_denom = state->rate;
13
+   }
14
+}
15
+
16
 static int flush_write(struct state *state, uint64_t current_time)
17
 {
18
    int32_t avail, wanted;
19
@@ -884,6 +895,8 @@
20
    uint32_t index, to_write;
21
    struct port *port = &state->ports0;
22
 
23
+   update_position(state);
24
+
25
    filled = spa_ringbuffer_get_write_index(&state->ring, &index);
26
    if (filled < 0) {
27
        spa_log_warn(state->log, "underrun %d", filled);
28
@@ -933,14 +946,16 @@
29
    }
30
    spa_ringbuffer_write_update(&state->ring, index);
31
 
32
-   if (state->following) {
33
+   if (state->following)
34
        flush_write(state, state->position->clock.nsec);
35
-   }
36
+
37
    return 0;
38
 }
39
 
40
 static int handle_play(struct state *state, uint64_t current_time)
41
 {
42
+   update_position(state);
43
+
44
    flush_write(state, current_time);
45
    spa_node_call_ready(&state->callbacks, SPA_STATUS_NEED_DATA);
46
    return 0;
47
@@ -955,8 +970,7 @@
48
    struct spa_data *d;
49
    uint32_t n_bytes;
50
 
51
-   if (state->position)
52
-       state->duration = state->position->clock.duration;
53
+   update_position(state);
54
 
55
    avail = spa_ringbuffer_get_read_index(&state->ring, &index);
56
    wanted = state->duration * state->stride;
57
@@ -1028,7 +1042,7 @@
58
 {
59
    struct state *state = source->data;
60
    uint64_t expirations, current_time, duration;
61
-   uint32_t rate;
62
+   struct spa_fraction rate;
63
    int res;
64
 
65
    spa_log_trace(state->log, "timeout");
66
@@ -1042,24 +1056,24 @@
67
 
68
    current_time = state->next_time;
69
    if (SPA_LIKELY(state->position)) {
70
-       duration = state->position->clock.duration;
71
-       rate = state->position->clock.rate.denom;
72
+       duration = state->position->clock.target_duration;
73
+       rate = state->position->clock.target_rate;
74
    } else {
75
        duration = 1024;
76
-       rate = 48000;
77
+       rate = SPA_FRACTION(1, 48000);
78
    }
79
-   state->duration = duration;
80
+
81
+   state->next_time = current_time + duration * SPA_NSEC_PER_SEC / rate.denom;
82
 
83
    if (state->ports0.direction == SPA_DIRECTION_INPUT)
84
        handle_play(state, current_time);
85
    else
86
        handle_capture(state, current_time);
87
 
88
-   state->next_time = current_time + duration * SPA_NSEC_PER_SEC / rate;
89
-
90
    if (SPA_LIKELY(state->clock)) {
91
        state->clock->nsec = current_time;
92
-       state->clock->position += duration;
93
+       state->clock->rate = rate;
94
+       state->clock->position += state->clock->duration;
95
        state->clock->duration = duration;
96
        state->clock->delay = 0;
97
        state->clock->rate_diff = 1.0;
98
@@ -1134,13 +1148,7 @@
99
    if (state->started)
100
        return 0;
101
 
102
-   if (state->position) {
103
-       state->duration = state->position->clock.duration;
104
-       state->rate_denom = state->position->clock.rate.denom;
105
-   } else {
106
-       state->duration = 1024;
107
-       state->rate_denom = state->rate;
108
-   }
109
+   update_position(state);
110
 
111
    spa_dll_init(&state->dll);
112
    state->max_error = (256.0 * state->rate) / state->rate_denom;
113
@@ -1184,10 +1192,11 @@
114
    struct state *state = user_data;
115
 
116
    spa_loop_remove_source(state->data_loop, &state->timer_source);
117
+   set_timeout(state, 0);
118
 
119
-   if (state->ports0.direction == SPA_DIRECTION_OUTPUT) {
120
+   if (state->ports0.direction == SPA_DIRECTION_OUTPUT)
121
        spa_loop_remove_source(state->data_loop, &state->sock_source);
122
-   }
123
+
124
    return 0;
125
 }
126
 
127
@@ -1201,7 +1210,6 @@
128
    spa_loop_invoke(state->data_loop, do_remove_source, 0, NULL, 0, true, state);
129
 
130
    state->started = false;
131
-   set_timeout(state, 0);
132
 
133
    return 0;
134
 }
135
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
89
 
1
@@ -835,12 +835,14 @@
2
 
3
 static void hsphfpd_audio_acquire_reply(DBusPendingCall *pending, void *user_data)
4
 {
5
-   struct impl *backend = user_data;
6
+   struct spa_bt_transport *transport = user_data;
7
+   struct impl *backend = SPA_CONTAINER_OF(transport->backend, struct impl, this);
8
    DBusMessage *r;
9
    const char *transport_path;
10
    const char *service_id;
11
    const char *agent_path;
12
    DBusError error;
13
+   int ret = 0;
14
 
15
    dbus_error_init(&error);
16
 
17
@@ -853,16 +855,19 @@
18
    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
19
        spa_log_error(backend->log, "RegisterApplication() failed: %s",
20
                dbus_message_get_error_name(r));
21
+       ret = -EIO;
22
        goto finish;
23
    }
24
 
25
    if (!spa_streq(dbus_message_get_sender(r), backend->hsphfpd_service_id)) {
26
        spa_log_error(backend->log, "Reply for " HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio() from invalid sender");
27
+       ret = -EIO;
28
        goto finish;
29
    }
30
 
31
    if (!check_signature(r, "oso")) {
32
        spa_log_error(backend->log, "Invalid reply signature for " HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio()");
33
+       ret = -EIO;
34
        goto finish;
35
    }
36
 
37
@@ -872,11 +877,13 @@
38
                              DBUS_TYPE_OBJECT_PATH, &agent_path,
39
                              DBUS_TYPE_INVALID) == FALSE) {
40
        spa_log_error(backend->log, "Failed to parse " HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio() reply: %s", error.message);
41
+       ret = -EIO;
42
        goto finish;
43
    }
44
 
45
    if (!spa_streq(service_id, dbus_bus_get_unique_name(backend->conn))) {
46
        spa_log_warn(backend->log, HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio() failed: Other audio application took audio socket");
47
+       ret = -EIO;
48
        goto finish;
49
    }
50
 
51
@@ -885,6 +892,11 @@
52
 finish:
53
    dbus_message_unref(r);
54
    dbus_pending_call_unref(pending);
55
+
56
+   if (ret < 0)
57
+       spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ERROR);
58
+   else
59
+       spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ACTIVE);
60
 }
61
 
62
 static int hsphfpd_audio_acquire(void *data, bool optional)
63
@@ -919,15 +931,10 @@
64
    dbus_error_init(&err);
65
 
66
    dbus_connection_send_with_reply(backend->conn, m, &call, -1);
67
-   dbus_pending_call_set_notify(call, hsphfpd_audio_acquire_reply, backend, NULL);
68
+   dbus_pending_call_set_notify(call, hsphfpd_audio_acquire_reply, transport, NULL);
69
    dbus_message_unref(m);
70
 
71
-   /* The ConnectAudio method triggers Introspect and NewConnection calls,
72
-      which will set the fd to use for the SCO data.
73
-      We need to run the DBus loop to be able to reply to those method calls */
74
    backend->acquire_in_progress = true;
75
-   while (backend->acquire_in_progress && dbus_connection_read_write_dispatch(backend->conn, -1))
76
-       ; // empty loop body
77
 
78
    return 0;
79
 }
80
@@ -941,6 +948,8 @@
81
    spa_log_debug(backend->log, "transport %p: Release %s",
82
            transport, transport->path);
83
 
84
+   spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_IDLE);
85
+
86
    if (transport->sco_io) {
87
        spa_bt_sco_io_destroy(transport->sco_io);
88
        transport->sco_io = NULL;
89
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
201
 
1
@@ -40,6 +40,7 @@
2
 #undef SPA_LOG_TOPIC_DEFAULT
3
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
4
 
5
+#define PROP_KEY_ROLES "bluez5.roles"
6
 #define PROP_KEY_HEADSET_ROLES "bluez5.headset-roles"
7
 
8
 #define HFP_CODEC_SWITCH_INITIAL_TIMEOUT_MSEC 5000
9
@@ -111,6 +112,8 @@
10
 struct transport_data {
11
    struct rfcomm *rfcomm;
12
    struct spa_source sco;
13
+   int err;
14
+   bool requesting;
15
 };
16
 
17
 enum hfp_hf_state {
18
@@ -1361,7 +1364,7 @@
19
    bdaddr_t src;
20
    int sock = -1;
21
 
22
-   sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
23
+   sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK, BTPROTO_SCO);
24
    if (sock < 0) {
25
        spa_log_error(backend->log, "socket(SEQPACKET, SCO) %s", strerror(errno));
26
        goto fail;
27
@@ -1414,6 +1417,8 @@
28
    spa_log_debug(backend->log, "transport %p: enter sco_do_connect, codec=%u",
29
            t, t->codec);
30
 
31
+   td->err = -EIO;
32
+
33
    if (d->adapter == NULL)
34
        return -EIO;
35
 
36
@@ -1456,6 +1461,8 @@
37
        goto fail_close;
38
    }
39
 
40
+   td->err = -EINPROGRESS;
41
+
42
    return sock;
43
 
44
 fail_close:
45
@@ -1465,37 +1472,76 @@
46
 
47
 static int rfcomm_ag_sync_volume(struct rfcomm *rfcomm, bool later);
48
 
49
-static void wait_for_socket(int fd)
50
-{
51
-   struct pollfd fds1;
52
-   const int timeout_ms = 500;
53
-
54
-   fds0.fd = fd;
55
-   fds0.events = POLLIN | POLLERR | POLLHUP;
56
-   poll(fds, 1, timeout_ms);
57
-}
58
-
59
-static int sco_acquire_cb(void *data, bool optional)
60
+static void sco_ready(struct spa_bt_transport *t)
61
 {
62
-   struct spa_bt_transport *t = data;
63
-   struct transport_data *td = t->user_data;
64
    struct impl *backend = SPA_CONTAINER_OF(t->backend, struct impl, this);
65
-   int sock;
66
+   struct transport_data *td = t->user_data;
67
+   struct sco_options sco_opt;
68
    socklen_t len;
69
+   int err;
70
 
71
-   spa_log_debug(backend->log, "transport %p: enter sco_acquire_cb", t);
72
+   spa_log_debug(backend->log, "transport %p: ready", t);
73
 
74
-   if (optional || t->fd > 0)
75
-       sock = t->fd;
76
-   else
77
-       sock = sco_do_connect(t);
78
+   /* Read socket error status */
79
+   if (t->fd >= 0) {
80
+       if (td->err == -EINPROGRESS) {
81
+           len = sizeof(err);
82
+           memset(&err, 0, len);
83
+           if (getsockopt(t->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
84
+               td->err = -errno;
85
+           else
86
+               td->err = -err;
87
+       }
88
+   } else {
89
+       td->err = -EIO;
90
+   }
91
 
92
-   if (sock < 0)
93
-       goto fail;
94
+   if (!td->requesting)
95
+       return;
96
 
97
-#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
98
-   rfcomm_hfp_ag_set_cind(td->rfcomm, true);
99
-#endif
100
+   td->requesting = false;
101
+
102
+   if (td->err)
103
+       goto done;
104
+
105
+   /* XXX: The MTU as currently reported by kernel (6.2) here is not a valid packet size,
106
+    * XXX: for USB adapters, see sco-io.
107
+    */
108
+   len = sizeof(sco_opt);
109
+   memset(&sco_opt, 0, len);
110
+   if (getsockopt(t->fd, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
111
+       spa_log_warn(backend->log, "getsockopt(SCO_OPTIONS) failed, using defaults");
112
+       t->read_mtu = 48;
113
+       t->write_mtu = 48;
114
+   } else {
115
+       spa_log_debug(backend->log, "autodetected mtu = %u", sco_opt.mtu);
116
+       t->read_mtu = sco_opt.mtu;
117
+       t->write_mtu = sco_opt.mtu;
118
+   }
119
+
120
+   /* Clear nonblocking flag we set for connect() */
121
+   err = fcntl(t->fd, F_GETFL, O_NONBLOCK);
122
+   if (err < 0) {
123
+       td->err = -errno;
124
+       goto done;
125
+   }
126
+   err &= ~O_NONBLOCK;
127
+   err = fcntl(t->fd, F_SETFL, O_NONBLOCK, err);
128
+   if (err < 0) {
129
+       td->err = -errno;
130
+       goto done;
131
+   }
132
+
133
+done:
134
+   if (td->err) {
135
+       spa_log_debug(backend->log, "transport %p: acquire failed: %s (%d)",
136
+               t, strerror(-td->err), td->err);
137
+       spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_ERROR);
138
+       return;
139
+   }
140
+
141
+   spa_log_debug(backend->log, "transport %p: acquire complete, read_mtu=%u, write_mtu=%u",
142
+           t, t->read_mtu, t->write_mtu);
143
 
144
    /*
145
     * Send RFCOMM volume after connection is ready, and also after
146
@@ -1512,55 +1558,65 @@
147
     * volume buttons, which is updated by an +VGS event only after
148
     * sufficient time is elapsed from the connection.
149
     */
150
-   wait_for_socket(sock);
151
    rfcomm_ag_sync_volume(td->rfcomm, false);
152
    rfcomm_ag_sync_volume(td->rfcomm, true);
153
 
154
-   t->fd = sock;
155
+   spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_ACTIVE);
156
+}
157
+
158
+static void sco_start_source(struct spa_bt_transport *t);
159
+
160
+static int sco_acquire_cb(void *data, bool optional)
161
+{
162
+   struct spa_bt_transport *t = data;
163
+   struct transport_data *td = t->user_data;
164
+   struct impl *backend = SPA_CONTAINER_OF(t->backend, struct impl, this);
165
+   int sock;
166
 
167
-   /* Fallback value */
168
-   t->read_mtu = 48;
169
-   t->write_mtu = 48;
170
+   spa_log_debug(backend->log, "transport %p: enter sco_acquire_cb", t);
171
 
172
-   if (true) {
173
-       struct sco_options sco_opt;
174
+   if (optional || t->fd > 0)
175
+       sock = t->fd;
176
+   else
177
+       sock = sco_do_connect(t);
178
 
179
-       len = sizeof(sco_opt);
180
-       memset(&sco_opt, 0, len);
181
+   if (sock < 0)
182
+       goto fail;
183
 
184
-       if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0)
185
-           spa_log_warn(backend->log, "getsockopt(SCO_OPTIONS) failed, loading defaults");
186
-       else {
187
-           spa_log_debug(backend->log, "autodetected mtu = %u", sco_opt.mtu);
188
-           t->read_mtu = sco_opt.mtu;
189
-           t->write_mtu = sco_opt.mtu;
190
-       }
191
-   }
192
-   spa_log_debug(backend->log, "transport %p: read_mtu=%u, write_mtu=%u", t, t->read_mtu, t->write_mtu);
193
+#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
194
+   rfcomm_hfp_ag_set_cind(td->rfcomm, true);
195
+#endif
196
+
197
+   t->fd = sock;
198
+
199
+   td->requesting = true;
200
+
201
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
33
 
1
@@ -231,7 +231,9 @@
2
        ts.tv_nsec = 1;
3
        spa_loop_utils_update_timer(backend->loop_utils, backend->timer,
4
                &ts, NULL, false);
5
-       return -EIO;
6
+
7
+       ret = -EIO;
8
+       goto finish;
9
    }
10
 
11
    td->broken = false;
12
@@ -243,6 +245,11 @@
13
    ret = 0;
14
 
15
 finish:
16
+   if (ret < 0)
17
+       spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ERROR);
18
+   else
19
+       spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ACTIVE);
20
+
21
    return ret;
22
 }
23
 
24
@@ -254,6 +261,8 @@
25
    spa_log_debug(backend->log, "transport %p: Release %s",
26
            transport, transport->path);
27
 
28
+   spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_IDLE);
29
+
30
    if (transport->sco_io) {
31
        spa_bt_sco_io_destroy(transport->sco_io);
32
        transport->sco_io = NULL;
33
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
201
 
1
@@ -35,6 +35,7 @@
2
 #include "config.h"
3
 #include "codec-loader.h"
4
 #include "player.h"
5
+#include "iso-io.h"
6
 #include "defs.h"
7
 
8
 static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5");
9
@@ -65,6 +66,12 @@
10
 
11
 #define CODEC_SWITCH_RETRIES   1
12
 
13
+/* How many times to retry acquire on errors, and how long delay to require before we can
14
+ * try again.
15
+ */
16
+#define TRANSPORT_ERROR_MAX_RETRY  3
17
+#define TRANSPORT_ERROR_TIMEOUT        (2*BLUEZ_ACTION_RATE_MSEC*SPA_NSEC_PER_MSEC)
18
+
19
 
20
 struct spa_bt_monitor {
21
    struct spa_handle handle;
22
@@ -72,7 +79,9 @@
23
 
24
    struct spa_log *log;
25
    struct spa_loop *main_loop;
26
+   struct spa_loop *data_loop;
27
    struct spa_system *main_system;
28
+   struct spa_system *data_system;
29
    struct spa_plugin_loader *plugin_loader;
30
    struct spa_dbus *dbus;
31
    struct spa_dbus_connection *dbus_connection;
32
@@ -103,6 +112,8 @@
33
 
34
    struct spa_dict enabled_codecs;
35
 
36
+   enum spa_bt_profile enabled_profiles;
37
+
38
    unsigned int connection_info_supported:1;
39
    unsigned int dummy_avrcp_player:1;
40
 
41
@@ -178,11 +189,12 @@
42
  * SCO socket connect may fail with ECONNABORTED if it is done too soon after
43
  * previous close. To avoid this in cases where nodes are toggled between
44
  * stopped/started rapidly, postpone release until the transport has remained
45
- * unused for a time. Since this appears common to multiple SCO backends, we do
46
- * it for all SCO backends here.
47
+ * unused for a time.
48
+ *
49
+ * Avoiding unnecessary release+reacquire also makes sense for other transports,
50
+ * so we use the release timeout for all of them.
51
  */
52
-#define SCO_TRANSPORT_RELEASE_TIMEOUT_MSEC 1000
53
-#define SPA_BT_TRANSPORT_IS_SCO(transport) (transport->backend != NULL)
54
+#define TRANSPORT_RELEASE_TIMEOUT_MSEC 1000
55
 
56
 #define TRANSPORT_VOLUME_TIMEOUT_MSEC 200
57
 
58
@@ -508,6 +520,19 @@
59
    }
60
 }
61
 
62
+static enum spa_bt_profile get_codec_profile(const struct media_codec *codec,
63
+       enum spa_bt_media_direction direction)
64
+{
65
+   switch (direction) {
66
+   case SPA_BT_MEDIA_SOURCE:
67
+       return codec->bap ? SPA_BT_PROFILE_BAP_SOURCE : SPA_BT_PROFILE_A2DP_SOURCE;
68
+   case SPA_BT_MEDIA_SINK:
69
+       return codec->bap ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_A2DP_SINK;
70
+   default:
71
+       spa_assert_not_reached();
72
+   }
73
+}
74
+
75
 static bool endpoint_should_be_registered(struct spa_bt_monitor *monitor,
76
                      const struct media_codec *codec,
77
                      enum spa_bt_media_direction direction)
78
@@ -517,7 +542,8 @@
79
     */
80
    return is_media_codec_enabled(monitor, codec) &&
81
        codec_has_direction(codec, direction) &&
82
-       codec->fill_caps;
83
+       codec->fill_caps &&
84
+       (get_codec_profile(codec, direction) & monitor->enabled_profiles);
85
 }
86
 
87
 static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata)
88
@@ -1209,11 +1235,17 @@
89
    return NULL;
90
 }
91
 
92
-void spa_bt_device_update_last_bluez_action_time(struct spa_bt_device *device)
93
+static uint64_t get_time_now(struct spa_bt_monitor *monitor)
94
 {
95
    struct timespec ts;
96
-   spa_system_clock_gettime(device->monitor->main_system, CLOCK_MONOTONIC, &ts);
97
-   device->last_bluez_action_time = SPA_TIMESPEC_TO_NSEC(&ts);
98
+
99
+   spa_system_clock_gettime(monitor->main_system, CLOCK_MONOTONIC, &ts);
100
+   return SPA_TIMESPEC_TO_NSEC(&ts);
101
+}
102
+
103
+void spa_bt_device_update_last_bluez_action_time(struct spa_bt_device *device)
104
+{
105
+   device->last_bluez_action_time = get_time_now(device->monitor);
106
 }
107
 
108
 static struct spa_bt_device *device_create(struct spa_bt_monitor *monitor, const char *path)
109
@@ -2199,6 +2231,8 @@
110
    t->sco_io = NULL;
111
    t->delay_us = SPA_BT_UNKNOWN_DELAY;
112
    t->latency_us = SPA_BT_UNKNOWN_DELAY;
113
+   t->bap_cig = 0xff;
114
+   t->bap_cis = 0xff;
115
    t->user_data = SPA_PTROFF(t, sizeof(struct spa_bt_transport), void);
116
    spa_hook_list_init(&t->listener_list);
117
    spa_list_init(&t->bap_transport_linked);
118
@@ -2236,6 +2270,18 @@
119
        spa_bt_transport_emit_state_changed(transport, old, state);
120
        if (state >= SPA_BT_TRANSPORT_STATE_PENDING && old < SPA_BT_TRANSPORT_STATE_PENDING)
121
            transport_sync_volume(transport);
122
+
123
+       if (state == SPA_BT_TRANSPORT_STATE_ERROR) {
124
+           uint64_t now = get_time_now(monitor);
125
+
126
+           if (now > transport->last_error_time + TRANSPORT_ERROR_TIMEOUT) {
127
+               spa_log_error(monitor->log, "Failure in Bluetooth audio transport %s",
128
+                       transport->path);
129
+           }
130
+
131
+           transport->last_error_time = now;
132
+           ++transport->error_count;
133
+       }
134
    }
135
 }
136
 
137
@@ -2261,8 +2307,16 @@
138
        transport->sco_io = NULL;
139
    }
140
 
141
+   if (transport->iso_io)
142
+       spa_bt_iso_io_destroy(transport->iso_io);
143
+
144
    spa_bt_transport_destroy(transport);
145
 
146
+   if (transport->acquire_call) {
147
+       dbus_pending_call_cancel(transport->acquire_call);
148
+       transport->acquire_call = NULL;
149
+   }
150
+
151
    if (transport->fd >= 0) {
152
        spa_bt_player_set_state(transport->device->adapter->dummy_player, SPA_BT_PLAYER_STOPPED);
153
 
154
@@ -2313,10 +2367,17 @@
155
    if (transport->acquire_refcount > 0) {
156
        spa_log_debug(monitor->log, "transport %p: incref %s", transport, transport->path);
157
        transport->acquire_refcount += 1;
158
+       spa_bt_transport_emit_state_changed(transport, transport->state, transport->state);
159
        return 0;
160
    }
161
    spa_assert(transport->acquire_refcount == 0);
162
 
163
+   /* If we are getting into error state too often, stop trying */
164
+   if (get_time_now(monitor) > transport->last_error_time + TRANSPORT_ERROR_TIMEOUT)
165
+       transport->error_count = 0;
166
+   if (transport->error_count >= TRANSPORT_ERROR_MAX_RETRY)
167
+       return -EIO;
168
+
169
    if (!transport->acquired)
170
        res = spa_bt_transport_impl(transport, acquire, 0, optional);
171
    else
172
@@ -2333,11 +2394,11 @@
173
 int spa_bt_transport_release(struct spa_bt_transport *transport)
174
 {
175
    struct spa_bt_monitor *monitor = transport->monitor;
176
-   int res;
177
 
178
    if (transport->acquire_refcount > 1) {
179
        spa_log_debug(monitor->log, "transport %p: decref %s", transport, transport->path);
180
        transport->acquire_refcount -= 1;
181
+       spa_bt_transport_emit_state_changed(transport, transport->state, transport->state);
182
        return 0;
183
    }
184
    else if (transport->acquire_refcount == 0) {
185
@@ -2347,23 +2408,8 @@
186
    spa_assert(transport->acquire_refcount == 1);
187
    spa_assert(transport->acquired);
188
 
189
-   if (SPA_BT_TRANSPORT_IS_SCO(transport)) {
190
-       /* Postpone SCO transport releases, since we might need it again soon */
191
-       res = spa_bt_transport_start_release_timer(transport);
192
-   } else if (transport->keepalive) {
193
-       res = 0;
194
-       transport->acquire_refcount = 0;
195
-       spa_log_debug(monitor->log, "transport %p: keepalive %s on release",
196
-               transport, transport->path);
197
-   } else {
198
-       res = spa_bt_transport_impl(transport, release, 0);
199
-       if (res >= 0) {
200
-           transport->acquire_refcount = 0;
201
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
20
 
1
@@ -1038,13 +1038,16 @@
2
 
3
 static void set_initial_profile(struct impl *this);
4
 
5
-static void device_connected(void *userdata, bool connected) {
6
+static void device_connected(void *userdata, bool connected)
7
+{
8
    struct impl *this = userdata;
9
 
10
    spa_log_debug(this->log, "connected: %d", connected);
11
 
12
-   if (connected ^ (this->profile != DEVICE_PROFILE_OFF))
13
+   if (connected ^ (this->profile != DEVICE_PROFILE_OFF)) {
14
+       emit_remove_nodes(this);
15
        set_initial_profile(this);
16
+   }
17
 }
18
 
19
 static const struct spa_bt_device_events bt_dev_events = {
20
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
157
 
1
@@ -38,10 +38,13 @@
2
 #include <spa/utils/defs.h>
3
 #include <spa/support/log.h>
4
 
5
+#include "rate-control.h"
6
+
7
 #define BUFFERING_LONG_MSEC        (2*60000)
8
 #define BUFFERING_SHORT_MSEC       1000
9
 #define BUFFERING_RATE_DIFF_MAX        0.005
10
 
11
+
12
 /**
13
  * Safety margin.
14
  *
15
@@ -51,131 +54,6 @@
16
 #define BUFFERING_TARGET(spike,packet_size,max_buf)            \
17
    SPA_CLAMP((spike)*3/2, (packet_size), (max_buf) - 2*(packet_size))
18
 
19
-/**
20
- * Rate controller.
21
- *
22
- * It's here in a form, where it operates on the running average
23
- * so it's compatible with the level spike determination, and
24
- * clamping the rate to a range is easy. The impulse response
25
- * is similar to spa_dll, and step response does not have sign changes.
26
- *
27
- * The controller iterates as
28
- *
29
- *    avg(j+1) = (1 - beta) avg(j) + beta level(j)
30
- *    corr(j+1) = corr(j) + a avg(j+1) - avg(j) / duration
31
- *           + b avg(j) - target / duration
32
- *
33
- * with beta = duration/avg_period < 0.5 is the moving average parameter,
34
- * and a = beta/3 + ..., b = beta^2/27 + ....
35
- *
36
- * This choice results to c(j) being low-pass filtered, and buffer level(j)
37
- * converging towards target with stable damped evolution with eigenvalues
38
- * real and close to each other around (1 - beta)^(1/3).
39
- *
40
- * Derivation:
41
- *
42
- * The deviation from the buffer level target evolves as
43
- *
44
- *     delta(j) = level(j) - target
45
- *     delta(j+1) = delta(j) + r(j) - c(j+1)
46
- *
47
- * where r is samples received in one duration, and c corrected rate
48
- * (samples per duration).
49
- *
50
- * The rate correction is in general determined by linear filter f
51
- *
52
- *     c(j+1) = c(j) + \sum_{k=0}^\infty delta(j - k) f(k)
53
- *
54
- * If \sum_k f(k) is not zero, the only fixed point is c=r, delta=0,
55
- * so this structure (if the filter is stable) rate matches and
56
- * drives buffer level to target.
57
- *
58
- * The z-transform then is
59
- *
60
- *     delta(z) = G(z) r(z)
61
- *     c(z) = F(z) delta(z)
62
- *     G(z) = (z - 1) / (z - 1)^2 + z f(z)
63
- *     F(z) = f(z) / (z - 1)
64
- *
65
- * We now want: poles of G(z) must be in |z|<1 for stability, F(z)
66
- * should damp high frequencies, and f(z) is causal.
67
- *
68
- * To satisfy the conditions, take
69
- *
70
- *     (z - 1)^2 + z f(z) = p(z) / q(z)
71
- *
72
- * where p(z) is polynomial with leading term z^n with wanted root
73
- * structure, and q(z) is any polynomial with leading term z^{n-2}.
74
- * This guarantees f(z) is causal, and G(z) = (z-1) q(z) / p(z).
75
- * We can choose p(z) and q(z) to improve low-pass properties of F(z).
76
- *
77
- * Simplest choice is p(z)=(z-x)^2 and q(z)=1, but that gives flat
78
- * high frequency response in F(z). Better choice is p(z) = (z-u)*(z-v)*(z-w)
79
- * and q(z) = z - r. To make F(z) better lowpass, one can cancel
80
- * a resulting 1/z pole in F(z) by setting r=u*v*w. Then,
81
- *
82
- *     G(z) = (z - u*v*w)*(z - 1) / (z - u)*(z - v)*(z - w)
83
- *     F(z) = (a z + b - a) / (z - 1) *     H(z)
84
- *     H(z) = beta / (z - 1 + beta)
85
- *     beta = 1 - u*v*w
86
- *     a = (1-u) + (1-v) + (1-w) - beta / beta
87
- *     b = (1-u)*(1-v)*(1-w) / beta
88
- *
89
- * which corresponds to iteration for c(j):
90
- *
91
- *    avg(j+1) = (1 - beta) avg(j) + beta delta(j)
92
- *    c(j+1) = c(j) + a avg(j+1) - avg(j) + b avg(j)
93
- *
94
- * So the controller operates on the running average,
95
- * which gives the low-pass property for c(j).
96
- *
97
- * The simplest filter is obtained by putting the poles at
98
- * u=v=w=(1-beta)**(1/3). Since beta << 1, computing the root
99
- * can be avoided by expanding in series.
100
- *
101
- * Overshoot in impulse response could be reduced by moving one of the
102
- * poles closer to z=1, but this increases the step response time.
103
- */
104
-struct spa_bt_rate_control
105
-{
106
-   double avg;
107
-   double corr;
108
-};
109
-
110
-static void spa_bt_rate_control_init(struct spa_bt_rate_control *this, double level)
111
-{
112
-   this->avg = level;
113
-   this->corr = 1.0;
114
-}
115
-
116
-static double spa_bt_rate_control_update(struct spa_bt_rate_control *this, double level,
117
-       double target, double duration, double period)
118
-{
119
-   /*
120
-    * u = (1 - beta)^(1/3)
121
-    * x = a / beta
122
-    * y = b / beta
123
-    * a = (2 + u) * (1 - u)^2 / beta
124
-    * b = (1 - u)^3 / beta
125
-    * beta -> 0
126
-    */
127
-   const double beta = SPA_CLAMP(duration / period, 0, 0.5);
128
-   const double x = 1.0/3;
129
-   const double y = beta/27;
130
-   double avg;
131
-
132
-   avg = beta * level + (1 - beta) * this->avg;
133
-   this->corr += x * (avg - this->avg) / period
134
-       + y * (this->avg - target) / period;
135
-   this->avg = avg;
136
-
137
-   this->corr = SPA_CLAMP(this->corr,
138
-           1 - BUFFERING_RATE_DIFF_MAX,
139
-           1 + BUFFERING_RATE_DIFF_MAX);
140
-
141
-   return this->corr;
142
-}
143
-
144
 
145
 /** Windowed min/max */
146
 struct spa_bt_ptp
147
@@ -463,7 +341,8 @@
148
        }
149
 
150
        this->corr = spa_bt_rate_control_update(&this->ctl,
151
-               level, target, this->prev_consumed, avg_period);
152
+               level, target, this->prev_consumed, avg_period,
153
+               BUFFERING_RATE_DIFF_MAX);
154
 
155
        spa_bt_decode_buffer_get_read(this, &avail);
156
 
157
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/defs.h Changed
47
 
1
@@ -522,6 +522,8 @@
2
 #define spa_bt_device_add_listener(d,listener,events,data)           \
3
    spa_hook_list_append(&(d)->listener_list, listener, events, data)
4
 
5
+struct spa_bt_iso_io;
6
+
7
 struct spa_bt_sco_io;
8
 
9
 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);
10
@@ -539,9 +541,10 @@
11
 #define SPA_BT_VOLUME_A2DP_MAX 127
12
 
13
 enum spa_bt_transport_state {
14
-        SPA_BT_TRANSPORT_STATE_IDLE,
15
-        SPA_BT_TRANSPORT_STATE_PENDING,
16
-        SPA_BT_TRANSPORT_STATE_ACTIVE,
17
+        SPA_BT_TRANSPORT_STATE_ERROR = -1,
18
+        SPA_BT_TRANSPORT_STATE_IDLE = 0,
19
+        SPA_BT_TRANSPORT_STATE_PENDING = 1,
20
+        SPA_BT_TRANSPORT_STATE_ACTIVE = 2,
21
 };
22
 
23
 struct spa_bt_transport_events {
24
@@ -600,16 +603,22 @@
25
    int acquire_refcount;
26
    bool acquired;
27
    bool keepalive;
28
+   int error_count;
29
+   uint64_t last_error_time;
30
    int fd;
31
    uint16_t read_mtu;
32
    uint16_t write_mtu;
33
    unsigned int delay_us;
34
    unsigned int latency_us;
35
+   uint8_t bap_cig;
36
+   uint8_t bap_cis;
37
 
38
+   struct spa_bt_iso_io *iso_io;
39
    struct spa_bt_sco_io *sco_io;
40
 
41
    struct spa_source volume_timer;
42
    struct spa_source release_timer;
43
+   DBusPendingCall *acquire_call;
44
 
45
    struct spa_hook_list listener_list;
46
    struct spa_callbacks impl;
47
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/iso-io.c Added
201
 
1
@@ -0,0 +1,377 @@
2
+/* Spa ISO I/O */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Pauli Virtanen. */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <unistd.h>
7
+#include <stddef.h>
8
+#include <stdio.h>
9
+#include <errno.h>
10
+#include <limits.h>
11
+#include <sys/socket.h>
12
+
13
+#include <spa/support/loop.h>
14
+#include <spa/support/log.h>
15
+#include <spa/utils/list.h>
16
+#include <spa/utils/string.h>
17
+#include <spa/utils/result.h>
18
+#include <spa/node/io.h>
19
+
20
+#include <bluetooth/bluetooth.h>
21
+
22
+#include "config.h"
23
+#include "iso-io.h"
24
+
25
+static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.iso");
26
+#undef SPA_LOG_TOPIC_DEFAULT
27
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
28
+
29
+#define IDLE_TIME  (100 * SPA_NSEC_PER_MSEC)
30
+
31
+struct group {
32
+   struct spa_log *log;
33
+   struct spa_log_topic log_topic;
34
+   struct spa_loop *data_loop;
35
+   struct spa_system *data_system;
36
+   struct spa_source source;
37
+   struct spa_list streams;
38
+   int timerfd;
39
+   uint8_t cig;
40
+   uint64_t next;
41
+   uint64_t duration;
42
+   uint32_t paused;
43
+};
44
+
45
+struct stream {
46
+   struct spa_bt_iso_io this;
47
+   struct spa_list link;
48
+   struct group *group;
49
+   int fd;
50
+   bool sink;
51
+   bool idle;
52
+
53
+   spa_bt_iso_io_pull_t pull;
54
+};
55
+
56
+struct modify_info
57
+{
58
+   struct stream *stream;
59
+   struct spa_list *streams;
60
+};
61
+
62
+static int do_modify(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
63
+{
64
+   struct modify_info *info = user_data;
65
+
66
+   if (info->streams)
67
+       spa_list_append(info->streams, &info->stream->link);
68
+   else
69
+       spa_list_remove(&info->stream->link);
70
+
71
+   return 0;
72
+}
73
+
74
+static void stream_link(struct group *group, struct stream *stream)
75
+{
76
+   struct modify_info info = { .stream = stream, .streams = &group->streams };
77
+   int res;
78
+
79
+   res = spa_loop_invoke(group->data_loop, do_modify, 0, NULL, 0, true, &info);
80
+   spa_assert_se(res == 0);
81
+}
82
+
83
+static void stream_unlink(struct stream *stream)
84
+{
85
+   struct modify_info info = { .stream = stream, .streams = NULL };
86
+   int res;
87
+
88
+   res = spa_loop_invoke(stream->group->data_loop, do_modify, 0, NULL, 0, true, &info);
89
+   spa_assert_se(res == 0);
90
+}
91
+
92
+static int set_timeout(struct group *group, uint64_t time)
93
+{
94
+   struct itimerspec ts;
95
+   ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC;
96
+   ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC;
97
+   ts.it_interval.tv_sec = 0;
98
+   ts.it_interval.tv_nsec = 0;
99
+   return spa_system_timerfd_settime(group->data_system,
100
+           group->timerfd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
101
+}
102
+
103
+static int set_timers(struct group *group)
104
+{
105
+   struct timespec now;
106
+
107
+   spa_system_clock_gettime(group->data_system, CLOCK_MONOTONIC, &now);
108
+   group->next = SPA_ROUND_UP(SPA_TIMESPEC_TO_NSEC(&now) + group->duration,
109
+           group->duration);
110
+
111
+   return set_timeout(group, group->next);
112
+}
113
+
114
+static void group_on_timeout(struct spa_source *source)
115
+{
116
+   struct group *group = source->data;
117
+   struct stream *stream;
118
+   uint64_t exp;
119
+   int res;
120
+   bool active = false;
121
+
122
+   if ((res = spa_system_timerfd_read(group->data_system, group->timerfd, &exp)) < 0) {
123
+       if (res != -EAGAIN)
124
+           spa_log_warn(group->log, "%p: ISO group:%u error reading timerfd: %s",
125
+                   group, group->cig, spa_strerror(res));
126
+       return;
127
+   }
128
+
129
+   /*
130
+    * If an idle stream activates when another stream is already active,
131
+    * pause output of all streams for a while to avoid desynchronization.
132
+    */
133
+   spa_list_for_each(stream, &group->streams, link) {
134
+       if (!stream->sink)
135
+           continue;
136
+       if (!stream->idle) {
137
+           active = true;
138
+           break;
139
+       }
140
+   }
141
+
142
+   spa_list_for_each(stream, &group->streams, link) {
143
+       if (!stream->sink)
144
+           continue;
145
+
146
+       if (stream->idle && stream->this.size > 0 && active && !group->paused)
147
+           group->paused = 1u + IDLE_TIME / group->duration;
148
+
149
+       stream->idle = (stream->this.size == 0);
150
+   }
151
+
152
+   if (group->paused) {
153
+       --group->paused;
154
+       spa_log_debug(group->log, "%p: ISO group:%d paused:%u", group, group->cig, group->paused);
155
+   }
156
+
157
+   /* Produce output */
158
+   spa_list_for_each(stream, &group->streams, link) {
159
+       int res;
160
+
161
+       if (!stream->sink)
162
+           continue;
163
+       if (stream->idle)
164
+           continue;
165
+       if (group->paused) {
166
+           stream->this.size = 0;
167
+           continue;
168
+       }
169
+
170
+       res = send(stream->fd, stream->this.buf, stream->this.size, MSG_DONTWAIT | MSG_NOSIGNAL);
171
+       if (res < 0)
172
+           res = -errno;
173
+
174
+       spa_log_trace(group->log, "%p: ISO group:%u sent fd:%d size:%u ts:%u res:%d",
175
+               group, group->cig, stream->fd, (unsigned)stream->this.size,
176
+               (unsigned)stream->this.timestamp, res);
177
+
178
+       stream->this.size = 0;
179
+   }
180
+
181
+   /* Pull data for the next interval */
182
+   group->next += exp * group->duration;
183
+
184
+   spa_list_for_each(stream, &group->streams, link) {
185
+       if (!stream->sink)
186
+           continue;
187
+
188
+       if (stream->pull) {
189
+           stream->this.now = group->next;
190
+           stream->pull(&stream->this);
191
+       } else {
192
+           stream->this.size = 0;
193
+       }
194
+   }
195
+
196
+   set_timeout(group, group->next);
197
+}
198
+
199
+static struct group *group_create(int fd, struct spa_log *log, struct spa_loop *data_loop,
200
+       struct spa_system *data_system)
201
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/iso-io.h Added
41
 
1
@@ -0,0 +1,39 @@
2
+/* Spa Bluez5 ISO I/O */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Pauli Virtanen */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_BLUEZ5_ISO_IO_H
7
+#define SPA_BLUEZ5_ISO_IO_H
8
+
9
+#include <spa/utils/defs.h>
10
+#include <spa/support/loop.h>
11
+#include <spa/support/log.h>
12
+#include <spa/node/io.h>
13
+
14
+/**
15
+ * ISO I/O.
16
+ *
17
+ * Synchronizes related writes from different streams in the same group
18
+ * to occur at same real time instant (or not at all).
19
+ */
20
+struct spa_bt_iso_io
21
+{
22
+   uint64_t now;
23
+   uint64_t duration;
24
+
25
+   uint32_t timestamp;
26
+   uint8_t buf4096;
27
+   size_t size;
28
+
29
+   void *user_data;
30
+};
31
+
32
+typedef void (*spa_bt_iso_io_pull_t)(struct spa_bt_iso_io *io);
33
+
34
+struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, struct spa_log *log,
35
+       struct spa_loop *data_loop, struct spa_system *data_system);
36
+struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *io, int fd, bool sink);
37
+void spa_bt_iso_io_destroy(struct spa_bt_iso_io *io);
38
+void spa_bt_iso_io_set_cb(struct spa_bt_iso_io *io, spa_bt_iso_io_pull_t pull, void *user_data);
39
+
40
+#endif
41
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
201
 
1
@@ -31,11 +31,15 @@
2
 #include <spa/debug/mem.h>
3
 #include <spa/debug/log.h>
4
 
5
+#include <bluetooth/bluetooth.h>
6
+
7
 #include <sbc/sbc.h>
8
 
9
 #include "defs.h"
10
 #include "rtp.h"
11
 #include "media-codecs.h"
12
+#include "rate-control.h"
13
+#include "iso-io.h"
14
 
15
 static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.sink.media");
16
 #undef SPA_LOG_TOPIC_DEFAULT
17
@@ -52,6 +56,7 @@
18
 #define MIN_BUFFERS 2
19
 #define MAX_BUFFERS 32
20
 #define BUFFER_SIZE    (8192*8)
21
+#define RATE_CTL_DIFF_MAX 0.005
22
 
23
 struct buffer {
24
    uint32_t id;
25
@@ -70,6 +75,7 @@
26
    uint64_t info_all;
27
    struct spa_port_info info;
28
    struct spa_io_buffers *io;
29
+   struct spa_io_rate_match *rate_match;
30
    struct spa_latency_info latency;
31
 #define IDX_EnumFormat 0
32
 #define IDX_Meta   1
33
@@ -87,6 +93,8 @@
34
    struct spa_list ready;
35
 
36
    size_t ready_offset;
37
+
38
+   struct spa_bt_rate_control ratectl;
39
 };
40
 
41
 struct impl {
42
@@ -116,6 +124,8 @@
43
    struct port port;
44
 
45
    unsigned int started:1;
46
+   unsigned int start_ready:1;
47
+   unsigned int transport_started:1;
48
    unsigned int following:1;
49
    unsigned int is_output:1;
50
    unsigned int flush_pending:1;
51
@@ -135,6 +145,8 @@
52
    uint64_t next_time;
53
    uint64_t last_error;
54
    uint64_t process_time;
55
+   uint64_t process_duration;
56
+   uint64_t process_rate;
57
 
58
    uint64_t prev_flush_time;
59
    uint64_t next_flush_time;
60
@@ -147,6 +159,8 @@
61
 
62
    int need_flush;
63
    bool fragment;
64
+   bool resync;
65
+   bool have_iso_packet;
66
    uint32_t block_size;
67
    uint8_t bufferBUFFER_SIZE;
68
    uint32_t buffer_used;
69
@@ -270,52 +284,75 @@
70
    return set_timeout(this, this->following ? 0 : this->next_time);
71
 }
72
 
73
-static int do_reassign_follower(struct spa_loop *loop,
74
+static inline bool is_following(struct impl *this)
75
+{
76
+   return this->position && this->clock && this->position->clock.id != this->clock->id;
77
+}
78
+
79
+struct reassign_io_info {
80
+   struct impl *this;
81
+   struct spa_io_position *position;
82
+   struct spa_io_clock *clock;
83
+};
84
+
85
+static int do_reassign_io(struct spa_loop *loop,
86
            bool async,
87
            uint32_t seq,
88
            const void *data,
89
            size_t size,
90
            void *user_data)
91
 {
92
-   struct impl *this = user_data;
93
-   set_timers(this);
94
-   return 0;
95
-}
96
+   struct reassign_io_info *info = user_data;
97
+   struct impl *this = info->this;
98
+   bool following;
99
 
100
-static inline bool is_following(struct impl *this)
101
-{
102
-   return this->position && this->clock && this->position->clock.id != this->clock->id;
103
+   if (this->position != info->position || this->clock != info->clock)
104
+       this->resync = true;
105
+
106
+   this->position = info->position;
107
+   this->clock = info->clock;
108
+
109
+   following = is_following(this);
110
+
111
+   if (following != this->following) {
112
+       spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following);
113
+       this->following = following;
114
+       set_timers(this);
115
+   }
116
+
117
+   return 0;
118
 }
119
 
120
 static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
121
 {
122
    struct impl *this = object;
123
-   bool following;
124
+   struct reassign_io_info info = { .this = this, .position = this->position, .clock = this->clock };
125
 
126
    spa_return_val_if_fail(this != NULL, -EINVAL);
127
 
128
    switch (id) {
129
    case SPA_IO_Clock:
130
-       this->clock = data;
131
-       if (this->clock != NULL) {
132
-           spa_scnprintf(this->clock->name,
133
-                   sizeof(this->clock->name),
134
+       info.clock = data;
135
+       if (info.clock != NULL) {
136
+           spa_scnprintf(info.clock->name,
137
+                   sizeof(info.clock->name),
138
                    "%s", this->props.clock_name);
139
        }
140
        break;
141
    case SPA_IO_Position:
142
-       this->position = data;
143
+       info.position = data;
144
        break;
145
    default:
146
        return -ENOENT;
147
    }
148
 
149
-   following = is_following(this);
150
-   if (this->started && following != this->following) {
151
-       spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following);
152
-       this->following = following;
153
-       spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this);
154
+   if (this->started) {
155
+       spa_loop_invoke(this->data_loop, do_reassign_io, 0, NULL, 0, true, &info);
156
+   } else {
157
+       this->clock = info.clock;
158
+       this->position = info.position;
159
    }
160
+
161
    return 0;
162
 }
163
 
164
@@ -395,6 +432,62 @@
165
    return 0;
166
 }
167
 
168
+static uint32_t get_queued_frames(struct impl *this)
169
+{
170
+   struct port *port = &this->port;
171
+   uint32_t bytes = 0;
172
+   struct buffer *b;
173
+
174
+   spa_list_for_each(b, &port->ready, link) {
175
+       struct spa_data *d = b->buf->datas;
176
+
177
+       bytes += d0.chunk->size;
178
+   }
179
+
180
+   if (bytes > port->ready_offset)
181
+       bytes -= port->ready_offset;
182
+   else
183
+       bytes = 0;
184
+
185
+   /* Count (partially) encoded packet */
186
+   bytes += this->tmp_buffer_used;
187
+   bytes += this->block_count * this->block_size;
188
+
189
+   return bytes / port->frame_size;
190
+}
191
+
192
+static uint64_t get_reference_time(struct impl *this, uint64_t *duration_ns_ret)
193
+{
194
+   struct port *port = &this->port;
195
+   uint64_t t, duration_ns;
196
+
197
+   if (!this->process_rate || !this->process_duration) {
198
+       if (this->position) {
199
+           this->process_duration = this->position->clock.duration;
200
+           this->process_rate = this->position->clock.rate.denom;
201
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
201
 
1
@@ -114,7 +114,8 @@
2
    struct port port;
3
 
4
    unsigned int started:1;
5
-   unsigned int transport_acquired:1;
6
+   unsigned int start_ready:1;
7
+   unsigned int transport_started:1;
8
    unsigned int following:1;
9
    unsigned int matching:1;
10
    unsigned int resampling:1;
11
@@ -258,7 +259,8 @@
12
    struct port *port = &this->port;
13
 
14
    set_timers(this);
15
-   spa_bt_decode_buffer_recover(&port->buffer);
16
+   if (this->transport_started)
17
+       spa_bt_decode_buffer_recover(&port->buffer);
18
    return 0;
19
 }
20
 
21
@@ -561,12 +563,15 @@
22
 {
23
    struct port *port = &this->port;
24
 
25
+   if (!this->transport_started)
26
+       port->buffer.corr = 1.0;
27
+
28
    if (this->position && port->rate_match) {
29
        port->rate_match->rate = 1 / port->buffer.corr;
30
 
31
        this->matching = this->following;
32
        this->resampling = this->matching ||
33
-           (port->current_format.info.raw.rate != this->position->clock.rate.denom);
34
+           (port->current_format.info.raw.rate != this->position->clock.target_rate.denom);
35
    } else {
36
        this->matching = false;
37
        this->resampling = false;
38
@@ -607,8 +612,8 @@
39
            now_time, now_time - prev_time);
40
 
41
    if (SPA_LIKELY(this->position)) {
42
-       duration = this->position->clock.duration;
43
-       rate = this->position->clock.rate.denom;
44
+       duration = this->position->clock.target_duration;
45
+       rate = this->position->clock.target_rate.denom;
46
    } else {
47
        duration = 1024;
48
        rate = 48000;
49
@@ -620,7 +625,8 @@
50
 
51
    if (SPA_LIKELY(this->clock)) {
52
        this->clock->nsec = now_time;
53
-       this->clock->position += duration;
54
+       this->clock->rate = this->clock->target_rate;
55
+       this->clock->position += this->clock->duration;
56
        this->clock->duration = duration;
57
        this->clock->rate_diff = port->buffer.corr;
58
        this->clock->next_nsec = this->next_time;
59
@@ -643,15 +649,15 @@
60
    struct port *port = &this->port;
61
    uint32_t flags;
62
 
63
-   if (this->transport_acquired)
64
+   if (this->transport_started)
65
        return 0;
66
+   if (!this->start_ready)
67
+       return -EIO;
68
 
69
-   spa_log_debug(this->log, "%p: transport %p acquire", this,
70
-           this->transport);
71
-   if ((res = spa_bt_transport_acquire(this->transport, false)) < 0)
72
-       return res;
73
+   spa_return_val_if_fail(this->transport != NULL, -EIO);
74
 
75
-   this->transport_acquired = true;
76
+   spa_log_debug(this->log, "%p: start transport state:%d",
77
+           this, this->transport->state);
78
 
79
    flags = this->is_duplex ? 0 : MEDIA_CODEC_FLAG_SINK;
80
 
81
@@ -724,25 +730,16 @@
82
        set_duplex_timeout(this, this->duplex_timeout);
83
    }
84
 
85
-   this->timer_source.data = this;
86
-   this->timer_source.fd = this->timerfd;
87
-   this->timer_source.func = media_on_timeout;
88
-   this->timer_source.mask = SPA_IO_IN;
89
-   this->timer_source.rmask = 0;
90
-   spa_loop_add_source(this->data_loop, &this->timer_source);
91
-
92
    this->sample_count = 0;
93
 
94
-   setup_matching(this);
95
-
96
-   set_timers(this);
97
+   this->transport_started = true;
98
 
99
    return 0;
100
 }
101
 
102
 static int do_start(struct impl *this)
103
 {
104
-   int res = 0;
105
+   int res;
106
 
107
    if (this->started)
108
        return 0;
109
@@ -751,16 +748,31 @@
110
 
111
    this->following = is_following(this);
112
 
113
-   spa_log_debug(this->log, "%p: start state:%d following:%d",
114
-           this, this->transport->state, this->following);
115
+   this->start_ready = true;
116
 
117
-   if (this->transport->state >= SPA_BT_TRANSPORT_STATE_PENDING ||
118
-           this->is_duplex || this->codec->bap)
119
-       res = transport_start(this);
120
+   spa_log_debug(this->log, "%p: start following:%d", this, this->following);
121
+
122
+   spa_log_debug(this->log, "%p: transport %p acquire", this,
123
+           this->transport);
124
+   if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) {
125
+       this->start_ready = false;
126
+       return res;
127
+   }
128
+
129
+   this->timer_source.data = this;
130
+   this->timer_source.fd = this->timerfd;
131
+   this->timer_source.func = media_on_timeout;
132
+   this->timer_source.mask = SPA_IO_IN;
133
+   this->timer_source.rmask = 0;
134
+   spa_loop_add_source(this->data_loop, &this->timer_source);
135
+
136
+   setup_matching(this);
137
+
138
+   set_timers(this);
139
 
140
    this->started = true;
141
 
142
-   return res;
143
+   return 0;
144
 }
145
 
146
 static int do_remove_source(struct spa_loop *loop,
147
@@ -771,54 +783,58 @@
148
                void *user_data)
149
 {
150
    struct impl *this = user_data;
151
-   struct itimerspec ts;
152
 
153
    spa_log_debug(this->log, "%p: remove source", this);
154
 
155
+   if (this->timer_source.loop)
156
+       spa_loop_remove_source(this->data_loop, &this->timer_source);
157
+   set_timeout(this, 0);
158
+
159
+   return 0;
160
+}
161
+
162
+static int do_remove_transport_source(struct spa_loop *loop,
163
+               bool async,
164
+               uint32_t seq,
165
+               const void *data,
166
+               size_t size,
167
+               void *user_data)
168
+{
169
+   struct impl *this = user_data;
170
+
171
+   spa_log_debug(this->log, "%p: remove transport source", this);
172
+
173
+   this->transport_started = false;
174
+
175
    set_duplex_timeout(this, 0);
176
 
177
    if (this->source.loop)
178
        spa_loop_remove_source(this->data_loop, &this->source);
179
 
180
-   if (this->timer_source.loop)
181
-       spa_loop_remove_source(this->data_loop, &this->timer_source);
182
-   ts.it_value.tv_sec = 0;
183
-   ts.it_value.tv_nsec = 0;
184
-   ts.it_interval.tv_sec = 0;
185
-   ts.it_interval.tv_nsec = 0;
186
-   spa_system_timerfd_settime(this->data_system, this->timerfd, 0, &ts, NULL);
187
-
188
    return 0;
189
 }
190
 
191
-static int transport_stop(struct impl *this)
192
+static void transport_stop(struct impl *this)
193
 {
194
    struct port *port = &this->port;
195
-   int res;
196
+
197
+   if (!this->transport_started)
198
+       return;
199
 
200
    spa_log_debug(this->log, "%p: transport stop", this);
201
pipewire-0.3.67.tar.gz/spa/plugins/bluez5/meson.build -> pipewire-0.3.68.tar.gz/spa/plugins/bluez5/meson.build Changed
9
 
1
@@ -19,6 +19,7 @@
2
   'sco-sink.c',
3
   'sco-source.c',
4
   'sco-io.c',
5
+  'iso-io.c',
6
   'quirks.c',
7
   'player.c',
8
   'bluez5-device.c',
9
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
51
 
1
@@ -868,13 +868,20 @@
2
    spa_log_trace(this->log, "%p: timer %"PRIu64" %"PRIu64"", this,
3
            now_time, now_time - prev_time);
4
 
5
-   update_position(this);
6
+   if (SPA_LIKELY(this->position)) {
7
+       this->duration = this->position->clock.target_duration;
8
+       this->rate = this->position->clock.target_rate.denom;
9
+   } else {
10
+       this->duration = 1024;
11
+       this->rate = 48000;
12
+   }
13
 
14
    this->next_time = now_time + this->duration * SPA_NSEC_PER_SEC / this->rate;
15
 
16
    if (SPA_LIKELY(this->clock)) {
17
        this->clock->nsec = now_time;
18
-       this->clock->position += this->duration;
19
+       this->clock->rate = this->clock->target_rate;
20
+       this->clock->position += this->clock->duration;
21
        this->clock->duration = this->duration;
22
        this->clock->rate_diff = 1.0f;
23
        this->clock->next_nsec = this->next_time;
24
@@ -1103,17 +1110,10 @@
25
        void *user_data)
26
 {
27
    struct impl *this = user_data;
28
-   struct itimerspec ts;
29
 
30
    if (this->timer_source.loop)
31
        spa_loop_remove_source(this->data_loop, &this->timer_source);
32
-
33
-   ts.it_value.tv_sec = 0;
34
-   ts.it_value.tv_nsec = 0;
35
-   ts.it_interval.tv_sec = 0;
36
-   ts.it_interval.tv_nsec = 0;
37
-   spa_system_timerfd_settime(this->data_system, this->timerfd, 0, &ts, NULL);
38
-
39
+   set_timeout(this, 0);
40
    return 0;
41
 }
42
 
43
@@ -1217,7 +1217,6 @@
44
        void *user_data)
45
 {
46
    struct impl *this = user_data;
47
-
48
    set_timers(this);
49
    return 0;
50
 }
51
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/rate-control.h Added
135
 
1
@@ -0,0 +1,133 @@
2
+/* Spa Bluez5 rate control */
3
+/* SPDX-FileCopyrightText: Copyright © 2022 Pauli Virtanen */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_BLUEZ5_RATE_CONTROL_H
7
+#define SPA_BLUEZ5_RATE_CONTROL_H
8
+
9
+#include <spa/utils/defs.h>
10
+
11
+/**
12
+ * Rate controller.
13
+ *
14
+ * It's here in a form, where it operates on the running average
15
+ * so it's compatible with the level spike determination, and
16
+ * clamping the rate to a range is easy. The impulse response
17
+ * is similar to spa_dll, and step response does not have sign changes.
18
+ *
19
+ * The controller iterates as
20
+ *
21
+ *    avg(j+1) = (1 - beta) avg(j) + beta level(j)
22
+ *    corr(j+1) = corr(j) + a avg(j+1) - avg(j) / duration
23
+ *           + b avg(j) - target / duration
24
+ *
25
+ * with beta = duration/avg_period < 0.5 is the moving average parameter,
26
+ * and a = beta/3 + ..., b = beta^2/27 + ....
27
+ *
28
+ * This choice results to c(j) being low-pass filtered, and buffer level(j)
29
+ * converging towards target with stable damped evolution with eigenvalues
30
+ * real and close to each other around (1 - beta)^(1/3).
31
+ *
32
+ * Derivation:
33
+ *
34
+ * The deviation from the buffer level target evolves as
35
+ *
36
+ *     delta(j) = level(j) - target
37
+ *     delta(j+1) = delta(j) + r(j) - c(j+1)
38
+ *
39
+ * where r is samples received in one duration, and c corrected rate
40
+ * (samples per duration).
41
+ *
42
+ * The rate correction is in general determined by linear filter f
43
+ *
44
+ *     c(j+1) = c(j) + \sum_{k=0}^\infty delta(j - k) f(k)
45
+ *
46
+ * If \sum_k f(k) is not zero, the only fixed point is c=r, delta=0,
47
+ * so this structure (if the filter is stable) rate matches and
48
+ * drives buffer level to target.
49
+ *
50
+ * The z-transform then is
51
+ *
52
+ *     delta(z) = G(z) r(z)
53
+ *     c(z) = F(z) delta(z)
54
+ *     G(z) = (z - 1) / (z - 1)^2 + z f(z)
55
+ *     F(z) = f(z) / (z - 1)
56
+ *
57
+ * We now want: poles of G(z) must be in |z|<1 for stability, F(z)
58
+ * should damp high frequencies, and f(z) is causal.
59
+ *
60
+ * To satisfy the conditions, take
61
+ *
62
+ *     (z - 1)^2 + z f(z) = p(z) / q(z)
63
+ *
64
+ * where p(z) is polynomial with leading term z^n with wanted root
65
+ * structure, and q(z) is any polynomial with leading term z^{n-2}.
66
+ * This guarantees f(z) is causal, and G(z) = (z-1) q(z) / p(z).
67
+ * We can choose p(z) and q(z) to improve low-pass properties of F(z).
68
+ *
69
+ * Simplest choice is p(z)=(z-x)^2 and q(z)=1, but that gives flat
70
+ * high frequency response in F(z). Better choice is p(z) = (z-u)*(z-v)*(z-w)
71
+ * and q(z) = z - r. To make F(z) better lowpass, one can cancel
72
+ * a resulting 1/z pole in F(z) by setting r=u*v*w. Then,
73
+ *
74
+ *     G(z) = (z - u*v*w)*(z - 1) / (z - u)*(z - v)*(z - w)
75
+ *     F(z) = (a z + b - a) / (z - 1) *     H(z)
76
+ *     H(z) = beta / (z - 1 + beta)
77
+ *     beta = 1 - u*v*w
78
+ *     a = (1-u) + (1-v) + (1-w) - beta / beta
79
+ *     b = (1-u)*(1-v)*(1-w) / beta
80
+ *
81
+ * which corresponds to iteration for c(j):
82
+ *
83
+ *    avg(j+1) = (1 - beta) avg(j) + beta delta(j)
84
+ *    c(j+1) = c(j) + a avg(j+1) - avg(j) + b avg(j)
85
+ *
86
+ * So the controller operates on the running average,
87
+ * which gives the low-pass property for c(j).
88
+ *
89
+ * The simplest filter is obtained by putting the poles at
90
+ * u=v=w=(1-beta)**(1/3). Since beta << 1, computing the root
91
+ * can be avoided by expanding in series.
92
+ *
93
+ * Overshoot in impulse response could be reduced by moving one of the
94
+ * poles closer to z=1, but this increases the step response time.
95
+ */
96
+struct spa_bt_rate_control
97
+{
98
+   double avg;
99
+   double corr;
100
+};
101
+
102
+static void spa_bt_rate_control_init(struct spa_bt_rate_control *this, double level)
103
+{
104
+   this->avg = level;
105
+   this->corr = 1.0;
106
+}
107
+
108
+static double spa_bt_rate_control_update(struct spa_bt_rate_control *this, double level,
109
+       double target, double duration, double period, double rate_diff_max)
110
+{
111
+   /*
112
+    * u = (1 - beta)^(1/3)
113
+    * x = a / beta
114
+    * y = b / beta
115
+    * a = (2 + u) * (1 - u)^2 / beta
116
+    * b = (1 - u)^3 / beta
117
+    * beta -> 0
118
+    */
119
+   const double beta = SPA_CLAMP(duration / period, 0, 0.5);
120
+   const double x = 1.0/3;
121
+   const double y = beta/27;
122
+   double avg;
123
+
124
+   avg = beta * level + (1 - beta) * this->avg;
125
+   this->corr += x * (avg - this->avg) / period
126
+       + y * (this->avg - target) / period;
127
+   this->avg = avg;
128
+
129
+   this->corr = SPA_CLAMP(this->corr, 1 - rate_diff_max, 1 + rate_diff_max);
130
+
131
+   return this->corr;
132
+}
133
+
134
+#endif
135
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
19
 
1
@@ -89,7 +89,7 @@
2
        int res;
3
 
4
    read_again:
5
-       res = read(io->fd, io->read_buffer, SPA_MIN(io->read_mtu, MAX_MTU));
6
+       res = recv(io->fd, io->read_buffer, SPA_MIN(io->read_mtu, MAX_MTU), MSG_DONTWAIT);
7
        if (res <= 0) {
8
            if (errno == EINTR) {
9
                /* retry if interrupted */
10
@@ -165,7 +165,7 @@
11
    do {
12
        int written;
13
 
14
-       written = write(io->fd, buf, packet_size);
15
+       written = send(io->fd, buf, packet_size, MSG_DONTWAIT | MSG_NOSIGNAL);
16
        if (written < 0) {
17
            if (errno == EINTR) {
18
                /* retry if interrupted */
19
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
201
 
1
@@ -116,6 +116,8 @@
2
 
3
    /* Flags */
4
    unsigned int started:1;
5
+   unsigned int start_ready:1;
6
+   unsigned int transport_started:1;
7
    unsigned int following:1;
8
    unsigned int flush_pending:1;
9
 
10
@@ -360,9 +362,17 @@
11
    return bytes / port->frame_size;
12
 }
13
 
14
-static void flush_data(struct impl *this)
15
+static int flush_data(struct impl *this)
16
 {
17
    struct port *port = &this->port;
18
+   int processed = 0;
19
+   int written;
20
+
21
+   spa_assert(this->transport_started);
22
+
23
+   if (this->transport == NULL || this->transport->sco_io == NULL || !this->flush_timer_source.loop)
24
+       return -EIO;
25
+
26
    const uint32_t min_in_size =
27
        (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ?
28
        MSBC_DECODED_SIZE : this->transport->write_mtu;
29
@@ -372,18 +382,13 @@
30
    const uint32_t packet_samples = min_in_size / port->frame_size;
31
    const uint64_t packet_time = (uint64_t)packet_samples * SPA_NSEC_PER_SEC
32
        / port->current_format.info.raw.rate;
33
-   int processed = 0;
34
-   int written;
35
-
36
-   if (this->transport == NULL || this->transport->sco_io == NULL)
37
-       return;
38
 
39
    while (!spa_list_is_empty(&port->ready) && port->write_buffer_size < min_in_size) {
40
        struct spa_data *datas;
41
 
42
        /* get buffer */
43
        if (!port->current_buffer) {
44
-           spa_return_if_fail(!spa_list_is_empty(&port->ready));
45
+           spa_return_val_if_fail(!spa_list_is_empty(&port->ready), -EIO);
46
            port->current_buffer = spa_list_first(&port->ready, struct buffer, link);
47
            port->ready_offset = 0;
48
        }
49
@@ -418,14 +423,14 @@
50
 
51
    if (this->flush_pending) {
52
        spa_log_trace(this->log, "%p: wait for flush timer", this);
53
-       return;
54
+       return 0;
55
    }
56
 
57
    if (port->write_buffer_size < min_in_size) {
58
        /* wait for more data */
59
        spa_log_trace(this->log, "%p: skip flush", this);
60
        enable_flush_timer(this, false);
61
-       return;
62
+       return 0;
63
    }
64
 
65
    if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
66
@@ -445,7 +450,7 @@
67
                this->buffer_next + 2, MSBC_ENCODED_SIZE - 3, &out_encoded);
68
        if (processed < 0) {
69
            spa_log_warn(this->log, "sbc_encode failed: %d", processed);
70
-           return;
71
+           return -EINVAL;
72
        }
73
        this->buffer_next += out_encoded + 3;
74
        port->write_buffer_size = 0;
75
@@ -539,18 +544,19 @@
76
    }
77
 
78
    enable_flush_timer(this, true);
79
-   return;
80
+   return 0;
81
 
82
 stop:
83
-   if (this->source.loop)
84
-       spa_loop_remove_source(this->data_loop, &this->source);
85
    enable_flush_timer(this, false);
86
+   if (this->flush_timer_source.loop)
87
+       spa_loop_remove_source(this->data_loop, &this->flush_timer_source);
88
+   return -EIO;
89
 }
90
 
91
 static void sco_on_flush_timeout(struct spa_source *source)
92
 {
93
    struct impl *this = source->data;
94
-   uint64_t exp;
95
+   uint64_t exp = 0;
96
    int res;
97
 
98
    spa_log_trace(this->log, "%p: flush on timeout", this);
99
@@ -582,9 +588,6 @@
100
    uint64_t prev_time, now_time;
101
    int res;
102
 
103
-   if (this->transport == NULL)
104
-       return;
105
-
106
    if (this->started) {
107
        if ((res = spa_system_timerfd_read(this->data_system, this->timerfd, &exp)) < 0) {
108
            if (res != -EAGAIN)
109
@@ -600,8 +603,8 @@
110
            now_time, now_time - prev_time);
111
 
112
    if (SPA_LIKELY(this->position)) {
113
-       duration = this->position->clock.duration;
114
-       rate = this->position->clock.rate.denom;
115
+       duration = this->position->clock.target_duration;
116
+       rate = this->position->clock.target_rate.denom;
117
    } else {
118
        duration = 1024;
119
        rate = 48000;
120
@@ -611,7 +614,8 @@
121
 
122
    if (SPA_LIKELY(this->clock)) {
123
        this->clock->nsec = now_time;
124
-       this->clock->position += duration;
125
+       this->clock->rate = this->clock->target_rate;
126
+       this->clock->position += this->clock->duration;
127
        this->clock->duration = duration;
128
        this->clock->rate_diff = 1.0f;
129
        this->clock->next_nsec = this->next_time;
130
@@ -639,28 +643,22 @@
131
     return (a*b)/gcd(a,b);
132
 }
133
 
134
-static int do_start(struct impl *this)
135
+static int transport_start(struct impl *this)
136
 {
137
-   bool do_accept;
138
    int res;
139
 
140
    /* Don't do anything if the node has already started */
141
-   if (this->started)
142
+   if (this->transport_started)
143
        return 0;
144
+   if (!this->start_ready)
145
+       return -EIO;
146
 
147
    /* Make sure the transport is valid */
148
    spa_return_val_if_fail(this->transport != NULL, -EIO);
149
 
150
    this->following = is_following(this);
151
 
152
-   spa_log_debug(this->log, "%p: start following:%d", this, this->following);
153
-
154
-   /* Do accept if Gateway; otherwise do connect for Head Unit */
155
-   do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY;
156
-
157
-   /* acquire the socket fd (false -> connect | true -> accept) */
158
-   if ((res = spa_bt_transport_acquire(this->transport, do_accept)) < 0)
159
-       return res;
160
+   spa_log_debug(this->log, "%p: start transport", this);
161
 
162
    /* Init mSBC if needed */
163
    if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
164
@@ -689,14 +687,6 @@
165
    if ((res = spa_bt_transport_ensure_sco_io(this->transport, this->data_loop)) < 0)
166
        goto fail;
167
 
168
-   /* Add the timeout callback */
169
-   this->source.data = this;
170
-   this->source.fd = this->timerfd;
171
-   this->source.func = sco_on_timeout;
172
-   this->source.mask = SPA_IO_IN;
173
-   this->source.rmask = 0;
174
-   spa_loop_add_source(this->data_loop, &this->source);
175
-
176
    this->flush_timer_source.data = this;
177
    this->flush_timer_source.fd = this->flush_timerfd;
178
    this->flush_timer_source.func = sco_on_flush_timeout;
179
@@ -706,20 +696,58 @@
180
 
181
    /* start processing */
182
    this->flush_pending = false;
183
-   set_timers(this);
184
 
185
    /* Set the started flag */
186
-   this->started = true;
187
+   this->transport_started = true;
188
 
189
    return 0;
190
 
191
 fail:
192
    free(this->buffer);
193
    this->buffer = NULL;
194
-   spa_bt_transport_release(this->transport);
195
    return res;
196
 }
197
 
198
+static int do_start(struct impl *this)
199
+{
200
+   bool do_accept;
201
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
201
 
1
@@ -113,9 +113,12 @@
2
    struct port port;
3
 
4
    unsigned int started:1;
5
+   unsigned int start_ready:1;
6
+   unsigned int transport_started:1;
7
    unsigned int following:1;
8
    unsigned int matching:1;
9
    unsigned int resampling:1;
10
+   unsigned int io_error:1;
11
 
12
    struct spa_source timer_source;
13
    int timerfd;
14
@@ -230,7 +233,8 @@
15
    struct port *port = &this->port;
16
 
17
    set_timers(this);
18
-   spa_bt_decode_buffer_recover(&port->buffer);
19
+   if (this->transport_started)
20
+       spa_bt_decode_buffer_recover(&port->buffer);
21
    return 0;
22
 }
23
 
24
@@ -495,6 +499,10 @@
25
    uint32_t decoded;
26
    uint64_t dt;
27
 
28
+   /* Drop data when not started */
29
+   if (!this->started)
30
+       return 0;
31
+
32
    if (this->transport == NULL) {
33
        spa_log_debug(this->log, "no transport, stop reading");
34
        goto stop;
35
@@ -548,6 +556,7 @@
36
    return 0;
37
 
38
 stop:
39
+   this->io_error = true;
40
    return 1;
41
 }
42
 
43
@@ -555,12 +564,15 @@
44
 {
45
    struct port *port = &this->port;
46
 
47
+   if (!this->transport_started)
48
+       port->buffer.corr = 1.0;
49
+
50
    if (this->position && port->rate_match) {
51
        port->rate_match->rate = 1 / port->buffer.corr;
52
 
53
        this->matching = this->following;
54
        this->resampling = this->matching ||
55
-           (port->current_format.info.raw.rate != this->position->clock.rate.denom);
56
+           (port->current_format.info.raw.rate != this->position->clock.target_rate.denom);
57
    } else {
58
        this->matching = false;
59
        this->resampling = false;
60
@@ -583,9 +595,6 @@
61
    uint64_t prev_time, now_time;
62
    int res;
63
 
64
-   if (this->transport == NULL)
65
-       return;
66
-
67
    if (this->started) {
68
        if ((res = spa_system_timerfd_read(this->data_system, this->timerfd, &exp)) < 0) {
69
            if (res != -EAGAIN)
70
@@ -602,8 +611,8 @@
71
            now_time, now_time - prev_time);
72
 
73
    if (SPA_LIKELY(this->position)) {
74
-       duration = this->position->clock.duration;
75
-       rate = this->position->clock.rate.denom;
76
+       duration = this->position->clock.target_duration;
77
+       rate = this->position->clock.target_rate.denom;
78
    } else {
79
        duration = 1024;
80
        rate = 48000;
81
@@ -615,7 +624,8 @@
82
 
83
    if (SPA_LIKELY(this->clock)) {
84
        this->clock->nsec = now_time;
85
-       this->clock->position += duration;
86
+       this->clock->rate = this->clock->target_rate;
87
+       this->clock->position += this->clock->duration;
88
        this->clock->duration = duration;
89
        this->clock->rate_diff = port->buffer.corr;
90
        this->clock->next_nsec = this->next_time;
91
@@ -646,31 +656,22 @@
92
    return 0;
93
 }
94
 
95
-static int do_start(struct impl *this)
96
+static int transport_start(struct impl *this)
97
 {
98
    struct port *port = &this->port;
99
-   bool do_accept;
100
    int res;
101
 
102
    /* Don't do anything if the node has already started */
103
-   if (this->started)
104
+   if (this->transport_started)
105
        return 0;
106
+   if (!this->start_ready)
107
+       return -EIO;
108
 
109
-   this->following = is_following(this);
110
-
111
-   spa_log_debug(this->log, "%p: start following:%d",
112
-           this, this->following);
113
+   spa_log_debug(this->log, "%p: start transport", this);
114
 
115
    /* Make sure the transport is valid */
116
    spa_return_val_if_fail (this->transport != NULL, -EIO);
117
 
118
-   /* Do accept if Gateway; otherwise do connect for Head Unit */
119
-   do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY;
120
-
121
-   /* acquire the socket fd (false -> connect | true -> accept) */
122
-   if ((res = spa_bt_transport_acquire(this->transport, do_accept)) < 0)
123
-       return res;
124
-
125
    /* Reset the buffers and sample count */
126
    reset_buffers(port);
127
 
128
@@ -694,11 +695,47 @@
129
        this->msbc_buffer_pos = 0;
130
    }
131
 
132
+   this->io_error = false;
133
+
134
    /* Start socket i/o */
135
    if ((res = spa_bt_transport_ensure_sco_io(this->transport, this->data_loop)) < 0)
136
        goto fail;
137
    spa_loop_invoke(this->data_loop, do_add_source, 0, NULL, 0, true, this);
138
 
139
+   /* Set the started flag */
140
+   this->transport_started = true;
141
+
142
+   return 0;
143
+
144
+fail:
145
+   return res;
146
+}
147
+
148
+static int do_start(struct impl *this)
149
+{
150
+   bool do_accept;
151
+   int res;
152
+
153
+   if (this->started)
154
+       return 0;
155
+
156
+   spa_return_val_if_fail(this->transport, -EIO);
157
+
158
+   this->following = is_following(this);
159
+
160
+   this->start_ready = true;
161
+
162
+   spa_log_debug(this->log, "%p: start following:%d", this, this->following);
163
+
164
+   /* Do accept if Gateway; otherwise do connect for Head Unit */
165
+   do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY;
166
+
167
+   /* acquire the socket fd (false -> connect | true -> accept) */
168
+   if ((res = spa_bt_transport_acquire(this->transport, do_accept)) < 0) {
169
+       this->start_ready = false;
170
+       return res;
171
+   }
172
+
173
    /* Start timer */
174
    this->timer_source.data = this;
175
    this->timer_source.fd = this->timerfd;
176
@@ -708,16 +745,12 @@
177
    spa_loop_add_source(this->data_loop, &this->timer_source);
178
 
179
    setup_matching(this);
180
+
181
    set_timers(this);
182
 
183
-   /* Set the started flag */
184
    this->started = true;
185
 
186
    return 0;
187
-
188
-fail:
189
-   spa_bt_transport_release(this->transport);
190
-   return res;
191
 }
192
 
193
 static int do_remove_source(struct spa_loop *loop,
194
@@ -728,42 +761,66 @@
195
                void *user_data)
196
 {
197
    struct impl *this = user_data;
198
-   struct itimerspec ts;
199
-
200
-   if (this->transport && this->transport->sco_io)
201
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
16
 
1
@@ -885,9 +885,12 @@
2
    }
3
    const FrameMetadata &fmd = buffer->metadata();
4
 
5
-
6
-
7
    if (impl->clock) {
8
+       /* FIXME, we should follow the driver clock and target_ values.
9
+        * for now we ignore and use our own. */
10
+       impl->clock->target_rate = port->rate;
11
+       impl->clock->target_duration = 1;
12
+
13
        impl->clock->nsec = fmd.timestamp;
14
        impl->clock->rate = port->rate;
15
        impl->clock->position = fmd.sequence;
16
pipewire-0.3.67.tar.gz/spa/plugins/meson.build -> pipewire-0.3.68.tar.gz/spa/plugins/meson.build Changed
7
 
1
@@ -1,4 +1,4 @@
2
-if alsa_dep.found()
3
+if alsa_dep.found() and host_machine.system() == 'linux'
4
   subdir('alsa')
5
 endif
6
 if get_option('avb').require(host_machine.system() == 'linux', error_message: 'AVB support is only available on Linux').allowed()
7
pipewire-0.3.67.tar.gz/spa/plugins/support/loop.c -> pipewire-0.3.68.tar.gz/spa/plugins/support/loop.c Changed
41
 
1
@@ -333,7 +333,7 @@
2
        impl->enter_count = 1;
3
    } else {
4
        spa_return_if_fail(impl->enter_count > 0);
5
-       spa_return_if_fail(impl->thread == thread_id);
6
+       spa_return_if_fail(pthread_equal(impl->thread, thread_id));
7
        impl->enter_count++;
8
    }
9
    spa_log_trace(impl->log, "%p: enter %p", impl, (void *) impl->thread);
10
@@ -345,7 +345,7 @@
11
    pthread_t thread_id = pthread_self();
12
 
13
    spa_return_if_fail(impl->enter_count > 0);
14
-   spa_return_if_fail(impl->thread == thread_id);
15
+   spa_return_if_fail(pthread_equal(impl->thread, thread_id));
16
 
17
    spa_log_trace(impl->log, "%p: leave %p", impl, (void *) impl->thread);
18
 
19
@@ -356,6 +356,13 @@
20
    }
21
 }
22
 
23
+static int loop_check(void *object)
24
+{
25
+   struct impl *impl = object;
26
+   pthread_t thread_id = pthread_self();
27
+   return (impl->thread == 0 || pthread_equal(impl->thread, thread_id)) ? 1 : 0;
28
+}
29
+
30
 static inline void free_source(struct source_impl *s)
31
 {
32
    detach_source(&s->source);
33
@@ -826,6 +833,7 @@
34
    .enter = loop_enter,
35
    .leave = loop_leave,
36
    .iterate = loop_iterate,
37
+   .check = loop_check,
38
 };
39
 
40
 static const struct spa_loop_utils_methods impl_loop_utils = {
41
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
118
 
1
@@ -88,9 +88,13 @@
2
    clockid_t id;
3
 } clock_info = {
4
    { "realtime", CLOCK_REALTIME },
5
+#ifdef CLOCK_TAI
6
    { "tai", CLOCK_TAI },
7
+#endif
8
    { "monotonic", CLOCK_MONOTONIC },
9
+#ifdef CLOCK_MONOTONIC_RAW
10
    { "monotonic-raw", CLOCK_MONOTONIC_RAW },
11
+#endif
12
    { "boottime", CLOCK_BOOTTIME },
13
 };
14
 
15
@@ -145,7 +149,7 @@
16
 
17
    spa_log_debug(this->log, "%p now:%"PRIu64, this, this->next_time);
18
 
19
-   if (this->following) {
20
+   if (this->following || !this->started) {
21
        set_timeout(this, 0);
22
    } else {
23
        set_timeout(this, this->next_time);
24
@@ -158,7 +162,7 @@
25
    return this->position && this->clock && this->position->clock.id != this->clock->id;
26
 }
27
 
28
-static int do_reassign_follower(struct spa_loop *loop,
29
+static int do_set_timers(struct spa_loop *loop,
30
                bool async,
31
                uint32_t seq,
32
                const void *data,
33
@@ -185,7 +189,7 @@
34
    if (following != this->following) {
35
        spa_log_debug(this->log, NAME" %p: reassign follower %d->%d", this, this->following, following);
36
        this->following = following;
37
-       spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this);
38
+       spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this);
39
    }
40
    return 0;
41
 }
42
@@ -243,8 +247,8 @@
43
        return;
44
    }
45
    if (SPA_LIKELY(this->position)) {
46
-       duration = this->position->clock.duration;
47
-       rate = this->position->clock.rate.denom;
48
+       duration = this->position->clock.target_duration;
49
+       rate = this->position->clock.target_rate.denom;
50
    } else {
51
        duration = 1024;
52
        rate = 48000;
53
@@ -259,15 +263,14 @@
54
 
55
    current_position = scale_u64(current_time, rate, SPA_NSEC_PER_SEC);
56
 
57
-   if (SPA_LIKELY(this->clock))
58
-       position = this->clock->position;
59
-   else
60
-       position = current_position;
61
-
62
    if (this->last_time == 0) {
63
        spa_dll_set_bw(&this->dll, SPA_DLL_BW_MIN, duration, rate);
64
        this->max_error = rate * MAX_ERROR_MS / 1000;
65
        position = current_position;
66
+   } else if (SPA_LIKELY(this->clock)) {
67
+       position = this->clock->position + this->clock->duration;
68
+   } else {
69
+       position = current_position;
70
    }
71
 
72
    /* check the elapsed time of the other clock against
73
@@ -279,7 +282,6 @@
74
    else if (err < -this->max_error)
75
        err = -this->max_error;
76
 
77
-   position += duration;
78
    this->last_time = current_time;
79
 
80
    if (this->tracking) {
81
@@ -287,7 +289,7 @@
82
        this->next_time = nsec + duration / corr * 1e9 / rate;
83
    } else {
84
        corr = 1.0;
85
-       this->next_time = scale_u64(position, SPA_NSEC_PER_SEC, rate);
86
+       this->next_time = scale_u64(position + duration, SPA_NSEC_PER_SEC, rate);
87
    }
88
 
89
    if (SPA_UNLIKELY((this->next_time - this->base_time) > BW_PERIOD)) {
90
@@ -300,6 +302,7 @@
91
 
92
    if (SPA_LIKELY(this->clock)) {
93
        this->clock->nsec = nsec;
94
+       this->clock->rate = this->clock->target_rate;
95
        this->clock->position = position;
96
        this->clock->duration = duration;
97
        this->clock->delay = 0;
98
@@ -319,9 +322,9 @@
99
        return 0;
100
 
101
    this->following = is_following(this);
102
-   set_timers(this);
103
    this->started = true;
104
    this->last_time = 0;
105
+   spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this);
106
    return 0;
107
 }
108
 
109
@@ -330,7 +333,7 @@
110
    if (!this->started)
111
        return 0;
112
    this->started = false;
113
-   set_timeout(this, 0);
114
+   spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this);
115
    return 0;
116
 }
117
 
118
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
68
 
1
@@ -191,7 +191,7 @@
2
        return res;
3
    this->next_time = SPA_TIMESPEC_TO_NSEC(&now);
4
 
5
-   if (this->following) {
6
+   if (this->following || !this->started) {
7
        set_timeout(this, 0);
8
    } else {
9
        set_timeout(this, this->next_time);
10
@@ -204,7 +204,7 @@
11
    return this->position && this->clock && this->position->clock.id != this->clock->id;
12
 }
13
 
14
-static int do_reassign_follower(struct spa_loop *loop,
15
+static int do_set_timers(struct spa_loop *loop,
16
                bool async,
17
                uint32_t seq,
18
                const void *data,
19
@@ -227,7 +227,7 @@
20
    if (following != this->following) {
21
        spa_log_debug(this->log, NAME" %p: reassign follower %d->%d", this, this->following, following);
22
        this->following = following;
23
-       spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this);
24
+       spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this);
25
    }
26
    return 0;
27
 }
28
@@ -280,8 +280,8 @@
29
    nsec = this->next_time;
30
 
31
    if (SPA_LIKELY(this->position)) {
32
-       duration = this->position->clock.duration;
33
-       rate = this->position->clock.rate.denom;
34
+       duration = this->position->clock.target_duration;
35
+       rate = this->position->clock.target_rate.denom;
36
    } else {
37
        duration = 1024;
38
        rate = 48000;
39
@@ -291,7 +291,8 @@
40
 
41
    if (SPA_LIKELY(this->clock)) {
42
        this->clock->nsec = nsec;
43
-       this->clock->position += duration;
44
+       this->clock->rate = this->clock->target_rate;
45
+       this->clock->position += this->clock->duration;
46
        this->clock->duration = duration;
47
        this->clock->delay = 0;
48
        this->clock->rate_diff = 1.0;
49
@@ -309,8 +310,8 @@
50
        return 0;
51
 
52
    this->following = is_following(this);
53
-   set_timers(this);
54
    this->started = true;
55
+   spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this);
56
    return 0;
57
 }
58
 
59
@@ -319,7 +320,7 @@
60
    if (!this->started)
61
        return 0;
62
    this->started = false;
63
-   set_timeout(this, 0);
64
+   spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this);
65
    return 0;
66
 }
67
 
68
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
13
 
1
@@ -1289,6 +1289,11 @@
2
    spa_log_trace(this->log, "v4l2 %p: have output %d", this, buf.index);
3
 
4
    if (this->clock) {
5
+       /* FIXME, we should follow the driver clock and target_ values.
6
+        * for now we ignore and use our own. */
7
+       this->clock->target_rate = port->rate;
8
+       this->clock->target_duration = 1;
9
+
10
        this->clock->nsec = pts;
11
        this->clock->rate = port->rate;
12
        this->clock->position = buf.sequence;
13
pipewire-0.3.67.tar.gz/spa/plugins/volume/volume.c -> pipewire-0.3.68.tar.gz/spa/plugins/volume/volume.c Changed
10
 
1
@@ -525,7 +525,7 @@
2
        b->flags = direction == SPA_DIRECTION_INPUT ? BUFFER_FLAG_OUT : 0;
3
        b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h));
4
 
5
-       if (d0.data == NULL) {
6
+       if (d0.data != NULL) {
7
            b->ptr = d0.data;
8
            b->size = d0.maxsize;
9
        } else {
10
pipewire-0.3.68.tar.gz/src/daemon/client-rt.conf.avail Added
2
 
1
+(directory)
2
pipewire-0.3.68.tar.gz/src/daemon/client-rt.conf.avail/20-upmix.conf.in Added
10
 
1
@@ -0,0 +1,8 @@
2
+# Enables upmixing
3
+stream.properties = {
4
+    channelmix.upmix      = true
5
+    channelmix.upmix-method = psd  # none, simple
6
+    channelmix.lfe-cutoff = 150
7
+    channelmix.fc-cutoff  = 12000
8
+    channelmix.rear-delay = 12.0
9
+}
10
pipewire-0.3.68.tar.gz/src/daemon/client-rt.conf.avail/meson.build Added
13
 
1
@@ -0,0 +1,11 @@
2
+conf_files = 
3
+ '20-upmix.conf',
4
+
5
+
6
+foreach c : conf_files
7
+  configure_file(input : '@0@.in'.format(c),
8
+                 output : c,
9
+                 configuration : conf_config,
10
+                 install_dir : pipewire_confdatadir / 'client-rt.conf.avail')
11
+endforeach
12
+
13
pipewire-0.3.68.tar.gz/src/daemon/client.conf.avail Added
2
 
1
+(directory)
2
pipewire-0.3.68.tar.gz/src/daemon/client.conf.avail/20-upmix.conf.in Added
10
 
1
@@ -0,0 +1,8 @@
2
+# Enables upmixing
3
+stream.properties = {
4
+    channelmix.upmix      = true
5
+    channelmix.upmix-method = psd  # none, simple
6
+    channelmix.lfe-cutoff = 150
7
+    channelmix.fc-cutoff  = 12000
8
+    channelmix.rear-delay = 12.0
9
+}
10
pipewire-0.3.68.tar.gz/src/daemon/client.conf.avail/meson.build Added
13
 
1
@@ -0,0 +1,11 @@
2
+conf_files = 
3
+ '20-upmix.conf',
4
+
5
+
6
+foreach c : conf_files
7
+  configure_file(input : '@0@.in'.format(c),
8
+                 output : c,
9
+                 configuration : conf_config,
10
+                 install_dir : pipewire_confdatadir / 'client.conf.avail')
11
+endforeach
12
+
13
pipewire-0.3.67.tar.gz/src/daemon/meson.build -> pipewire-0.3.68.tar.gz/src/daemon/meson.build Changed
66
 
1
@@ -86,31 +86,18 @@
2
                output : 'pipewire-uninstalled.conf',
3
                configuration : conf_config_uninstalled)
4
 
5
-pipewire_exec = executable('pipewire',
6
-  pipewire_daemon_sources,
7
-  install: true,
8
-  c_args : pipewire_c_args,
9
-  include_directories :  configinc ,
10
-  dependencies :  spa_dep, pipewire_dep, ,
11
-)
12
-
13
-executable('pipewire-pulse',
14
-  pipewire_daemon_sources,
15
-  install: true,
16
-  c_args : pipewire_c_args,
17
-  include_directories :  configinc ,
18
-  dependencies :  spa_dep, pipewire_dep, ,
19
-)
20
+conf_avail_folders = 
21
+  'pipewire.conf.avail',
22
+  'client.conf.avail',
23
+  'client-rt.conf.avail',
24
+  'pipewire-pulse.conf.avail',
25
+
26
 
27
-executable('pipewire-avb',
28
-  pipewire_daemon_sources,
29
-  install: true,
30
-  c_args : pipewire_c_args,
31
-  include_directories :  configinc ,
32
-  dependencies :  spa_dep, pipewire_dep, ,
33
-)
34
+foreach c : conf_avail_folders
35
+  subdir(c)
36
+endforeach
37
 
38
-executable('pipewire-aes67',
39
+pipewire_exec = executable('pipewire',
40
   pipewire_daemon_sources,
41
   install: true,
42
   c_args : pipewire_c_args,
43
@@ -120,6 +107,22 @@
44
 
45
 ln = find_program('ln')
46
 
47
+foreach alias : 'pipewire-pulse', 'pipewire-avb', 'pipewire-aes67'
48
+  custom_target(
49
+    alias,
50
+    build_by_default: true,
51
+    install: false,
52
+    command: ln, '-sf', meson.project_build_root() + '/@INPUT@', '@OUTPUT@',
53
+    input: pipewire_exec,
54
+    output: alias,
55
+  )
56
+  install_symlink(
57
+    alias,
58
+    pointing_to: pipewire_exec.name(),
59
+    install_dir: pipewire_bindir,
60
+  )
61
+endforeach
62
+
63
 custom_target('pipewire-uninstalled',
64
   build_by_default: true,
65
   install: false,
66
pipewire-0.3.67.tar.gz/src/daemon/minimal.conf.in -> pipewire-0.3.68.tar.gz/src/daemon/minimal.conf.in Changed
17
 
1
@@ -326,7 +326,6 @@
2
     #        link.output.port = capture_1
3
     #        link.input.node  = my-mic
4
     #        link.input.port  = input_FL
5
-    #        link.passive     = true
6
     #    }
7
     #}
8
     #{ factory = link-factory
9
@@ -335,7 +334,6 @@
10
     #        link.output.port = capture_2
11
     #        link.input.node  = my-mic
12
     #        link.input.port  = input_FR
13
-    #        link.passive     = true
14
     #    }
15
     #}
16
 
17
pipewire-0.3.68.tar.gz/src/daemon/pipewire-pulse.conf.avail Added
2
 
1
+(directory)
2
pipewire-0.3.68.tar.gz/src/daemon/pipewire-pulse.conf.avail/20-upmix.conf.in Added
10
 
1
@@ -0,0 +1,8 @@
2
+# Enables upmixing
3
+stream.properties = {
4
+    channelmix.upmix      = true
5
+    channelmix.upmix-method = psd  # none, simple
6
+    channelmix.lfe-cutoff = 150
7
+    channelmix.fc-cutoff  = 12000
8
+    channelmix.rear-delay = 12.0
9
+}
10
pipewire-0.3.68.tar.gz/src/daemon/pipewire-pulse.conf.avail/meson.build Added
13
 
1
@@ -0,0 +1,11 @@
2
+conf_files = 
3
+ '20-upmix.conf',
4
+
5
+
6
+foreach c : conf_files
7
+  configure_file(input : '@0@.in'.format(c),
8
+                 output : c,
9
+                 configuration : conf_config,
10
+                 install_dir : pipewire_confdatadir / 'pipewire-pulse.conf.avail')
11
+endforeach
12
+
13
pipewire-0.3.68.tar.gz/src/daemon/pipewire.conf.avail Added
2
 
1
+(directory)
2
pipewire-0.3.68.tar.gz/src/daemon/pipewire.conf.avail/10-rates.conf.in Added
6
 
1
@@ -0,0 +1,4 @@
2
+# Adds more common rates
3
+context.properties = {
4
+    default.clock.allowed-rates =  44100 48000 88200 96000 
5
+}
6
pipewire-0.3.68.tar.gz/src/daemon/pipewire.conf.avail/20-upmix.conf.in Added
10
 
1
@@ -0,0 +1,8 @@
2
+# Enables upmixing
3
+stream.properties = {
4
+    channelmix.upmix      = true
5
+    channelmix.upmix-method = psd  # none, simple
6
+    channelmix.lfe-cutoff = 150
7
+    channelmix.fc-cutoff  = 12000
8
+    channelmix.rear-delay = 12.0
9
+}
10
pipewire-0.3.68.tar.gz/src/daemon/pipewire.conf.avail/meson.build Added
14
 
1
@@ -0,0 +1,12 @@
2
+conf_files = 
3
+ '10-rates.conf',
4
+ '20-upmix.conf',
5
+
6
+
7
+foreach c : conf_files
8
+  configure_file(input : '@0@.in'.format(c),
9
+                 output : c,
10
+                 configuration : conf_config,
11
+                 install_dir : pipewire_confdatadir / 'pipewire.conf.avail')
12
+endforeach
13
+
14
pipewire-0.3.67.tar.gz/src/examples/export-spa.c -> pipewire-0.3.68.tar.gz/src/examples/export-spa.c Changed
19
 
1
@@ -36,7 +36,7 @@
2
    uint32_t id;
3
 };
4
 
5
-static void proxy_event_bound(void *_data, uint32_t global_id)
6
+static void proxy_event_bound_props(void *_data, uint32_t global_id, const struct spa_dict *props)
7
 {
8
    struct data *data = _data;
9
    if (data->id != global_id) {
10
@@ -47,7 +47,7 @@
11
 
12
 static const struct pw_proxy_events proxy_events = {
13
    PW_VERSION_PROXY_EVENTS,
14
-   .bound = proxy_event_bound,
15
+   .bound_props = proxy_event_bound_props,
16
 };
17
 
18
 static int make_node(struct data *data)
19
pipewire-0.3.67.tar.gz/src/gst/gstpipewiredeviceprovider.c -> pipewire-0.3.68.tar.gz/src/gst/gstpipewiredeviceprovider.c Changed
88
 
1
@@ -210,6 +210,7 @@
2
   const struct pw_node_info *info = data->info;
3
   const gchar *element = NULL;
4
   GstPipeWireDevice *gstdev;
5
+  int priority = 0;
6
 
7
   if (info->max_input_ports > 0 && info->max_output_ports == 0) {
8
     type = GST_PIPEWIRE_DEVICE_TYPE_SINK;
9
@@ -224,11 +225,16 @@
10
   props = gst_structure_new_empty ("pipewire-proplist");
11
   if (info->props) {
12
     const struct spa_dict_item *item;
13
+    const char *str;
14
+
15
     spa_dict_for_each (item, info->props)
16
       gst_structure_set (props, item->key, G_TYPE_STRING, item->value, NULL);
17
 
18
     klass = spa_dict_lookup (info->props, PW_KEY_MEDIA_CLASS);
19
     name = spa_dict_lookup (info->props, PW_KEY_NODE_DESCRIPTION);
20
+
21
+    if ((str = spa_dict_lookup(info->props, PW_KEY_PRIORITY_SESSION)))
22
+      priority = atoi(str);
23
   }
24
   if (klass == NULL)
25
     klass = "unknown/unknown";
26
@@ -244,26 +250,56 @@
27
   gstdev->serial = data->serial;
28
   gstdev->type = type;
29
   gstdev->element = element;
30
+  gstdev->priority = priority;
31
   if (props)
32
     gst_structure_free (props);
33
 
34
   return GST_DEVICE (gstdev);
35
 }
36
 
37
+static int
38
+compare_device_session_priority (const void *a,
39
+                                 const void *b)
40
+{
41
+  const GstPipeWireDevice *dev_a = a;
42
+  const GstPipeWireDevice *dev_b = b;
43
+
44
+  if (dev_a->priority < dev_b->priority)
45
+    return 1;
46
+  else if (dev_a->priority > dev_b->priority)
47
+    return -1;
48
+  else
49
+    return 0;
50
+}
51
+
52
 static void do_add_nodes(GstPipeWireDeviceProvider *self)
53
 {
54
   struct node_data *nd;
55
+  GList *new_devices = NULL;
56
+  GList *l;
57
 
58
   spa_list_for_each(nd, &self->nodes, link) {
59
     if (nd->dev != NULL)
60
        continue;
61
     pw_log_info("add node %d", nd->id);
62
     nd->dev = new_node (self, nd);
63
-    if (nd->dev) {
64
-      if(self->list_only)
65
-        self->devices = g_list_prepend (self->devices, gst_object_ref_sink (nd->dev));
66
-      else
67
-        gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), nd->dev);
68
+    if (nd->dev)
69
+      new_devices = g_list_prepend (new_devices, nd->dev);
70
+  }
71
+  if (!new_devices)
72
+    return;
73
+
74
+  new_devices = g_list_sort (new_devices,
75
+                             compare_device_session_priority);
76
+  for (l = new_devices; l != NULL; l = l->next) {
77
+    GstDevice *device = l->data;
78
+
79
+    if(self->list_only) {
80
+      self->devices = g_list_insert_sorted (self->devices,
81
+                                            gst_object_ref_sink (device),
82
+                                            compare_device_session_priority);
83
+    } else {
84
+      gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), device);
85
     }
86
   }
87
 }
88
pipewire-0.3.67.tar.gz/src/gst/gstpipewiredeviceprovider.h -> pipewire-0.3.68.tar.gz/src/gst/gstpipewiredeviceprovider.h Changed
9
 
1
@@ -39,6 +39,7 @@
2
   uint64_t            serial;
3
   int                 fd;
4
   const gchar        *element;
5
+  int                 priority;
6
 };
7
 
8
 struct _GstPipeWireDeviceClass {
9
pipewire-0.3.67.tar.gz/src/modules/meson.build -> pipewire-0.3.68.tar.gz/src/modules/meson.build Changed
64
 
1
@@ -28,6 +28,8 @@
2
   'module-rt.c',
3
   'module-raop-discover.c',
4
   'module-raop-sink.c',
5
+  'module-rtp-sap.c',
6
+  'module-rtp-session.c',
7
   'module-rtp-source.c',
8
   'module-rtp-sink.c',
9
   'module-session-manager.c',
10
@@ -153,7 +155,7 @@
11
   install : true,
12
   install_dir : modules_install_dir,
13
   install_rpath: modules_install_dir,
14
-  dependencies : mathlib, dl_lib, pipewire_dep,
15
+  dependencies : mathlib, dl_lib, pipewire_dep, audioconvert_dep,
16
 )
17
 
18
 pipewire_module_profiler = shared_library('pipewire-module-profiler',
19
@@ -519,16 +521,41 @@
20
 summary({'ROC': roc_dep.found()}, bool_yn: true, section: 'Streaming between daemons')
21
 
22
 pipewire_module_rtp_source = shared_library('pipewire-module-rtp-source',
23
-   'module-rtp-source.c' ,
24
+   'module-rtp-source.c',
25
+    'module-rtp/stream.c' ,
26
   include_directories : configinc,
27
   install : true,
28
   install_dir : modules_install_dir,
29
   install_rpath: modules_install_dir,
30
-  dependencies : mathlib, dl_lib, rt_lib, pipewire_dep,
31
+  dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep,
32
 )
33
 
34
 pipewire_module_rtp_sink = shared_library('pipewire-module-rtp-sink',
35
-   'module-rtp-sink.c' ,
36
+   'module-rtp-sink.c',
37
+    'module-rtp/stream.c' ,
38
+  include_directories : configinc,
39
+  install : true,
40
+  install_dir : modules_install_dir,
41
+  install_rpath: modules_install_dir,
42
+  dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep,
43
+)
44
+
45
+build_module_rtp_session = avahi_dep.found()
46
+if build_module_rtp_session
47
+  pipewire_module_rtp_session = shared_library('pipewire-module-rtp-session',
48
+     'module-rtp/stream.c',
49
+      'module-zeroconf-discover/avahi-poll.c',
50
+     'module-rtp-session.c' ,
51
+    include_directories : configinc,
52
+    install : true,
53
+    install_dir : modules_install_dir,
54
+    install_rpath: modules_install_dir,
55
+    dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep, opus_dep,
56
+  )
57
+endif
58
+
59
+pipewire_module_rtp_sap = shared_library('pipewire-module-rtp-sap',
60
+   'module-rtp-sap.c' ,
61
   include_directories : configinc,
62
   install : true,
63
   install_dir : modules_install_dir,
64
pipewire-0.3.67.tar.gz/src/modules/module-access.c -> pipewire-0.3.68.tar.gz/src/modules/module-access.c Changed
16
 
1
@@ -111,10 +111,10 @@
2
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
3
 #define PW_LOG_TOPIC_DEFAULT mod_topic
4
 
5
-#define MODULE_USAGE   " access.force=flatpak  "       \
6
-           " access.allowed=<cmd-line>  "  \
7
-           " access.rejected=<cmd-line>  " \
8
-           " access.restricted=<cmd-line>  "   \
9
+#define MODULE_USAGE   "( access.force=flatpak ) "     \
10
+           "( access.allowed=  <cmd-line>,..  ) "  \
11
+           "( access.rejected=  <cmd-line>,..  ) " \
12
+           "( access.restricted=  <cmd-line>,..  ) "   \
13
 
14
 static const struct spa_dict_item module_props = {
15
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
16
pipewire-0.3.67.tar.gz/src/modules/module-adapter.c -> pipewire-0.3.68.tar.gz/src/modules/module-adapter.c Changed
19
 
1
@@ -26,7 +26,7 @@
2
 #define PW_LOG_TOPIC_DEFAULT mod_topic
3
 
4
 #define FACTORY_USAGE  SPA_KEY_FACTORY_NAME"=<factory-name> " \
5
-           ""SPA_KEY_LIBRARY_NAME"=<library-name> " \
6
+           "("SPA_KEY_LIBRARY_NAME"=<library-name>) " \
7
            ADAPTER_USAGE
8
 
9
 static const struct spa_dict_item module_props = {
10
@@ -203,7 +203,7 @@
11
 
12
        handle = pw_context_load_spa_handle(d->context,
13
                factory_name,
14
-               properties ? &properties->dict : NULL);
15
+               &properties->dict);
16
        if (handle == NULL)
17
            goto error_errno;
18
 
19
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
28
 
1
@@ -240,6 +240,7 @@
2
 struct server *avdecc_server_new(struct impl *impl, struct spa_dict *props)
3
 {
4
    struct server *server;
5
+   const char *str;
6
    int res = 0;
7
 
8
    server = calloc(1, sizeof(*server));
9
@@ -248,7 +249,8 @@
10
 
11
    server->impl = impl;
12
    spa_list_append(&impl->servers, &server->link);
13
-   server->ifname = strdup(spa_dict_lookup(props, "ifname"));
14
+   str = spa_dict_lookup(props, "ifname");
15
+   server->ifname = str ? strdup(str) : NULL;
16
    spa_hook_list_init(&server->listener_list);
17
    spa_list_init(&server->descriptors);
18
    spa_list_init(&server->streams);
19
@@ -309,7 +311,7 @@
20
    if (server->source)
21
        pw_loop_destroy_source(impl->loop, server->source);
22
    if (server->timer)
23
-       pw_loop_destroy_source(impl->loop, server->source);
24
+       pw_loop_destroy_source(impl->loop, server->timer);
25
    spa_hook_list_clean(&server->listener_list);
26
    free(server);
27
 }
28
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
24
 
1
@@ -1143,18 +1143,20 @@
2
    client_node_removed(_data);
3
 }
4
 
5
-static void client_node_bound(void *_data, uint32_t global_id)
6
+static void client_node_bound_props(void *_data, uint32_t global_id, const struct spa_dict *props)
7
 {
8
    struct node_data *data = _data;
9
    pw_log_debug("%p: bound %u", data, global_id);
10
    data->remote_id = global_id;
11
+   if (props)
12
+       pw_properties_update(data->node->properties, props);
13
 }
14
 
15
 static const struct pw_proxy_events proxy_client_node_events = {
16
    PW_VERSION_PROXY_EVENTS,
17
    .removed = client_node_removed,
18
    .destroy = client_node_destroy,
19
-   .bound = client_node_bound,
20
+   .bound_props = client_node_bound_props,
21
 };
22
 
23
 static int node_ready(void *d, int status)
24
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
54
 
1
@@ -35,6 +35,10 @@
2
  * - a new virtual sink that forwards audio to other sinks
3
  * - a new virtual source that combines audio from other sources
4
  *
5
+ * The sources and sink that need to be combined can be selected using generic match
6
+ * rules. This makes it possible to combine static nodes or nodes based on certain
7
+ * properties.
8
+ *
9
  * ## Module Options
10
  *
11
  * - `node.name`: a unique name for the stream
12
@@ -42,6 +46,7 @@
13
  * - `combine.mode` = capture | playback | sink | source, default sink
14
  * - `combine.props = {}`: properties to be passed to the sink/source
15
  * - `stream.props = {}`: properties to be passed to the streams
16
+ * - `stream.rules = {}`: rules for matching streams, use create-stream actions
17
  *
18
  * ## General options
19
  *
20
@@ -198,15 +203,15 @@
21
 #define DEFAULT_CHANNELS 2
22
 #define DEFAULT_POSITION " FL FR "
23
 
24
-#define MODULE_USAGE   " node.latency=<latency as fraction>  "             \
25
-           " combine.mode=<mode of stream, playback|capture|sink|source>, default:sink  "  \
26
-           " node.name=<name of the stream>  "                 \
27
-           " node.description=<description of the stream>  "           \
28
-           " audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) ">  "    \
29
-           " audio.position=<channel map, default:"DEFAULT_POSITION">  "       \
30
-           " combine.props=<properties>  "                     \
31
-           " stream.props=<properties>  "                  \
32
-           " stream.rules=<properties>  "
33
+#define MODULE_USAGE   "( node.latency=<latency as fraction> ) "               \
34
+           "( combine.mode=<mode of stream, playback|capture|sink|source>, default:sink ) "    \
35
+           "( node.name=<name of the stream> ) "                   \
36
+           "( node.description=<description of the stream> ) "         \
37
+           "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) "> ) "  \
38
+           "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) "     \
39
+           "( combine.props=<properties> ) "                   \
40
+           "( stream.props=<properties> ) "                    \
41
+           "( stream.rules=<properties> ) "
42
 
43
 
44
 static const struct spa_dict_item module_props = {
45
@@ -576,7 +581,7 @@
46
        if (impl->mode == MODE_CAPTURE || impl->mode == MODE_SINK)
47
            str = " { matches =  { media.class = \"Audio/Sink\" }  "
48
                "  actions = { create-stream = {} } } ";
49
-       else if (impl->mode == MODE_PLAYBACK || impl->mode == MODE_SOURCE)
50
+       else
51
            str = " { matches =  { media.class = \"Audio/Source\" }  "
52
                "  actions = { create-stream = {} } } ";
53
    }
54
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
201
 
1
@@ -33,6 +33,8 @@
2
 #include <spa/support/plugin-loader.h>
3
 #include <spa/interfaces/audio/aec.h>
4
 
5
+#include <spa/plugins/audioconvert/wavfile.h>
6
+
7
 #include <pipewire/impl.h>
8
 #include <pipewire/pipewire.h>
9
 
10
@@ -155,19 +157,19 @@
11
 static const struct spa_dict_item module_props = {
12
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
13
    { PW_KEY_MODULE_DESCRIPTION, "Echo Cancellation" },
14
-   { PW_KEY_MODULE_USAGE, "  remote.name=<remote>  "
15
-               " node.latency=<latency as fraction>  "
16
-               " audio.rate=<sample rate>  "
17
-               " audio.channels=<number of channels>  "
18
-               " audio.position=<channel map>  "
19
-               " buffer.max_size=<max buffer size in ms>  "
20
-               " buffer.play_delay=<delay as fraction>  "
21
-               " library.name =<library name>  "
22
-               " aec.args=<aec arguments>  "
23
-               " capture.props=<properties>  "
24
-               " source.props=<properties>  "
25
-               " sink.props=<properties>  "
26
-               " playback.props=<properties>  " },
27
+   { PW_KEY_MODULE_USAGE, " ( remote.name=<remote> ) "
28
+               "( node.latency=<latency as fraction> ) "
29
+               "( audio.rate=<sample rate> ) "
30
+               "( audio.channels=<number of channels> ) "
31
+               "( audio.position=<channel map> ) "
32
+               "( buffer.max_size=<max buffer size in ms> ) "
33
+               "( buffer.play_delay=<delay as fraction> ) "
34
+               "( library.name =<library name> ) "
35
+               "( aec.args=<aec arguments> ) "
36
+               "( capture.props=<properties> ) "
37
+               "( source.props=<properties> ) "
38
+               "( sink.props=<properties> ) "
39
+               "( playback.props=<properties> ) " },
40
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
41
 };
42
 
43
@@ -231,8 +233,50 @@
44
    struct spa_plugin_loader *loader;
45
 
46
    bool monitor_mode;
47
+
48
+   char wav_path512;
49
+   struct wav_file *wav_file;
50
 };
51
 
52
+static inline void aec_run(struct impl *impl, const float *rec, const float *play,
53
+       float *out, uint32_t n_samples)
54
+{
55
+   spa_audio_aec_run(impl->aec, rec, play, out, n_samples);
56
+
57
+   if (SPA_UNLIKELY(impl->wav_path0)) {
58
+       if (impl->wav_file == NULL) {
59
+           struct wav_file_info info;
60
+
61
+           info.info.info.raw = impl->info;
62
+           info.info.info.raw.channels *= 3;
63
+           info.info.media_type = SPA_MEDIA_TYPE_audio;
64
+           info.info.media_subtype = SPA_MEDIA_SUBTYPE_raw;
65
+
66
+           impl->wav_file = wav_file_open(impl->wav_path,
67
+                   "w", &info);
68
+           if (impl->wav_file == NULL)
69
+               pw_log_warn("can't open wav path '%s': %m",
70
+                       impl->wav_path);
71
+       }
72
+       if (impl->wav_file) {
73
+           uint32_t i, c = impl->info.channels;
74
+           const float *datac * 3;
75
+
76
+           for (i = 0; i < c; i++) {
77
+               datai       = playi;
78
+               datai +   c = reci;
79
+               datai + 2*c = outi;
80
+           }
81
+           wav_file_write(impl->wav_file, (void*)data, n_samples);
82
+       } else {
83
+           spa_zero(impl->wav_path);
84
+       }
85
+   } else if (impl->wav_file != NULL) {
86
+       wav_file_close(impl->wav_file);
87
+       impl->wav_file = NULL;
88
+   }
89
+}
90
+
91
 static void process(struct impl *impl)
92
 {
93
    struct pw_buffer *cout;
94
@@ -326,11 +370,11 @@
95
                pdi = play_delayedi + delay_left;
96
                oi = outi + delay_left;
97
            }
98
-           spa_audio_aec_run(impl->aec, rec, pd, o, size / sizeof(float) - delay_left);
99
+           aec_run(impl, rec, pd, o, size / sizeof(float) - delay_left);
100
        }
101
    } else {
102
        /* run the canceller */
103
-       spa_audio_aec_run(impl->aec, rec, play_delayed, out, size / sizeof(float));
104
+       aec_run(impl, rec, play_delayed, out, size / sizeof(float));
105
    }
106
 
107
    /* Next, copy over the output to the output ringbuffer */
108
@@ -520,6 +564,27 @@
109
    }
110
 }
111
 
112
+static void reset_buffers(struct impl *impl)
113
+{
114
+   uint32_t index, i;
115
+
116
+   spa_ringbuffer_init(&impl->rec_ring);
117
+   spa_ringbuffer_init(&impl->play_ring);
118
+   spa_ringbuffer_init(&impl->play_delayed_ring);
119
+   spa_ringbuffer_init(&impl->out_ring);
120
+
121
+   for (i = 0; i < impl->info.channels; i++) {
122
+       memset(impl->rec_bufferi, 0, impl->rec_ringsize);
123
+       memset(impl->play_bufferi, 0, impl->play_ringsize);
124
+       memset(impl->out_bufferi, 0, impl->out_ringsize);
125
+   }
126
+
127
+   spa_ringbuffer_get_write_index(&impl->play_ring, &index);
128
+   spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay)));
129
+   spa_ringbuffer_get_read_index(&impl->play_ring, &index);
130
+   spa_ringbuffer_read_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay)));
131
+}
132
+
133
 static void input_param_latency_changed(struct impl *impl, const struct spa_pod *param)
134
 {
135
    struct spa_latency_info latency;
136
@@ -538,21 +603,64 @@
137
    else
138
        pw_stream_update_params(impl->capture, params, 1);
139
 }
140
+
141
 static struct spa_pod* get_props_param(struct impl* impl, struct spa_pod_builder* b)
142
 {
143
-   if (spa_audio_aec_get_params(impl->aec, NULL) > 0) {
144
-       struct spa_pod_frame f2;
145
-       spa_pod_builder_push_object(
146
-           b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
147
-       spa_pod_builder_prop(b, SPA_PROP_params, 0);
148
-       spa_pod_builder_push_struct(b, &f1);
149
+   struct spa_pod_frame f2;
150
 
151
+   spa_pod_builder_push_object(
152
+       b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
153
+   spa_pod_builder_prop(b, SPA_PROP_params, 0);
154
+   spa_pod_builder_push_struct(b, &f1);
155
+
156
+   spa_pod_builder_string(b, "debug.aec.wav-path");
157
+   spa_pod_builder_string(b, impl->wav_path);
158
+
159
+   if (spa_audio_aec_get_params(impl->aec, NULL) > 0)
160
        spa_audio_aec_get_params(impl->aec, b);
161
 
162
-       spa_pod_builder_pop(b, &f1);
163
-       return spa_pod_builder_pop(b, &f0);
164
+   spa_pod_builder_pop(b, &f1);
165
+   return spa_pod_builder_pop(b, &f0);
166
+}
167
+
168
+static int set_params(struct impl* impl, const struct spa_pod *params)
169
+{
170
+   struct spa_pod_parser prs;
171
+   struct spa_pod_frame f;
172
+   int changed = 0;
173
+
174
+   spa_pod_parser_pod(&prs, params);
175
+   if (spa_pod_parser_push_struct(&prs, &f) < 0)
176
+       return 0;
177
+
178
+   while (true) {
179
+       const char *name;
180
+       struct spa_pod *pod;
181
+       char value512;
182
+
183
+       if (spa_pod_parser_get_string(&prs, &name) < 0)
184
+           break;
185
+
186
+       if (spa_pod_parser_get_pod(&prs, &pod) < 0)
187
+           break;
188
+
189
+       if (spa_pod_is_string(pod)) {
190
+           spa_pod_copy_string(pod, sizeof(value), value);
191
+       } else if (spa_pod_is_none(pod)) {
192
+           spa_zero(value);
193
+       } else
194
+           continue;
195
+
196
+       pw_log_info("key:'%s' val:'%s'", name, value);
197
+
198
+       if (spa_streq(name, "debug.aec.wav-path")) {
199
+           spa_scnprintf(impl->wav_path,
200
+               sizeof(impl->wav_path), "%s", value);
201
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
24
 
1
@@ -83,14 +83,14 @@
2
 #define DEFAULT_CHANNELS 2
3
 #define DEFAULT_POSITION " FL FR "
4
 
5
-#define MODULE_USAGE   " node.latency=<latency as fraction>  "             \
6
-           " node.name=<name of the nodes>  "                  \
7
-           " node.description=<description of the nodes>  "            \
8
-           " audio.format=<format, default:"DEFAULT_FORMAT">  "            \
9
-           " audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)">  "           \
10
-           " audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) ">  "    \
11
-           " audio.position=<channel map, default:"DEFAULT_POSITION">  "       \
12
-           " stream.props=<properties>  "
13
+#define MODULE_USAGE   "( node.latency=<latency as fraction> ) "               \
14
+           "( node.name=<name of the nodes> ) "                    \
15
+           "( node.description=<description of the nodes> ) "          \
16
+           "( audio.format=<format, default:"DEFAULT_FORMAT"> ) "          \
17
+           "( audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)"> ) "         \
18
+           "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) "> ) "  \
19
+           "( audio.position=<channel map, default:"DEFAULT_POSITION">  "      \
20
+           "( stream.props=<properties> ) "
21
 
22
 
23
 static const struct spa_dict_item module_props = {
24
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
24
 
1
@@ -83,14 +83,14 @@
2
 #define DEFAULT_CHANNELS 2
3
 #define DEFAULT_POSITION " FL FR "
4
 
5
-#define MODULE_USAGE   " node.latency=<latency as fraction>  "             \
6
-           " node.name=<name of the nodes>  "                  \
7
-           " node.description=<description of the nodes>  "            \
8
-           " audio.format=<format, default:"DEFAULT_FORMAT">  "            \
9
-           " audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)">  "           \
10
-           " audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) ">  "    \
11
-           " audio.position=<channel map, default:"DEFAULT_POSITION">  "       \
12
-           " stream.props=<properties>  "
13
+#define MODULE_USAGE   "( node.latency=<latency as fraction> ) "               \
14
+           "( node.name=<name of the nodes> ) "                    \
15
+           "( node.description=<description of the nodes> ) "          \
16
+           "( audio.format=<format, default:"DEFAULT_FORMAT"> ) "          \
17
+           "( audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)"> ) "         \
18
+           "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS) "> ) "  \
19
+           "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) "     \
20
+           "( stream.props=<properties> ) "
21
 
22
 
23
 static const struct spa_dict_item module_props = {
24
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
30
 
1
@@ -33,8 +33,8 @@
2
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
3
 #define PW_LOG_TOPIC_DEFAULT mod_topic
4
 
5
-#define MODULE_USAGE   (" sink.name=<str>  " \
6
-           " sink.description=<str>  ")
7
+#define MODULE_USAGE   ("( sink.name=<str> ) " \
8
+           "( sink.description=<str> ) ")
9
 
10
 static const struct spa_dict_item module_props = {
11
    { PW_KEY_MODULE_AUTHOR, "Pauli Virtanen <pav@iki.fi>" },
12
@@ -162,7 +162,7 @@
13
    pw_proxy_destroy(impl->sink);
14
 }
15
 
16
-static void sink_proxy_bound(void *data, uint32_t id)
17
+static void sink_proxy_bound_props(void *data, uint32_t id, const struct spa_dict *props)
18
 {
19
    struct impl *impl = data;
20
 
21
@@ -186,7 +186,7 @@
22
 static const struct pw_proxy_events sink_proxy_events = {
23
    PW_VERSION_PROXY_EVENTS,
24
    .removed = sink_proxy_removed,
25
-   .bound = sink_proxy_bound,
26
+   .bound_props = sink_proxy_bound_props,
27
    .destroy = sink_proxy_destroy,
28
 };
29
 
30
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
159
 
1
@@ -62,7 +62,7 @@
2
  *     filter.graph = {
3
  *         nodes = 
4
  *             {
5
- *                 type = <ladspa | lv2 | builtin>
6
+ *                 type = <ladspa | lv2 | builtin | sofa>
7
  *                 name = <name>
8
  *                 plugin = <plugin>
9
  *                 label = <label>
10
@@ -89,22 +89,24 @@
11
  * Nodes describe the processing filters in the graph. Use a tool like lv2ls
12
  * or listplugins to get a list of available plugins, labels and the port names.
13
  *
14
- * - `type` is one of `ladspa`, `lv2` or `builtin`
15
+ * - `type` is one of `ladspa`, `lv2`, `builtin` or `sofa`.
16
  * - `name` is the name for this node, you might need this later to refer to this node
17
  *    and its ports when setting controls or making links.
18
  * - `plugin` is the type specific plugin name.
19
  *    - For LADSPA plugins it will append `.so` to find the shared object with that
20
  *       name in the LADSPA plugin path.
21
  *    - For LV2, this is the plugin URI obtained with lv2ls.
22
- *    - For builtin this is ignored
23
+ *    - For builtin and sofa this is ignored
24
  * - `label` is the type specific filter inside the plugin.
25
  *    - For LADSPA this is the label
26
  *    - For LV2 this is unused
27
- *    - For builtin this is the name of the filter to use
28
+ *    - For builtin and sofa this is the name of the filter to use
29
  *
30
- * - `config` contains a filter specific configuration section. The convolver
31
- *            plugin needs this.
32
+ * - `config` contains a filter specific configuration section. Some plugins need
33
+ *            this. (convolver, sofa, delay, ...)
34
  * - `control` contains the initial values for the control ports of the filter.
35
+ *            normally these are given with the port name but it is also possible
36
+ *            to give the control index as the key.
37
  *
38
  * ### Links
39
  *
40
@@ -196,6 +198,7 @@
41
  *                 offset = ...
42
  *                 length = ...
43
  *                 channel = ...
44
+ *                 resample_quality = ...
45
  *             }
46
  *             ...
47
  *         }
48
@@ -218,9 +221,13 @@
49
  *                 can be used as gain.
50
  *     - A filename to load as the IR. This needs to be a file format supported
51
  *               by sndfile.
52
+ *     -  filename, ...  an array of filenames. The file with the closest samplerate match
53
+ *               with the graph samplerate will be used.
54
  * - `offset`  The sample offset in the file as the start of the IR.
55
  * - `length`  The number of samples to use as the IR.
56
  * - `channel` The channel to use from the file as the IR.
57
+ * - `resample_quality` The resample quality in case the IR does not match the graph
58
+ *                      samplerate.
59
  *
60
  * ### Delay
61
  *
62
@@ -259,6 +266,61 @@
63
  *
64
  * It has an input port "In" and an output port "Out".
65
  *
66
+ * ## SOFA filter
67
+ *
68
+ * There is an optional builtin SOFA filter available.
69
+ *
70
+ * ### Spatializer
71
+ *
72
+ * The spatializer can be used to place the sound in a 3D space.
73
+ *
74
+ * The spatializer has an input port "In" and a stereo pair of output ports
75
+ * called "Out L" and "Out R". It requires a config section in the node
76
+ * declaration in this format:
77
+ *
78
+ * The control can be changed at runtime to move the sounds around in the
79
+ * 3D space.
80
+ *
81
+ *\code{.unparsed}
82
+ * filter.graph = {
83
+ *     nodes = 
84
+ *         {
85
+ *             type   = sofa
86
+ *             name   = ...
87
+ *             label  = spatializer
88
+ *             config = {
89
+ *                 blocksize = ...
90
+ *                 tailsize = ...
91
+ *                 filename = ...
92
+ *             }
93
+ *             control = {
94
+ *                 "Azimuth" = ...
95
+ *                 "Elevation" = ...
96
+ *                 "Radius" = ...
97
+ *             }
98
+ *             ...
99
+ *         }
100
+ *     }
101
+ *     ...
102
+ * }
103
+ *\endcode
104
+ *
105
+ * - `blocksize` specifies the size of the blocks to use in the FFT. It is a value
106
+ *               between 64 and 256. When not specified, this value is
107
+ *               computed automatically from the number of samples in the file.
108
+ * - `tailsize` specifies the size of the tail blocks to use in the FFT.
109
+ * - `filename` The SOFA file to load. SOFA files usually end in the .sofa extension
110
+ *              and contain the HRTF for the various spatial positions.
111
+ *
112
+ * - `Azimuth`   controls the azimuth, this is the direction the sound is coming from
113
+ *               in degrees between 0 and 360. 0 is straight ahead. 90 is left, 180
114
+ *               behind, 270 right.
115
+ * - `Elevation` controls the elevation, this is how high/low the signal is in degrees
116
+ *               between -90 and 90. 0 is straight in front, 90 is directly above
117
+ *               and -90 directly below.
118
+ * - `Radius`    controls how far away the signal is as a value between 0 and 100.
119
+ *               default is 1.0.
120
+ *
121
  * ## General options
122
  *
123
  * Options with well-known behavior. Most options can be added to the global
124
@@ -375,16 +437,16 @@
125
 static const struct spa_dict_item module_props = {
126
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
127
    { PW_KEY_MODULE_DESCRIPTION, "Create filter chain streams" },
128
-   { PW_KEY_MODULE_USAGE, "  remote.name=<remote>  "
129
-               " node.latency=<latency as fraction>  "
130
-               " node.description=<description of the nodes>  "
131
-               " audio.rate=<sample rate>  "
132
-               " audio.channels=<number of channels>  "
133
-               " audio.position=<channel map>  "
134
+   { PW_KEY_MODULE_USAGE, " ( remote.name=<remote> ) "
135
+               "( node.latency=<latency as fraction> ) "
136
+               "( node.description=<description of the nodes> ) "
137
+               "( audio.rate=<sample rate> ) "
138
+               "( audio.channels=<number of channels> ) "
139
+               "( audio.position=<channel map> ) "
140
                "filter.graph =  "
141
                "    nodes =  "
142
                "        { "
143
-               "          type = <ladspa | lv2 | builtin> "
144
+               "          type = <ladspa | lv2 | builtin | sofa> "
145
                "          name = <name> "
146
                "          plugin = <plugin> "
147
                "          label = <label> "
148
@@ -402,8 +464,8 @@
149
                "    inputs =  <portname> ...  "
150
                "    outputs =  <portname> ...  "
151
                " "
152
-               " capture.props=<properties>  "
153
-               " playback.props=<properties>  " },
154
+               "( capture.props=<properties> ) "
155
+               "( playback.props=<properties> ) " },
156
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
157
 };
158
 
159
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
19
 
1
@@ -214,7 +214,7 @@
2
    struct fc_plugin *pl = NULL;
3
 
4
    if (plugin0 != '/') {
5
-       const char *search_dirs, *p;
6
+       const char *search_dirs, *p, *state = NULL;
7
        char pathPATH_MAX;
8
        size_t len;
9
 
10
@@ -229,7 +229,7 @@
11
         */
12
        errno = ENAMETOOLONG;
13
 
14
-       while ((p = pw_split_walk(NULL, ":", &len, &search_dirs))) {
15
+       while ((p = pw_split_walk(search_dirs, ":", &len, &state))) {
16
            int pathlen;
17
 
18
            if (len >= sizeof(path))
19
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
67
 
1
@@ -22,22 +22,29 @@
2
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
3
 #define PW_LOG_TOPIC_DEFAULT mod_topic
4
 
5
-#define FACTORY_USAGE  PW_KEY_LINK_OUTPUT_NODE"=<output-node> "    \
6
-           ""PW_KEY_LINK_OUTPUT_PORT"=<output-port> "  \
7
-           PW_KEY_LINK_INPUT_NODE"=<input-node> "      \
8
-           ""PW_KEY_LINK_INPUT_PORT"=<input-port> "    \
9
-           ""PW_KEY_OBJECT_LINGER"=<bool> "        \
10
-           ""PW_KEY_LINK_PASSIVE"=<bool>"
11
+#define FACTORY_USAGE  "("PW_KEY_LINK_OUTPUT_NODE"=<output-node>) "    \
12
+           "("PW_KEY_LINK_OUTPUT_PORT"=<output-port>) "    \
13
+           "("PW_KEY_LINK_INPUT_NODE"=<input-node>) "  \
14
+           "("PW_KEY_LINK_INPUT_PORT"=<input-port>) "  \
15
+           "("PW_KEY_OBJECT_LINGER"=<bool>) "      \
16
+           "("PW_KEY_LINK_PASSIVE"=<bool>)"
17
+
18
+#define MODULE_USAGE   "( allow.link.passive=<bool, default false> ) "
19
 
20
 static const struct spa_dict_item module_props = {
21
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
22
    { PW_KEY_MODULE_DESCRIPTION, "Allow clients to create links" },
23
+   { PW_KEY_MODULE_USAGE, MODULE_USAGE },
24
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
25
 };
26
 
27
 struct factory_data {
28
    struct pw_context *context;
29
 
30
+   struct pw_properties *props;
31
+
32
+   unsigned int allow_passive:1;
33
+
34
    struct pw_impl_module *module;
35
    struct spa_hook module_listener;
36
 
37
@@ -396,6 +403,8 @@
38
        pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d",
39
                pw_impl_client_get_info(client)->id);
40
 
41
+   if (!d->allow_passive)
42
+       pw_properties_set(properties, PW_KEY_LINK_PASSIVE, NULL);
43
 
44
    link = pw_context_create_link(context, outport, inport, NULL, properties, sizeof(struct link_data));
45
    properties = NULL;
46
@@ -462,6 +471,7 @@
47
    d->factory = NULL;
48
    if (d->module)
49
        pw_impl_module_destroy(d->module);
50
+   pw_properties_free(d->props);
51
 }
52
 
53
 static const struct pw_impl_factory_events factory_events = {
54
@@ -527,6 +537,12 @@
55
    data->module = module;
56
    data->context = context;
57
    data->work = pw_context_get_work_queue(context);
58
+   data->props = args ? pw_properties_new_string(args) : NULL;
59
+
60
+   if (data->props) {
61
+       data->allow_passive = pw_properties_get_bool(data->props,
62
+               "allow.link.passive", false);
63
+   }
64
 
65
    spa_list_init(&data->link_list);
66
 
67
pipewire-0.3.67.tar.gz/src/modules/module-loopback.c -> pipewire-0.3.68.tar.gz/src/modules/module-loopback.c Changed
26
 
1
@@ -130,15 +130,15 @@
2
 static const struct spa_dict_item module_props = {
3
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
4
    { PW_KEY_MODULE_DESCRIPTION, "Create loopback streams" },
5
-   { PW_KEY_MODULE_USAGE, "  remote.name=<remote>  "
6
-               " node.latency=<latency as fraction>  "
7
-               " node.description=<description of the nodes>  "
8
-               " audio.rate=<sample rate>  "
9
-               " audio.channels=<number of channels>  "
10
-               " audio.position=<channel map>  "
11
-               " target.delay.sec=<delay as seconds in float>  "
12
-               " capture.props=<properties>  "
13
-               " playback.props=<properties>  " },
14
+   { PW_KEY_MODULE_USAGE, " ( remote.name=<remote> ) "
15
+               "( node.latency=<latency as fraction> ) "
16
+               "( node.description=<description of the nodes> ) "
17
+               "( audio.rate=<sample rate> ) "
18
+               "( audio.channels=<number of channels> ) "
19
+               "( audio.position=<channel map> ) "
20
+               "( target.delay.sec=<delay as seconds in float> ) "
21
+               "( capture.props=<properties> ) "
22
+               "( playback.props=<properties> ) " },
23
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
24
 };
25
 
26
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
41
 
1
@@ -114,18 +114,18 @@
2
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
3
 #define PW_LOG_TOPIC_DEFAULT mod_topic
4
 
5
-#define MODULE_USAGE   " remote.name=<remote>  "               \
6
-           " node.latency=<latency as fraction>  "     \
7
-           " node.name=<name of the nodes>  "          \
8
-           " node.description=<description of the nodes>  "    \
9
-           " target.object=<remote node target name or serial>  "\
10
-           " audio.format=<sample format>  "           \
11
-           " audio.rate=<sample rate>  "               \
12
-           " audio.channels=<number of channels>  "        \
13
-           " audio.position=<channel map>  "           \
14
-           " tunnel.mode=capture|playback|sink|source "        \
15
-           " pipe.filename=<filename> "                \
16
-           " stream.props=<properties>  "
17
+#define MODULE_USAGE   "( remote.name=<remote> ) "             \
18
+           "( node.latency=<latency as fraction> ) "       \
19
+           "( node.name=<name of the nodes> ) "            \
20
+           "( node.description=<description of the nodes> ) "  \
21
+           "( target.object=<remote node target name or serial> ) "\
22
+           "( audio.format=<sample format> ) "         \
23
+           "( audio.rate=<sample rate> ) "             \
24
+           "( audio.channels=<number of channels> ) "      \
25
+           "( audio.position=<channel map> ) "         \
26
+           "( tunnel.mode=capture|playback|sink|source )"      \
27
+           "( pipe.filename=<filename> )"              \
28
+           "( stream.props=<properties> ) "
29
 
30
 
31
 static const struct spa_dict_item module_props = {
32
@@ -294,7 +294,7 @@
33
    .process = capture_stream_process
34
 };
35
 
36
-static int  create_stream(struct impl *impl)
37
+static int create_stream(struct impl *impl)
38
 {
39
    int res;
40
    uint32_t n_params;
41
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
86
 
1
@@ -385,6 +385,7 @@
2
    struct pw_proxy *proxy = data;
3
    struct spa_pod_parser prs;
4
    uint32_t id, global_id;
5
+   struct spa_dict props = SPA_DICT_INIT(NULL, 0);
6
 
7
    spa_pod_parser_init(&prs, msg->data, msg->size);
8
    if (spa_pod_parser_get_struct(&prs,
9
@@ -392,7 +393,33 @@
10
                SPA_POD_Int(&global_id)) < 0)
11
        return -EINVAL;
12
 
13
-   return pw_proxy_notify(proxy, struct pw_core_events, bound_id, 0, id, global_id);
14
+   /* old client / old/new server -> bound_id
15
+    * new client / old server     -> bound_id + bound_props (in case it's using bound_props only) */
16
+   pw_proxy_notify(proxy, struct pw_core_events, bound_id, 0, id, global_id);
17
+   return pw_proxy_notify(proxy, struct pw_core_events, bound_props, 1, id, global_id, &props);
18
+}
19
+
20
+static int core_event_demarshal_bound_props(void *data, const struct pw_protocol_native_message *msg)
21
+{
22
+   struct pw_proxy *proxy = data;
23
+   struct spa_pod_parser prs;
24
+   uint32_t id, global_id;
25
+   struct spa_pod_frame f2;
26
+   struct spa_dict props = SPA_DICT_INIT(NULL, 0);
27
+
28
+   spa_pod_parser_init(&prs, msg->data, msg->size);
29
+   if (spa_pod_parser_push_struct(&prs, &f0) < 0)
30
+       return -EINVAL;
31
+   if (spa_pod_parser_get(&prs,
32
+               SPA_POD_Int(&id),
33
+               SPA_POD_Int(&global_id), NULL) < 0)
34
+       return -EINVAL;
35
+
36
+   parse_dict_struct(&prs, &f1, &props);
37
+
38
+   /* new client / new server -> bound_props + bound_id (in case it's not using bound_props yet) */
39
+   pw_proxy_notify(proxy, struct pw_core_events, bound_id, 0, id, global_id);
40
+   return pw_proxy_notify(proxy, struct pw_core_events, bound_props, 1, id, global_id, &props);
41
 }
42
 
43
 static int core_event_demarshal_add_mem(void *data, const struct pw_protocol_native_message *msg)
44
@@ -526,6 +553,25 @@
45
    pw_protocol_native_end_resource(resource, b);
46
 }
47
 
48
+static void core_event_marshal_bound_props(void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props)
49
+{
50
+   struct pw_resource *resource = data;
51
+   struct spa_pod_builder *b;
52
+   struct spa_pod_frame f;
53
+
54
+   b = pw_protocol_native_begin_resource(resource, PW_CORE_EVENT_BOUND_PROPS, NULL);
55
+
56
+   spa_pod_builder_push_struct(b, &f);
57
+   spa_pod_builder_add(b,
58
+           SPA_POD_Int(id),
59
+           SPA_POD_Int(global_id),
60
+           NULL);
61
+   push_dict(b, props);
62
+   spa_pod_builder_pop(b, &f);
63
+
64
+   pw_protocol_native_end_resource(resource, b);
65
+}
66
+
67
 static void core_event_marshal_add_mem(void *data, uint32_t id, uint32_t type, int fd, uint32_t flags)
68
 {
69
    struct pw_resource *resource = data;
70
@@ -1863,6 +1909,7 @@
71
    .bound_id = &core_event_marshal_bound_id,
72
    .add_mem = &core_event_marshal_add_mem,
73
    .remove_mem = &core_event_marshal_remove_mem,
74
+   .bound_props = &core_event_marshal_bound_props,
75
 };
76
 
77
 static const struct pw_protocol_native_demarshal
78
@@ -1876,6 +1923,7 @@
79
    PW_CORE_EVENT_BOUND_ID = { &core_event_demarshal_bound_id, 0, },
80
    PW_CORE_EVENT_ADD_MEM = { &core_event_demarshal_add_mem, 0, },
81
    PW_CORE_EVENT_REMOVE_MEM = { &core_event_demarshal_remove_mem, 0, },
82
+   PW_CORE_EVENT_BOUND_PROPS = { &core_event_demarshal_bound_props, 0, },
83
 };
84
 
85
 static const struct pw_protocol_marshal pw_protocol_native_core_marshal = {
86
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
13
 
1
@@ -17,9 +17,9 @@
2
    struct module *module;
3
    char *a2 = { NULL };
4
 
5
-   n = pw_split_ip(args, WHITESPACE, 2, a);
6
+   n = args != NULL ? pw_split_ip(args, WHITESPACE, 2, a) : 0;
7
    if (n < 1) {
8
-       pw_log_info("load-module expects module name");
9
+       pw_log_info("load-module expects module name got '%s'", args);
10
        return -EINVAL;
11
    }
12
 
13
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
46
 
1
@@ -116,6 +116,44 @@
2
    return ERR_UNKNOWN;
3
 }
4
 
5
+static inline int err_to_res(int err)
6
+{
7
+   switch (err) {
8
+   case ERR_OK: return 0;
9
+   case ERR_ACCESS: return -EACCES;
10
+   case ERR_COMMAND: return -ENOTTY;
11
+   case ERR_INVALID: return -EINVAL;
12
+   case ERR_EXIST: return -EEXIST;
13
+   case ERR_NOENTITY: return -ENOENT;
14
+   case ERR_CONNECTIONREFUSED: return -ECONNREFUSED;
15
+   case ERR_PROTOCOL: return -EPROTO;
16
+   case ERR_TIMEOUT: return -ETIMEDOUT;
17
+#ifdef ENOKEY
18
+   case ERR_AUTHKEY: return -ENOKEY;
19
+#endif
20
+   case ERR_INTERNAL: return -ENFILE;
21
+   case ERR_CONNECTIONTERMINATED: return -ECONNRESET;
22
+   case ERR_KILLED: return -EFAULT;
23
+   case ERR_INVALIDSERVER: return -EINVAL;
24
+   case ERR_MODINITFAILED: return -EIO;
25
+#ifdef EBADFD
26
+   case ERR_BADSTATE: return -EBADFD;
27
+#endif
28
+   case ERR_NODATA: return -ENODATA;
29
+   case ERR_VERSION: return -EPROTO;
30
+   case ERR_TOOLARGE: return -E2BIG;
31
+   case ERR_NOTSUPPORTED: return -ENOTSUP;
32
+   case ERR_UNKNOWN: return -EIO;
33
+   case ERR_NOEXTENSION: return -ENOTTY;
34
+   case ERR_OBSOLETE: return -ENOTSUP;
35
+   case ERR_NOTIMPLEMENTED: return -ENOSYS;
36
+   case ERR_FORKED: return -EIO;
37
+   case ERR_IO: return -EIO;
38
+   case ERR_BUSY: return -EBUSY;
39
+   }
40
+   return -EIO;
41
+}
42
+
43
 enum {
44
    SUBSCRIPTION_MASK_NULL = 0x0000U,
45
    SUBSCRIPTION_MASK_SINK = 0x0001U,
46
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
19
 
1
@@ -40,7 +40,7 @@
2
    module_schedule_unload(module);
3
 }
4
 
5
-static void module_null_sink_proxy_bound(void *data, uint32_t global_id)
6
+static void module_null_sink_proxy_bound_props(void *data, uint32_t global_id, const struct spa_dict *props)
7
 {
8
    struct module *module = data;
9
    struct module_null_sink_data *d = module->user_data;
10
@@ -63,7 +63,7 @@
11
 static const struct pw_proxy_events proxy_events = {
12
    PW_VERSION_PROXY_EVENTS,
13
    .removed = module_null_sink_proxy_removed,
14
-   .bound = module_null_sink_proxy_bound,
15
+   .bound_props = module_null_sink_proxy_bound_props,
16
    .error = module_null_sink_proxy_error,
17
    .destroy = module_null_sink_proxy_destroy,
18
 };
19
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
39
 
1
@@ -51,13 +51,16 @@
2
 
3
    fprintf(f, "{");
4
    pw_properties_serialize_dict(f, &data->global_props->dict, 0);
5
-   fprintf(f, " stream.props = {");
6
+   fprintf(f, " stream.rules = ");
7
+   fprintf(f, " { matches =  { rtp.session = \"~.*\" }  "),
8
+   fprintf(f, "    actions = { create-stream = { ");
9
    pw_properties_serialize_dict(f, &data->stream_props->dict, 0);
10
-   fprintf(f, " } }");
11
+   fprintf(f, "    } } }  ");
12
+   fprintf(f, " }");
13
    fclose(f);
14
 
15
    data->mod = pw_context_load_module(module->impl->context,
16
-           "libpipewire-module-rtp-source",
17
+           "libpipewire-module-rtp-sap",
18
            args, NULL);
19
 
20
    free(args);
21
@@ -113,14 +116,13 @@
22
        res = -errno;
23
        goto out;
24
    }
25
-   if ((str = pw_properties_get(props, "sink")) != NULL)
26
-       pw_properties_set(stream_props, PW_KEY_TARGET_OBJECT, str);
27
-
28
    if ((str = pw_properties_get(props, "sap_address")) != NULL)
29
        pw_properties_set(global_props, "sap.ip", str);
30
 
31
+   if ((str = pw_properties_get(props, "sink")) != NULL)
32
+       pw_properties_set(stream_props, PW_KEY_TARGET_OBJECT, str);
33
    if ((str = pw_properties_get(props, "latency_msec")) != NULL)
34
-       pw_properties_set(global_props, "sess.latency.msec", str);
35
+       pw_properties_set(stream_props, "sess.latency.msec", str);
36
 
37
    d->module = module;
38
    d->stream_props = stream_props;
39
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
166
 
1
@@ -19,8 +19,13 @@
2
    struct spa_hook mod_listener;
3
    struct pw_impl_module *mod;
4
 
5
+   struct spa_hook sap_listener;
6
+   struct pw_impl_module *sap;
7
+
8
    struct pw_properties *stream_props;
9
    struct pw_properties *global_props;
10
+   struct pw_properties *sap_props;
11
+
12
    struct spa_audio_info_raw info;
13
 };
14
 
15
@@ -37,6 +42,19 @@
16
    .destroy = module_destroy
17
 };
18
 
19
+static void sap_module_destroy(void *data)
20
+{
21
+   struct module_rtp_send_data *d = data;
22
+   spa_hook_remove(&d->sap_listener);
23
+   d->sap = NULL;
24
+   module_schedule_unload(d->module);
25
+}
26
+
27
+static const struct pw_impl_module_events sap_module_events = {
28
+   PW_VERSION_IMPL_MODULE_EVENTS,
29
+   .destroy = sap_module_destroy
30
+};
31
+
32
 static int module_rtp_send_load(struct module *module)
33
 {
34
    struct module_rtp_send_data *data = module->user_data;
35
@@ -75,7 +93,6 @@
36
    data->mod = pw_context_load_module(module->impl->context,
37
            "libpipewire-module-rtp-sink",
38
            args, NULL);
39
-
40
    free(args);
41
 
42
    if (data->mod == NULL)
43
@@ -85,6 +102,29 @@
44
            &data->mod_listener,
45
            &module_events, data);
46
 
47
+   if ((f = open_memstream(&args, &size)) == NULL)
48
+       return -errno;
49
+
50
+   fprintf(f, "{");
51
+   pw_properties_serialize_dict(f, &data->sap_props->dict, 0);
52
+   fprintf(f, " stream.rules = ");
53
+   fprintf(f, "   { matches =  { pulse.module.id = %u }  ", module->index);
54
+   fprintf(f, "     actions = { announce-stream = { } } ");
55
+   fprintf(f, "   }  }");
56
+   fclose(f);
57
+
58
+   data->sap = pw_context_load_module(module->impl->context,
59
+           "libpipewire-module-rtp-sap",
60
+           args, NULL);
61
+   free(args);
62
+
63
+   if (data->sap == NULL)
64
+       return -errno;
65
+
66
+   pw_impl_module_add_listener(data->sap,
67
+           &data->sap_listener,
68
+           &sap_module_events, data);
69
+
70
    return 0;
71
 }
72
 
73
@@ -92,6 +132,11 @@
74
 {
75
    struct module_rtp_send_data *d = module->user_data;
76
 
77
+   if (d->sap) {
78
+       spa_hook_remove(&d->sap_listener);
79
+       pw_impl_module_destroy(d->sap);
80
+       d->sap = NULL;
81
+   }
82
    if (d->mod) {
83
        spa_hook_remove(&d->mod_listener);
84
        pw_impl_module_destroy(d->mod);
85
@@ -100,6 +145,7 @@
86
 
87
    pw_properties_free(d->global_props);
88
    pw_properties_free(d->stream_props);
89
+   pw_properties_free(d->sap_props);
90
 
91
    return 0;
92
 }
93
@@ -127,7 +173,7 @@
94
 {
95
    struct module_rtp_send_data * const d = module->user_data;
96
    struct pw_properties * const props = module->props;
97
-   struct pw_properties *stream_props = NULL, *global_props = NULL;
98
+   struct pw_properties *stream_props = NULL, *global_props = NULL, *sap_props = NULL;
99
    struct spa_audio_info_raw info = { 0 };
100
    const char *str;
101
    int res;
102
@@ -136,7 +182,8 @@
103
 
104
    stream_props = pw_properties_new(NULL, NULL);
105
    global_props = pw_properties_new(NULL, NULL);
106
-   if (!stream_props || !global_props) {
107
+   sap_props = pw_properties_new(NULL, NULL);
108
+   if (!stream_props || !global_props || !sap_props) {
109
        res = -errno;
110
        goto out;
111
    }
112
@@ -165,31 +212,46 @@
113
        }
114
    }
115
 
116
-   if ((str = pw_properties_get(props, "destination_ip")) != NULL)
117
-       pw_properties_set(global_props, "destination.ip", str);
118
-   if ((str = pw_properties_get(props, "source_ip")) != NULL)
119
+   pw_properties_set(global_props, "sess.media", "audio");
120
+   if ((str = pw_properties_get(props, "enable_opus")) != NULL) {
121
+       if (module_args_parse_bool(str))
122
+           pw_properties_set(global_props, "sess.media", "opus");
123
+   }
124
+   if ((str = pw_properties_get(props, "source_ip")) != NULL) {
125
        pw_properties_set(global_props, "source.ip", str);
126
+       pw_properties_set(sap_props, "source.ip", str);
127
+   }
128
+   if ((str = pw_properties_get(props, "destination_ip")) != NULL) {
129
+       pw_properties_set(global_props, "destination.ip", str);
130
+       pw_properties_set(sap_props, "sap.ip", str);
131
+   }
132
    if ((str = pw_properties_get(props, "port")) != NULL)
133
        pw_properties_set(global_props, "destination.port", str);
134
    if ((str = pw_properties_get(props, "mtu")) != NULL)
135
        pw_properties_set(global_props, "net.mtu", str);
136
-   if ((str = pw_properties_get(props, "loop")) != NULL)
137
-       pw_properties_set(global_props, "net.loop",
138
-               module_args_parse_bool(str) ? "true" : "false");
139
-   if ((str = pw_properties_get(props, "ttl")) != NULL)
140
+   if ((str = pw_properties_get(props, "loop")) != NULL) {
141
+       const char *b = module_args_parse_bool(str) ? "true" : "false";
142
+       pw_properties_set(global_props, "net.loop", b);
143
+       pw_properties_set(sap_props, "net.loop", b);
144
+   }
145
+   if ((str = pw_properties_get(props, "ttl")) != NULL) {
146
        pw_properties_set(global_props, "net.ttl", str);
147
+       pw_properties_set(sap_props, "net.ttl", str);
148
+   }
149
    if ((str = pw_properties_get(props, "stream_name")) != NULL)
150
        pw_properties_set(global_props, "sess.name", str);
151
 
152
    d->module = module;
153
    d->stream_props = stream_props;
154
    d->global_props = global_props;
155
+   d->sap_props = sap_props;
156
    d->info = info;
157
 
158
    return 0;
159
 out:
160
    pw_properties_free(stream_props);
161
    pw_properties_free(global_props);
162
+   pw_properties_free(sap_props);
163
 
164
    return res;
165
 }
166
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
9
 
1
@@ -5,6 +5,7 @@
2
 
3
 #include <sys/utsname.h>
4
 #include <arpa/inet.h>
5
+#include <netinet/in.h>
6
 
7
 #include <pipewire/pipewire.h>
8
 
9
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
61
 
1
@@ -1154,30 +1154,25 @@
2
        struct buffer_attr *attr, struct spa_pod_builder *b)
3
 {
4
    const struct spa_pod *param;
5
-   uint32_t blocks, buffers, size, maxsize, stride;
6
+   uint32_t blocks, size, stride;
7
    struct defs *defs = &s->impl->defs;
8
 
9
    blocks = 1;
10
    stride = s->frame_size;
11
 
12
-   maxsize = defs->quantum_limit * 32 * s->frame_size;
13
-   if (s->direction == PW_DIRECTION_OUTPUT) {
14
-       size = attr->minreq;
15
-   } else {
16
-       size = attr->fragsize;
17
-   }
18
-   buffers = SPA_CLAMP(maxsize / size, MIN_BUFFERS, MAX_BUFFERS);
19
+   size = defs->quantum_limit * s->frame_size;
20
 
21
-   pw_log_info("%s stride %d maxsize %d size %u buffers %d", s->client->name,
22
-           stride, maxsize, size, buffers);
23
+   pw_log_info("%s stride %d size %u", s->client->name, stride, size);
24
 
25
    param = spa_pod_builder_add_object(b,
26
            SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
27
-           SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(buffers,
28
+           SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2,
29
                MIN_BUFFERS, MAX_BUFFERS),
30
            SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(blocks),
31
            SPA_PARAM_BUFFERS_size,    SPA_POD_CHOICE_RANGE_Int(
32
-                               size, size, maxsize),
33
+                               size,
34
+                               16 * s->frame_size,
35
+                               INT32_MAX),
36
            SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(stride));
37
    return param;
38
 }
39
@@ -1411,7 +1406,20 @@
40
        if (avail < (int32_t)minreq || stream->corked) {
41
            /* underrun, produce a silence buffer */
42
            size = SPA_MIN(d->maxsize, minreq);
43
-           memset(p, 0, size);
44
+           switch (stream->ss.format) {
45
+           case SPA_AUDIO_FORMAT_U8:
46
+               memset(p, 0x80, size);
47
+               break;
48
+           case SPA_AUDIO_FORMAT_ALAW:
49
+               memset(p, 0x80 ^ 0x55, size);
50
+               break;
51
+           case SPA_AUDIO_FORMAT_ULAW:
52
+               memset(p, 0x00 ^ 0xff, size);
53
+               break;
54
+           default:
55
+               memset(p, 0, size);
56
+               break;
57
+           }
58
 
59
            if (stream->draining && !stream->corked) {
60
                stream->draining = false;
61
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
41
 
1
@@ -49,7 +49,7 @@
2
  *  - `capture.node`: an optional node serial or name to use for capture.
3
  *  - `playback.node`: an optional node serial or name to use for playback.
4
  *  - `server.address = `: an array of server addresses to listen on as
5
- *                            tcp:<ip>:<port>.
6
+ *                            tcp:(<ip>:)<port>.
7
  *
8
  * ## General options
9
  *
10
@@ -119,18 +119,18 @@
11
 
12
 #define MAX_CLIENTS    10
13
 
14
-#define MODULE_USAGE   " capture=<bool>  "                     \
15
-           " playback=<bool>  "                        \
16
-           " remote.name=<remote>  "                   \
17
-           " node.latency=<num/denom, default:"DEFAULT_LATENCY">  "    \
18
-           " node.rate=<1/rate, default:1/"SPA_STRINGIFY(DEFAULT_RATE)">  "    \
19
-           " capture.node=<source-target>  stream.capture.sink=true  " \
20
-           " playback.node=<sink-target>  "                \
21
-           " audio.rate=<sample-rate, default:"SPA_STRINGIFY(DEFAULT_RATE)">  "        \
22
-           " audio.format=<format, default:"DEFAULT_FORMAT">  "        \
23
-           " audio.channels=<channels, default: "SPA_STRINGIFY(DEFAULT_CHANNELS)">  "  \
24
-           " audio.position=<position, default:"DEFAULT_POSITION">  "  \
25
-           " server.address=< tcp:<ip>:<port>,... , default:"DEFAULT_SERVER">" \
26
+#define MODULE_USAGE   "( capture=<bool> ) "                       \
27
+           "( playback=<bool> ) "                      \
28
+           "( remote.name=<remote> ) "                 \
29
+           "( node.latency=<num/denom, default:"DEFAULT_LATENCY"> ) "  \
30
+           "( node.rate=<1/rate, default:1/"SPA_STRINGIFY(DEFAULT_RATE)"> ) "  \
31
+           "( capture.node=<source-target> ( stream.capture.sink=true )) " \
32
+           "( playback.node=<sink-target> ) "              \
33
+           "( audio.rate=<sample-rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) "      \
34
+           "( audio.format=<format, default:"DEFAULT_FORMAT"> ) "      \
35
+           "( audio.channels=<channels, default: "SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) "    \
36
+           "( audio.position=<position, default:"DEFAULT_POSITION"> ) "    \
37
+           "( server.address=< tcp:(<ip>:)<port>(,...) , default:"DEFAULT_SERVER"> )"  \
38
 
39
 static const struct spa_dict_item module_props = {
40
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
41
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
168
 
1
@@ -32,6 +32,7 @@
2
 #include <pipewire/private.h>
3
 
4
 #include <pulse/pulseaudio.h>
5
+#include "module-protocol-pulse/defs.h"
6
 #include "module-protocol-pulse/format.h"
7
 
8
 /** \page page_module_pulse_tunnel PipeWire Module: Pulse Tunnel
9
@@ -105,19 +106,19 @@
10
 #define DEFAULT_CHANNELS 2
11
 #define DEFAULT_POSITION " FL FR "
12
 
13
-#define MODULE_USAGE   " remote.name=<remote>  "               \
14
-           " node.latency=<latency as fraction>  "     \
15
-           " node.name=<name of the nodes>  "          \
16
-           " node.description=<description of the nodes>  "    \
17
-           " node.target=<remote node target name or serial>  "    \
18
-           " audio.format=<sample format>  "           \
19
-           " audio.rate=<sample rate>  "               \
20
-           " audio.channels=<number of channels>  "        \
21
-           " audio.position=<channel map>  "           \
22
+#define MODULE_USAGE   "( remote.name=<remote>  "              \
23
+           "( node.latency=<latency as fraction>  "        \
24
+           "( node.name=<name of the nodes>  "         \
25
+           "( node.description=<description of the nodes>  "   \
26
+           "( node.target=<remote node target name or serial>  "   \
27
+           "( audio.format=<sample format>  "          \
28
+           "( audio.rate=<sample rate>  "              \
29
+           "( audio.channels=<number of channels>  "       \
30
+           "( audio.position=<channel map>  "          \
31
            "pulse.server.address=<address> "           \
32
-           "pulse.latency=<latency in msec> "          \
33
-           " tunnel.mode=source|sink "             \
34
-           " stream.props=<properties>  "
35
+           "( pulse.latency=<latency in msec, default 200> ) " \
36
+           "( tunnel.mode=source|sink, default sink ) "                \
37
+           "( stream.props=<properties> ) "
38
 
39
 
40
 static const struct spa_dict_item module_props = {
41
@@ -134,6 +135,7 @@
42
 
43
 struct impl {
44
    struct pw_context *context;
45
+   struct pw_loop *main_loop;
46
 
47
 #define MODE_SINK  0
48
 #define MODE_SOURCE    1
49
@@ -231,23 +233,22 @@
50
    }
51
 }
52
 
53
-static void update_rate(struct impl *impl, bool playback)
54
+static void update_rate(struct impl *impl, uint32_t filled)
55
 {
56
    float error, corr;
57
+   uint32_t current_latency;
58
 
59
    if (impl->rate_match == NULL)
60
        return;
61
 
62
-   if (playback)
63
-       error = (float)impl->target_latency - (float)impl->current_latency;
64
-   else
65
-       error = (float)impl->current_latency - (float)impl->target_latency;
66
+   current_latency = impl->current_latency + filled;
67
+   error = (float)impl->target_latency - (float)(current_latency);
68
    error = SPA_CLAMP(error, -impl->max_error, impl->max_error);
69
 
70
    corr = spa_dll_update(&impl->dll, error);
71
    pw_log_debug("error:%f corr:%f current:%u target:%u",
72
            error, corr,
73
-           impl->current_latency, impl->target_latency);
74
+           current_latency, impl->target_latency);
75
 
76
    SPA_FLAG_SET(impl->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE);
77
    impl->rate_match->rate = 1.0f / corr;
78
@@ -282,7 +283,7 @@
79
                                         size, RINGBUFFER_SIZE);
80
        impl->resync = true;
81
    } else {
82
-       update_rate(impl, true);
83
+       update_rate(impl, filled / impl->frame_size);
84
    }
85
    spa_ringbuffer_write_data(&impl->ring,
86
                impl->buffer, RINGBUFFER_SIZE,
87
@@ -323,7 +324,7 @@
88
            avail = impl->target_buffer;
89
            index += avail - impl->target_buffer;
90
        } else {
91
-           update_rate(impl, false);
92
+           update_rate(impl, avail / impl->frame_size);
93
        }
94
        spa_ringbuffer_read_data(&impl->ring,
95
                impl->buffer, RINGBUFFER_SIZE,
96
@@ -415,6 +416,20 @@
97
    return 0;
98
 }
99
 
100
+static int
101
+do_schedule_destroy(struct spa_loop *loop,
102
+   bool async, uint32_t seq, const void *data, size_t size, void *user_data)
103
+{
104
+   struct impl *impl = user_data;
105
+   pw_impl_module_schedule_destroy(impl->module);
106
+   return 0;
107
+}
108
+
109
+void module_schedule_destroy(struct impl *impl)
110
+{
111
+   pw_loop_invoke(impl->main_loop, do_schedule_destroy, 1, NULL, 0, false, impl);
112
+}
113
+
114
 static void context_state_cb(pa_context *c, void *userdata)
115
 {
116
    struct impl *impl = userdata;
117
@@ -436,7 +451,7 @@
118
        break;
119
    }
120
    if (do_destroy)
121
-       pw_impl_module_schedule_destroy(impl->module);
122
+       module_schedule_destroy(impl);
123
 }
124
 
125
 static void stream_state_cb(pa_stream *s, void * userdata)
126
@@ -458,7 +473,7 @@
127
        break;
128
    }
129
    if (do_destroy)
130
-       pw_impl_module_schedule_destroy(impl->module);
131
+       module_schedule_destroy(impl);
132
 }
133
 
134
 static void stream_read_request_cb(pa_stream *s, size_t length, void *userdata)
135
@@ -511,7 +526,6 @@
136
 
137
    pa_stream_get_latency(impl->pa_stream, &latency, &negative);
138
    impl->current_latency = latency * impl->info.rate / SPA_USEC_PER_SEC;
139
-   impl->current_latency += filled / impl->frame_size;
140
 
141
    spa_ringbuffer_write_update(&impl->ring, index);
142
 }
143
@@ -536,7 +550,6 @@
144
 
145
    pa_stream_get_latency(impl->pa_stream, &latency, &negative);
146
    impl->current_latency = latency * impl->info.rate / SPA_USEC_PER_SEC;
147
-   impl->current_latency += avail / impl->frame_size;
148
 
149
    while (avail < (int32_t)length) {
150
        uint32_t maxsize = SPA_ROUND_DOWN(sizeof(impl->empty), impl->frame_size);
151
@@ -753,7 +766,7 @@
152
    pa_threaded_mainloop_unlock(impl->pa_mainloop);
153
 error:
154
    pw_log_error("failed to connect: %s", pa_strerror(res));
155
-   return -res;
156
+   return err_to_res(res);
157
 }
158
 
159
 
160
@@ -960,6 +973,7 @@
161
 
162
    impl->module = module;
163
    impl->context = context;
164
+   impl->main_loop = pw_context_get_main_loop(context);
165
 
166
    spa_ringbuffer_init(&impl->ring);
167
    impl->buffer = calloc(1, RINGBUFFER_SIZE);
168
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
201
 
1
@@ -31,19 +31,56 @@
2
  * Automatically creates RAOP (Airplay) sink devices based on zeroconf
3
  * information.
4
  *
5
- * This module will load module-raop-sink for each discovered sink
6
- * with the right parameters.
7
+ * This module will load module-raop-sink for each announced stream that matches
8
+ * the rule with the create-stream action.
9
+ *
10
+ * If no stream.rules are given, it will create a sink for all announced
11
+ * streams.
12
  *
13
  * ## Module Options
14
  *
15
- * This module has no options.
16
+ * Options specific to the behavior of this module
17
+ *
18
+ * - `stream.rules` = <rules>: match rules, use create-stream actions. See
19
+ *   \ref page_module_raop_sink for module properties.
20
  *
21
  * ## Example configuration
22
  *
23
  *\code{.unparsed}
24
  * context.modules = 
25
  * {   name = libpipewire-raop-discover
26
- *     args = { }
27
+ *     args = {
28
+ *         stream.rules = 
29
+ *             {   matches = 
30
+ *                     {    raop.ip = "~.*"
31
+ *                          #raop.ip.version = 4 | 6
32
+ *                          #raop.ip.version = 4
33
+ *                          #raop.port = 1000
34
+ *                          #raop.name = ""
35
+ *                          #raop.hostname = ""
36
+ *                          #raop.domain = ""
37
+ *                          #raop.device = ""
38
+ *                          #raop.transport = "udp" | "tcp"
39
+ *                          #raop.encryption.type = "RSA" | "auth_setup" | "none"
40
+ *                          #raop.audio.codec = "PCM" | "ALAC" | "AAC" | "AAC-ELD"
41
+ *                          #audio.channels = 2
42
+ *                          #audio.format = "S16" | "S24" | "S32"
43
+ *                          #audio.rate = 44100
44
+ *                          #device.model = ""
45
+ *                     }
46
+ *                 
47
+ *                 actions = {
48
+ *                     create-stream = {
49
+ *                         #raop.password = ""
50
+ *                         stream.props = {
51
+ *                             #target.object = ""
52
+ *                             #media.class = "Audio/Sink"
53
+ *                         }
54
+ *                     }
55
+ *                 }
56
+ *             }
57
+ *         
58
+ *     }
59
  * }
60
  * 
61
  *\endcode
62
@@ -58,7 +95,10 @@
63
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
64
 #define PW_LOG_TOPIC_DEFAULT mod_topic
65
 
66
-#define MODULE_USAGE   " "
67
+#define MODULE_USAGE "( stream.rules=<rules>, use create-stream actions )"
68
+
69
+#define DEFAULT_CREATE_RULES   \
70
+        " { matches =  { raop.ip = \"~.*\" }  actions = { create-stream = { } } }  "
71
 
72
 static const struct spa_dict_item module_props = {
73
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
74
@@ -88,6 +128,7 @@
75
    AvahiIfIndex interface;
76
    AvahiProtocol protocol;
77
    const char *name;
78
+   const char *host_name;
79
    const char *type;
80
    const char *domain;
81
 };
82
@@ -114,6 +155,7 @@
83
    t->info.interface = info->interface;
84
    t->info.protocol = info->protocol;
85
    t->info.name = strdup(info->name);
86
+   t->info.host_name = strdup(info->host_name);
87
    t->info.type = strdup(info->type);
88
    t->info.domain = strdup(info->domain);
89
    spa_list_append(&impl->tunnel_list, &t->link);
90
@@ -255,6 +297,7 @@
91
    spa_hook_remove(&t->module_listener);
92
 
93
    free((char *) t->info.name);
94
+   free((char *) t->info.host_name);
95
    free((char *) t->info.type);
96
    free((char *) t->info.domain);
97
 
98
@@ -266,22 +309,87 @@
99
    .destroy = submodule_destroy,
100
 };
101
 
102
+struct match_info {
103
+   struct impl *impl;
104
+   struct pw_properties *props;
105
+   struct tunnel_info *tinfo;
106
+   bool matched;
107
+};
108
+
109
+static int create_stream(struct impl *impl, struct pw_properties *props,
110
+       struct tunnel_info *tinfo)
111
+{
112
+   FILE *f;
113
+   char *args;
114
+   size_t size;
115
+   int res = 0;
116
+   struct pw_impl_module *mod;
117
+   struct tunnel *t;
118
+
119
+   if ((f = open_memstream(&args, &size)) == NULL) {
120
+       res = -errno;
121
+       pw_log_error("Can't open memstream: %m");
122
+       goto done;
123
+   }
124
+
125
+   fprintf(f, "{");
126
+   pw_properties_serialize_dict(f, &props->dict, 0);
127
+   fprintf(f, "}");
128
+        fclose(f);
129
+
130
+   pw_log_info("loading module args:'%s'", args);
131
+   mod = pw_context_load_module(impl->context,
132
+           "libpipewire-module-raop-sink",
133
+           args, NULL);
134
+   free(args);
135
+
136
+   if (mod == NULL) {
137
+       res = -errno;
138
+       pw_log_error("Can't load module: %m");
139
+                goto done;
140
+   }
141
+
142
+   t = make_tunnel(impl, tinfo);
143
+   if (t == NULL) {
144
+       res = -errno;
145
+       pw_log_error("Can't make tunnel: %m");
146
+       pw_impl_module_destroy(mod);
147
+       goto done;
148
+   }
149
+
150
+   pw_impl_module_add_listener(mod, &t->module_listener, &submodule_events, t);
151
+
152
+   t->module = mod;
153
+done:
154
+   return res;
155
+}
156
+
157
+static int rule_matched(void *data, const char *location, const char *action,
158
+                        const char *str, size_t len)
159
+{
160
+   struct match_info *i = data;
161
+   int res = 0;
162
+
163
+   i->matched = true;
164
+   if (spa_streq(action, "create-stream")) {
165
+       pw_properties_update_string(i->props, str, len);
166
+       create_stream(i->impl, i->props, i->tinfo);
167
+   }
168
+   return res;
169
+}
170
+
171
 static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol,
172
    AvahiResolverEvent event, const char *name, const char *type, const char *domain,
173
    const char *host_name, const AvahiAddress *a, uint16_t port, AvahiStringList *txt,
174
    AvahiLookupResultFlags flags, void *userdata)
175
 {
176
    struct impl *impl = userdata;
177
-   struct tunnel *t;
178
    struct tunnel_info tinfo;
179
    const char *str;
180
    AvahiStringList *l;
181
-   FILE *f;
182
-   char *args;
183
-   size_t size;
184
-   struct pw_impl_module *mod;
185
    struct pw_properties *props = NULL;
186
    char atAVAHI_ADDRESS_STR_MAX;
187
+   int ipv;
188
 
189
    if (event != AVAHI_RESOLVER_FOUND) {
190
        pw_log_error("Resolving of '%s' failed: %s", name,
191
@@ -290,6 +398,7 @@
192
    }
193
    tinfo = TUNNEL_INFO(.interface = interface,
194
            .protocol = protocol,
195
+           .host_name = host_name,
196
            .name = name,
197
            .type = type,
198
            .domain = domain);
199
@@ -301,18 +410,14 @@
200
    }
201
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
201
 
1
@@ -18,6 +18,9 @@
2
 #include <netinet/in.h>
3
 
4
 #include <openssl/err.h>
5
+#if OPENSSL_API_LEVEL >= 30000
6
+#include <openssl/core_names.h>
7
+#endif
8
 #include <openssl/rand.h>
9
 #include <openssl/rsa.h>
10
 #include <openssl/engine.h>
11
@@ -53,8 +56,10 @@
12
  *
13
  * Options specific to the behavior of this module
14
  *
15
- * - `raop.hostname`: The hostname of the remote end.
16
+ * - `raop.ip`: The ip address of the remote end.
17
  * - `raop.port`: The port of the remote end.
18
+ * - `raop.name`: The name of the remote end.
19
+ * - `raop.hostname`: The hostname of the remote end.
20
  * - `raop.transport`: The data transport to use, one of "udp" or "tcp". Defaults
21
  *                    to "udp".
22
  * - `raop.encryption.type`: The encryption type to use. One of "none", "RSA" or
23
@@ -84,8 +89,10 @@
24
  * {   name = libpipewire-module-raop-sink
25
  *     args = {
26
  *         # Set the remote address to tunnel to
27
- *         raop.hostname = "my-raop-device"
28
+ *         raop.ip = "127.0.0.1"
29
  *         raop.port = 8190
30
+ *         raop.name = "my-raop-device"
31
+ *         raop.hostname = "My Service"
32
  *         #raop.transport = "udp"
33
  *         raop.encryption.type = "RSA"
34
  *         #raop.audio.codec = "PCM"
35
@@ -138,20 +145,22 @@
36
 
37
 #define DEFAULT_LATENCY 22050
38
 
39
-#define MODULE_USAGE   " raop.hostname=<name of host>  "                   \
40
-           " raop.port=<remote port>  "                        \
41
-           " raop.transport=<transport, default:udp>  "                \
42
-           " raop.encryption.type=<encryption, default:none>  "            \
43
-           " raop.audio.codec=PCM  "                       \
44
-           " raop.password=<password for auth>  "              \
45
-           " node.latency=<latency as fraction>  "             \
46
-           " node.name=<name of the nodes>  "                  \
47
-           " node.description=<description of the nodes>  "            \
48
-           " audio.format=<format, default:"DEFAULT_FORMAT">  "            \
49
-           " audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)">  "           \
50
-           " audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)">  " \
51
-           " audio.position=<channel map, default:"DEFAULT_POSITION">  "       \
52
-           " stream.props=<properties>  "
53
+#define MODULE_USAGE   "( raop.ip=<ip address of host> ) "                 \
54
+           "( raop.port=<remote port> ) "                      \
55
+           "( raop.name=<name of host> ) "                     \
56
+           "( raop.hostname=<hostname of host> ) "                 \
57
+           "( raop.transport=<transport, default:udp> ) "              \
58
+           "( raop.encryption.type=<encryption, default:none> ) "          \
59
+           "( raop.audio.codec=PCM ) "                     \
60
+           "( raop.password=<password for auth> ) "                \
61
+           "( node.latency=<latency as fraction> ) "               \
62
+           "( node.name=<name of the nodes> ) "                    \
63
+           "( node.description=<description of the nodes> ) "          \
64
+           "( audio.format=<format, default:"DEFAULT_FORMAT"> ) "          \
65
+           "( audio.rate=<sample rate, default: "SPA_STRINGIFY(DEFAULT_RATE)"> ) "         \
66
+           "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) "   \
67
+           "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) "     \
68
+           "( stream.props=<properties> ) "
69
 
70
 
71
 static const struct spa_dict_item module_props = {
72
@@ -207,12 +216,15 @@
73
 
74
    char session_id32;
75
    char *password;
76
+   char *auth_method;
77
+   char *realm;
78
+   char *nonce;
79
 
80
    unsigned int do_disconnect:1;
81
 
82
    uint8_t keyAES_CHUNK_SIZE; /* Key for aes-cbc */
83
    uint8_t ivAES_CHUNK_SIZE;  /* Initialization vector for cbc */
84
-   AES_KEY aes;                 /* AES encryption */
85
+   EVP_CIPHER_CTX *ctx;
86
 
87
    uint16_t control_port;
88
    int control_fd;
89
@@ -266,21 +278,10 @@
90
 
91
 static int aes_encrypt(struct impl *impl, uint8_t *data, int len)
92
 {
93
-    uint8_t nvAES_CHUNK_SIZE;
94
-    uint8_t *buffer;
95
-    int i, j;
96
-
97
-    memcpy(nv, impl->iv, AES_CHUNK_SIZE);
98
-    for (i = 0; i + AES_CHUNK_SIZE <= len; i += AES_CHUNK_SIZE) {
99
-        buffer = data + i;
100
-        for (j = 0; j < AES_CHUNK_SIZE; j++)
101
-            bufferj ^= nvj;
102
-
103
-        AES_encrypt(buffer, buffer, &impl->aes);
104
-
105
-        memcpy(nv, buffer, AES_CHUNK_SIZE);
106
-    }
107
-    return i;
108
+   int i = len & ~0xf, clen = i;
109
+   EVP_EncryptInit(impl->ctx, EVP_aes_128_cbc(), impl->key, impl->iv);
110
+   EVP_EncryptUpdate(impl->ctx, data, &clen, data, i);
111
+   return i;
112
 }
113
 
114
 static inline uint64_t timespec_to_ntp(struct timespec *ts)
115
@@ -587,7 +588,7 @@
116
    size_t salen;
117
    int res, af;
118
 
119
-   host = pw_properties_get(impl->props, "raop.hostname");
120
+   host = pw_properties_get(impl->props, "raop.ip");
121
    if (host == NULL)
122
        return -EINVAL;
123
 
124
@@ -703,6 +704,125 @@
125
    }
126
 }
127
 
128
+static void base64_encode(const uint8_t *data, size_t len, char *enc, char pad)
129
+{
130
+   static const char tab =
131
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
132
+   size_t i;
133
+   for (i = 0; i < len; i += 3) {
134
+       uint32_t v;
135
+       v  =              datai+0      << 16;
136
+       v |= (i+1 < len ? datai+1 : 0) << 8;
137
+       v |= (i+2 < len ? datai+2 : 0);
138
+       *enc++ =             tab(v >> (3*6)) & 0x3f;
139
+       *enc++ =             tab(v >> (2*6)) & 0x3f;
140
+       *enc++ = i+1 < len ? tab(v >> (1*6)) & 0x3f : pad;
141
+       *enc++ = i+2 < len ? tab(v >> (0*6)) & 0x3f : pad;
142
+   }
143
+   *enc = '\0';
144
+}
145
+
146
+static size_t base64_decode(const char *data, size_t len, uint8_t *dec)
147
+{
148
+   uint8_t tab = {
149
+       62, -1, -1, -1, 63, 52, 53, 54, 55, 56,
150
+       57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
151
+       -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,
152
+        8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
153
+       18, 19, 20, 21, 22, 23, 24, 25, -1, -1,
154
+       -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
155
+       32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
156
+       42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
157
+   size_t i, j;
158
+   for (i = 0, j = 0; i < len; i += 4) {
159
+       uint32_t v;
160
+       v =                          tabdatai+0-43  << (3*6);
161
+       v |=                         tabdatai+1-43  << (2*6);
162
+       v |= (datai+2 == '=' ? 0 : tabdatai+2-43) << (1*6);
163
+       v |= (datai+3 == '=' ? 0 : tabdatai+3-43);
164
+                             decj++ = (v >> 16) & 0xff;
165
+       if (datai+2 != '=') decj++ = (v >> 8)  & 0xff;
166
+       if (datai+3 != '=') decj++ =  v        & 0xff;
167
+   }
168
+   return j;
169
+}
170
+
171
+SPA_PRINTF_FUNC(2,3)
172
+static int MD5_hash(char hashMD5_HASH_LENGTH+1, const char *fmt, ...)
173
+{
174
+   unsigned char dMD5_DIGEST_LENGTH;
175
+   int i;
176
+   va_list args;
177
+   char buffer1024;
178
+   unsigned int size;
179
+
180
+   va_start(args, fmt);
181
+   vsnprintf(buffer, sizeof(buffer), fmt, args);
182
+   va_end(args);
183
+
184
+   size = MD5_DIGEST_LENGTH;
185
+   EVP_Digest(buffer, strlen(buffer), d, &size, EVP_md5(), NULL);
186
+   for (i = 0; i < MD5_DIGEST_LENGTH; i++)
187
+       sprintf(&hash2*i, "%02x", (uint8_t) di);
188
+   hashMD5_HASH_LENGTH = '\0';
189
+   return 0;
190
+}
191
+
192
+static int rtsp_add_auth(struct impl *impl, const char *method)
193
+{
194
+   char auth1024;
195
+
196
+   if (impl->auth_method == NULL)
197
+       return 0;
198
+
199
+   if (spa_streq(impl->auth_method, "Basic")) {
200
+       char buf256;
201
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
22
 
1
@@ -530,6 +530,8 @@
2
 
3
 int pw_rtsp_client_disconnect(struct pw_rtsp_client *client)
4
 {
5
+   struct message *msg;
6
+
7
    if (client->source == NULL)
8
        return 0;
9
 
10
@@ -539,6 +541,11 @@
11
    client->url = NULL;
12
    free(client->session_id);
13
    client->session_id = NULL;
14
+
15
+   spa_list_consume(msg, &client->messages, link) {
16
+       spa_list_remove(&msg->link);
17
+       free(msg);
18
+   }
19
    pw_rtsp_client_emit_disconnected(client);
20
    return 0;
21
 }
22
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
28
 
1
@@ -54,7 +54,6 @@
2
  * context.modules = 
3
  *  {   name = libpipewire-module-roc-sink
4
  *      args = {
5
- *          local.ip = 0.0.0.0
6
  *          fec.code = disable
7
  *          remote.ip = 192.168.0.244
8
  *          remote.source.port = 10001
9
@@ -350,13 +349,12 @@
10
 static const struct spa_dict_item module_roc_sink_info = {
11
    { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" },
12
    { PW_KEY_MODULE_DESCRIPTION, "roc sink" },
13
-   { PW_KEY_MODULE_USAGE,  "sink.name=<name for the sink> "
14
-               "local.ip=<local sender ip> "
15
-               "fec.code=<empty>|disable|rs8m|ldpc "
16
+   { PW_KEY_MODULE_USAGE,  "( sink.name=<name for the sink> ) "
17
+               "( fec.code=<empty>|disable|rs8m|ldpc ) "
18
                "remote.ip=<remote receiver ip> "
19
-               "remote.source.port=<remote receiver port for source packets> "
20
-               "remote.repair.port=<remote receiver port for repair packets> "
21
-               "sink.props= { key=val ... } " },
22
+               "( remote.source.port=<remote receiver port for source packets> ) "
23
+               "( remote.repair.port=<remote receiver port for repair packets> ) "
24
+               "( sink.props= { key=val ... } ) " },
25
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
26
 };
27
 
28
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
24
 
1
@@ -371,14 +371,14 @@
2
 static const struct spa_dict_item module_roc_source_info = {
3
    { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" },
4
    { PW_KEY_MODULE_DESCRIPTION, "roc source" },
5
-   { PW_KEY_MODULE_USAGE,  "source.name=<name for the source> "
6
-               "resampler.profile=<empty>|disable|high|medium|low "
7
-               "fec.code=<empty>|disable|rs8m|ldpc "
8
-               "sess.latency.msec=<target network latency in milliseconds> "
9
-               "local.ip=<local receiver ip> "
10
-               "local.source.port=<local receiver port for source packets> "
11
-               "local.repair.port=<local receiver port for repair packets> "
12
-               "source.props= { key=value ... }" },
13
+   { PW_KEY_MODULE_USAGE,  "( source.name=<name for the source> ) "
14
+               "( resampler.profile=<empty>|disable|high|medium|low ) "
15
+               "( fec.code=<empty>|disable|rs8m|ldpc ) "
16
+               "( sess.latency.msec=<target network latency in milliseconds> ) "
17
+               "( local.ip=<local receiver ip> ) "
18
+               "( local.source.port=<local receiver port for source packets> ) "
19
+               "( local.repair.port=<local receiver port for repair packets> ) "
20
+               "( source.props= { key=value ... } ) " },
21
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
22
 };
23
 
24
pipewire-0.3.67.tar.gz/src/modules/module-rt.c -> pipewire-0.3.68.tar.gz/src/modules/module-rt.c Changed
32
 
1
@@ -119,10 +119,10 @@
2
 #define DEFAULT_RT_TIME_SOFT   -1
3
 #define DEFAULT_RT_TIME_HARD   -1
4
 
5
-#define MODULE_USAGE   "nice.level=<priority: default "SPA_STRINGIFY(DEFAULT_NICE_LEVEL)"(don't change)> " \
6
-           "rt.prio=<priority: default "SPA_STRINGIFY(DEFAULT_RT_PRIO)"> "     \
7
-           "rt.time.soft=<in usec: default "SPA_STRINGIFY(DEFAULT_RT_TIME_SOFT)" " \
8
-           "rt.time.hard=<in usec: default "SPA_STRINGIFY(DEFAULT_RT_TIME_HARD)" "
9
+#define MODULE_USAGE   "( nice.level=<priority: default "SPA_STRINGIFY(DEFAULT_NICE_LEVEL)"(don't change)> ) " \
10
+           "( rt.prio=<priority: default "SPA_STRINGIFY(DEFAULT_RT_PRIO)"> ) "     \
11
+           "( rt.time.soft=<in usec: default "SPA_STRINGIFY(DEFAULT_RT_TIME_SOFT)" ) " \
12
+           "( rt.time.hard=<in usec: default "SPA_STRINGIFY(DEFAULT_RT_TIME_HARD)" ) "
13
 
14
 static const struct spa_dict_item module_props = {
15
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
16
@@ -574,6 +574,7 @@
17
            return false;
18
        }
19
        if (try == 2) {
20
+#ifdef RLIMIT_RTPRIO
21
            struct rlimit rlim;
22
            /* second try, try to clamp to RLIMIT_RTPRIO */
23
            if (getrlimit(RLIMIT_RTPRIO, &rlim) == 0 && max > (int)rlim.rlim_max) {
24
@@ -581,6 +582,7 @@
25
                max = (int)rlim.rlim_max;
26
            }
27
            else
28
+#endif
29
                break;
30
        }
31
        if (max < DEFAULT_RT_PRIO_MIN) {
32
pipewire-0.3.68.tar.gz/src/modules/module-rtp-sap.c Added
201
 
1
@@ -0,0 +1,1509 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include "config.h"
7
+
8
+#include <limits.h>
9
+#include <unistd.h>
10
+#include <sys/stat.h>
11
+#include <sys/socket.h>
12
+#include <sys/ioctl.h>
13
+#include <arpa/inet.h>
14
+#include <netinet/ip.h>
15
+#include <netinet/in.h>
16
+#include <net/if.h>
17
+#include <ctype.h>
18
+
19
+#include <spa/utils/hook.h>
20
+#include <spa/utils/result.h>
21
+#include <spa/debug/types.h>
22
+
23
+#include <pipewire/pipewire.h>
24
+#include <pipewire/impl.h>
25
+
26
+#include <module-rtp/sap.h>
27
+
28
+#ifdef __FreeBSD__
29
+#define ifr_ifindex ifr_index
30
+#endif
31
+
32
+/** \page page_module_rtp_sap PipeWire Module: Announce and create RTP streams
33
+ *
34
+ * The `rtp-sap` module announces RTP streams that match the rules with the
35
+ * announce-stream action.
36
+ *
37
+ * It will create source RTP streams that are announced with SAP when they
38
+ * match the rule with the create-stream action.
39
+ *
40
+ * If no stream.rules are given, it will announce all streams with
41
+ * sess.sap.announce = true and it will create a receiver for all announced
42
+ * streams.
43
+ *
44
+ * ## Module Options
45
+ *
46
+ * Options specific to the behavior of this module
47
+ *
48
+ * - `local.ifname = <str>`: interface name to use
49
+ * - `sap.ip = <str>`: IP address of the SAP messages, default "224.0.0.56"
50
+ * - `sap.port = <int>`: port of the SAP messages, default 9875
51
+ * - `sap.cleanup.sec = <int>`: cleanup interval in seconds, default 90 seconds
52
+ * - `source.ip =<str>`: source IP address, default "0.0.0.0"
53
+ * - `net.ttl = <int>`: TTL to use, default 1
54
+ * - `net.loop = <bool>`: loopback multicast, default false
55
+ * - `stream.rules` = <rules>: match rules, use create-stream and announce-stream actions
56
+ *
57
+ * ## General options
58
+ *
59
+ * Options with well-known behavior:
60
+ *
61
+ * - \ref PW_KEY_REMOTE_NAME
62
+ *
63
+ * ## Example configuration
64
+ *\code{.unparsed}
65
+ * context.modules = 
66
+ * {   name = libpipewire-module-rtp-sap
67
+ *     args = {
68
+ *         #local.ifname = "eth0"
69
+ *         #sap.ip = "224.0.0.56"
70
+ *         #sap.port = 9875
71
+ *         #sap.cleanup.sec = 5
72
+ *         #source.ip = "0.0.0.0"
73
+ *         #net.ttl = 1
74
+ *         #net.loop = false
75
+ *         stream.rules = 
76
+ *             {   matches = 
77
+ *                     # any of the items in matches needs to match, if one does,
78
+ *                     # actions are emited.
79
+ *                     {   # all keys must match the value. ~ in value starts regex.
80
+ *                         #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0"
81
+ *                         #rtp.payload = "127"
82
+ *                         #rtp.fmt = "L16/48000/2"
83
+ *                         #rtp.session = "PipeWire RTP Stream on fedora"
84
+ *                         #rtp.ts-offset = 0
85
+ *                         #rtp.ts-refclk = "private"
86
+ *                         sess.sap.announce = true
87
+ *                     }
88
+ *                 
89
+ *                 actions = {
90
+ *                     announce-stream = {
91
+ *                     }
92
+ *                 }
93
+ *             }
94
+ *             {   matches = 
95
+ *                     {   # all keys must match the value. ~ in value starts regex.
96
+ *                         #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0"
97
+ *                         #rtp.payload = "127"
98
+ *                         #rtp.fmt = "L16/48000/2"
99
+ *                         #rtp.session = "PipeWire RTP Stream on fedora"
100
+ *                         #rtp.ts-offset = 0
101
+ *                         #rtp.ts-refclk = "private"
102
+ *                         rtp.session = "~.*"
103
+ *                     }
104
+ *                 
105
+ *                 actions = {
106
+ *                     create-stream = {
107
+ *                         #sess.latency.msec = 100
108
+ *                         #sess.ts-direct = false
109
+ *                         #target.object = ""
110
+ *                     }
111
+ *                 }
112
+ *             }
113
+ *         
114
+ *     }
115
+ * }
116
+ * 
117
+ *\endcode
118
+ *
119
+ * \since 0.3.67
120
+ */
121
+
122
+#define NAME "rtp-sap"
123
+
124
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
125
+#define PW_LOG_TOPIC_DEFAULT mod_topic
126
+
127
+#define MAX_SESSIONS       64
128
+
129
+#define DEFAULT_ANNOUNCE_RULES \
130
+   " { matches =  { sess.sap.announce = true }  actions = { announce-stream = { } } } "
131
+#define DEFAULT_CREATE_RULES   \
132
+        " { matches =  { rtp.session = \"~.*\" }  actions = { create-stream = { } } }  "
133
+
134
+#define DEFAULT_CLEANUP_SEC    90
135
+#define SAP_INTERVAL_SEC   5
136
+#define SAP_MIME_TYPE      "application/sdp"
137
+
138
+#define DEFAULT_SAP_IP     "224.0.0.56"
139
+#define DEFAULT_SAP_PORT   9875
140
+
141
+#define DEFAULT_SOURCE_IP  "0.0.0.0"
142
+#define DEFAULT_TTL        1
143
+#define DEFAULT_LOOP       false
144
+
145
+#define USAGE  "( local.ifname=<local interface name to use> ) "                   \
146
+       "( sap.ip=<SAP IP address to send announce, default:"DEFAULT_SAP_IP"> ) "       \
147
+       "( sap.port=<SAP port to send on, default:"SPA_STRINGIFY(DEFAULT_SAP_PORT)"> ) "    \
148
+       "( sap.cleanup.sec=<cleanup interval in seconds, default 90> ) "            \
149
+       "( source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> ) "           \
150
+       "( net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> ) "          \
151
+       "( net.loop=<desired loopback, default:"SPA_STRINGIFY(DEFAULT_LOOP)"> ) "       \
152
+       "( stream.rules=<rules>, use announce-stream and create-stream actions )"
153
+
154
+static const struct spa_dict_item module_info = {
155
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
156
+   { PW_KEY_MODULE_DESCRIPTION, "RTP SAP announce/listen" },
157
+   { PW_KEY_MODULE_USAGE, USAGE },
158
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
159
+};
160
+
161
+struct sdp_info {
162
+   uint16_t hash;
163
+   uint32_t ntp;
164
+
165
+   char *origin;
166
+   char *session_name;
167
+   char *media_type;
168
+   char *mime_type;
169
+   char channelmap512;
170
+
171
+   uint16_t dst_port;
172
+   struct sockaddr_storage dst_addr;
173
+   socklen_t dst_len;
174
+   uint16_t ttl;
175
+
176
+   uint16_t port;
177
+   uint8_t payload;
178
+
179
+   uint32_t rate;
180
+   uint32_t channels;
181
+
182
+   float ptime;
183
+
184
+   uint32_t ts_offset;
185
+   char *ts_refclk;
186
+};
187
+
188
+struct session {
189
+   struct spa_list link;
190
+
191
+   bool announce;
192
+   uint64_t timestamp;
193
+
194
+   struct impl *impl;
195
+   struct node *node;
196
+
197
+   struct sdp_info info;
198
+
199
+   unsigned has_sent_sap:1;
200
+
201
pipewire-0.3.68.tar.gz/src/modules/module-rtp-session.c Added
201
 
1
@@ -0,0 +1,1850 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include "config.h"
7
+
8
+#include <limits.h>
9
+#include <unistd.h>
10
+#include <sys/stat.h>
11
+#include <sys/socket.h>
12
+#include <sys/ioctl.h>
13
+#include <arpa/inet.h>
14
+#include <netinet/ip.h>
15
+#include <netinet/in.h>
16
+#include <net/if.h>
17
+#include <ctype.h>
18
+
19
+#include <spa/utils/hook.h>
20
+#include <spa/utils/result.h>
21
+#include <spa/utils/ringbuffer.h>
22
+#include <spa/utils/json.h>
23
+#include <spa/param/audio/format-utils.h>
24
+#include <spa/debug/types.h>
25
+#include <spa/debug/mem.h>
26
+
27
+#include <pipewire/pipewire.h>
28
+#include <pipewire/impl.h>
29
+
30
+#include <avahi-client/publish.h>
31
+#include <avahi-client/lookup.h>
32
+#include <avahi-common/error.h>
33
+#include <avahi-common/malloc.h>
34
+
35
+#include "module-zeroconf-discover/avahi-poll.h"
36
+
37
+#include <module-rtp/rtp.h>
38
+#include <module-rtp/apple-midi.h>
39
+#include <module-rtp/stream.h>
40
+
41
+#ifdef __FreeBSD__
42
+#define ifr_ifindex ifr_index
43
+#endif
44
+
45
+/** \page page_module_rtp_session PipeWire Module: RTP session
46
+ *
47
+ * The `rtp-session` module creates a media session that is announced
48
+ * with avahi/mDNS/Bonjour.
49
+ *
50
+ * Other machines on the network that run a compatible session will see
51
+ * eachother and will be able to send audio/midi between eachother.
52
+ *
53
+ * The session setup is based on apple-midi and is compatible with
54
+ * apple-midi when the session is using midi.
55
+ *
56
+ * ## Module Options
57
+ *
58
+ * Options specific to the behavior of this module
59
+ *
60
+ * - `local.ifname = <str>`: interface name to use
61
+ * - `control.ip =<str>`: control IP address, default "0.0.0.0"
62
+ * - `control.port =<int>`: control port, default "0"
63
+ * - `net.mtu = <int>`: MTU to use, default 1280
64
+ * - `net.ttl = <int>`: TTL to use, default 1
65
+ * - `net.loop = <bool>`: loopback multicast, default false
66
+ * - `sess.min-ptime = <int>`: minimum packet time in milliseconds, default 2
67
+ * - `sess.max-ptime = <int>`: maximum packet time in milliseconds, default 20
68
+ * - `sess.latency.msec = <int>`: receiver latency in milliseconds, default 100
69
+ * - `sess.name = <str>`: a session name
70
+ * - `sess.ts-offset = <int>`: an offset to apply to the timestamp, default -1 = random offset
71
+ * - `sess.ts-refclk = <string>`: the name of a reference clock
72
+ * - `sess.media = <string>`: the media type audio|midi|opus, default midi
73
+ * - `stream.props = {}`: properties to be passed to the stream
74
+ *
75
+ * ## General options
76
+ *
77
+ * Options with well-known behavior:
78
+ *
79
+ * - \ref PW_KEY_REMOTE_NAME
80
+ * - \ref PW_KEY_AUDIO_FORMAT
81
+ * - \ref PW_KEY_AUDIO_RATE
82
+ * - \ref PW_KEY_AUDIO_CHANNELS
83
+ * - \ref SPA_KEY_AUDIO_POSITION
84
+ * - \ref PW_KEY_NODE_NAME
85
+ * - \ref PW_KEY_NODE_DESCRIPTION
86
+ * - \ref PW_KEY_MEDIA_NAME
87
+ * - \ref PW_KEY_NODE_GROUP
88
+ * - \ref PW_KEY_NODE_LATENCY
89
+ * - \ref PW_KEY_NODE_VIRTUAL
90
+ * - \ref PW_KEY_MEDIA_CLASS
91
+ *
92
+ * ## Example configuration
93
+ *\code{.unparsed}
94
+ * context.modules = 
95
+ * {   name = libpipewire-module-rtp-session
96
+ *     args = {
97
+ *         #local.ifname = "eth0"
98
+ *         #control.ip = "0.0.0.0"
99
+ *         #control.port = 0
100
+ *         #net.mtu = 1280
101
+ *         #net.ttl = 1
102
+ *         #net.loop = false
103
+ *         #sess.min-ptime = 2
104
+ *         #sess.max-ptime = 20
105
+ *         #sess.name = "PipeWire RTP stream"
106
+ *         #sess.media = "audio"
107
+ *         stream.props = {
108
+ *             node.name = "rtp-sink"
109
+ *             #audio.format = "S16BE"
110
+ *             #audio.rate = 48000
111
+ *             #audio.channels = 2
112
+ *             #audio.position =  FL FR 
113
+ *         }
114
+ *     }
115
+ *}
116
+ *
117
+ *\endcode
118
+ *
119
+ * \since 0.3.60
120
+ */
121
+
122
+#define NAME "rtp-session"
123
+
124
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
125
+#define PW_LOG_TOPIC_DEFAULT mod_topic
126
+
127
+#define DEFAULT_CONTROL_IP "0.0.0.0"
128
+#define DEFAULT_CONTROL_PORT   0
129
+#define DEFAULT_TTL        1
130
+#define DEFAULT_LOOP       false
131
+
132
+#define USAGE  "( control.ip=<destination IP address, default:"DEFAULT_CONTROL_IP"> ) "    \
133
+       "( control.port=<int, default:"SPA_STRINGIFY(DEFAULT_CONTROL_PORT)"> ) "    \
134
+       "( local.ifname=<local interface name to use> ) "               \
135
+       "( net.mtu=<desired MTU, default:"SPA_STRINGIFY(DEFAULT_MTU)"> ) "      \
136
+       "( net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> ) "      \
137
+       "( net.loop=<desired loopback, default:"SPA_STRINGIFY(DEFAULT_LOOP)"> ) "   \
138
+       "( sess.name=<a name for the session> ) "                   \
139
+       "( sess.min-ptime=<minimum packet time in milliseconds, default:2> ) "      \
140
+       "( sess.max-ptime=<maximum packet time in milliseconds, default:20> ) "     \
141
+       "( sess.media=<string, the media type audio|midi|opus, default midi> ) "    \
142
+       "( audio.format=<format, default:"DEFAULT_FORMAT"> ) "              \
143
+       "( audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) "      \
144
+       "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) "\
145
+       "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) "         \
146
+       "( stream.props= { key=value ... } ) "
147
+
148
+static const struct spa_dict_item module_info = {
149
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
150
+   { PW_KEY_MODULE_DESCRIPTION, "RTP Sink" },
151
+   { PW_KEY_MODULE_USAGE, USAGE },
152
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
153
+};
154
+
155
+struct service_info {
156
+   AvahiIfIndex interface;
157
+   AvahiProtocol protocol;
158
+   const char *name;
159
+   const char *type;
160
+   const char *domain;
161
+   const char *host_name;
162
+   AvahiAddress address;
163
+   uint16_t port;
164
+};
165
+
166
+#define SERVICE_INFO(...) ((struct service_info){ __VA_ARGS__ })
167
+
168
+struct service {
169
+   struct service_info info;
170
+
171
+   struct spa_list link;
172
+   struct impl *impl;
173
+
174
+   struct session *sess;
175
+};
176
+
177
+struct session {
178
+   struct impl *impl;
179
+   struct spa_list link;
180
+
181
+   struct sockaddr_storage ctrl_addr;
182
+   socklen_t ctrl_len;
183
+   struct sockaddr_storage data_addr;
184
+   socklen_t data_len;
185
+
186
+   struct rtp_stream *send;
187
+   struct spa_hook send_listener;
188
+   struct rtp_stream *recv;
189
+   struct spa_hook recv_listener;
190
+
191
+   char *name;
192
+
193
+   unsigned we_initiated:1;
194
+
195
+#define SESSION_STATE_INIT     0
196
+#define SESSION_STATE_SENDING_CTRL_IN  1
197
+#define SESSION_STATE_SENDING_DATA_IN  2
198
+#define SESSION_STATE_ESTABLISHING 3
199
+#define SESSION_STATE_ESTABLISHED  4
200
+   int state;
201
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
201
 
1
@@ -25,9 +25,12 @@
2
 #include <pipewire/pipewire.h>
3
 #include <pipewire/impl.h>
4
 
5
-#include <module-rtp/sap.h>
6
-#include <module-rtp/rtp.h>
7
+#include <module-rtp/stream.h>
8
 
9
+#ifndef IPTOS_DSCP
10
+#define IPTOS_DSCP_MASK 0xfc
11
+#define IPTOS_DSCP(x) ((x) & IPTOS_DSCP_MASK)
12
+#endif
13
 
14
 /** \page page_module_rtp_sink PipeWire Module: RTP sink
15
  *
16
@@ -38,8 +41,6 @@
17
  *
18
  * Options specific to the behavior of this module
19
  *
20
- * - `sap.ip = <str>`: IP address of the SAP messages, default "224.0.0.56"
21
- * - `sap.port = <int>`: port of the SAP messages, default 9875
22
  * - `source.ip =<str>`: source IP address, default "0.0.0.0"
23
  * - `destination.ip =<str>`: destination IP address, default "224.0.0.56"
24
  * - `destination.port =<int>`: destination port, default random beteen 46000 and 47024
25
@@ -52,7 +53,7 @@
26
  * - `sess.name = <str>`: a session name
27
  * - `sess.ts-offset = <int>`: an offset to apply to the timestamp, default -1 = random offset
28
  * - `sess.ts-refclk = <string>`: the name of a reference clock
29
- * - `sess.media = <string>`: the session media type audio|midi, default audio
30
+ * - `sess.media = <string>`: the media type audio|midi|opus, default audio
31
  * - `stream.props = {}`: properties to be passed to the stream
32
  *
33
  * ## General options
34
@@ -77,19 +78,17 @@
35
  * context.modules = 
36
  * {   name = libpipewire-module-rtp-sink
37
  *     args = {
38
- *         #sap.ip = "224.0.0.56"
39
- *         #sap.port = 9875
40
+ *         #local.ifname = "eth0"
41
  *         #source.ip = "0.0.0.0"
42
  *         #destination.ip = "224.0.0.56"
43
  *         #destination.port = 46000
44
- *         #local.ifname = "eth0"
45
  *         #net.mtu = 1280
46
  *         #net.ttl = 1
47
  *         #net.loop = false
48
  *         #sess.min-ptime = 2
49
  *         #sess.max-ptime = 20
50
  *         #sess.name = "PipeWire RTP stream"
51
- *         #sess.media = audio
52
+ *         #sess.media = "audio"
53
  *         #audio.format = "S16BE"
54
  *         #audio.rate = 48000
55
  *         #audio.channels = 2
56
@@ -110,52 +109,32 @@
57
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
58
 #define PW_LOG_TOPIC_DEFAULT mod_topic
59
 
60
-#define SAP_INTERVAL_SEC   5
61
-#define SAP_MIME_TYPE      "application/sdp"
62
-
63
-#define BUFFER_SIZE        (1u<<20)
64
-#define BUFFER_MASK        (BUFFER_SIZE-1)
65
-
66
-#define DEFAULT_SAP_IP     "224.0.0.56"
67
-#define DEFAULT_SAP_PORT   9875
68
-
69
-#define DEFAULT_SESS_MEDIA "audio"
70
-
71
-#define DEFAULT_FORMAT     "S16BE"
72
-#define DEFAULT_RATE       48000
73
-#define DEFAULT_CHANNELS   2
74
-#define DEFAULT_POSITION   " FL FR "
75
-
76
 #define DEFAULT_PORT       46000
77
 #define DEFAULT_SOURCE_IP  "0.0.0.0"
78
 #define DEFAULT_DESTINATION_IP "224.0.0.56"
79
 #define DEFAULT_TTL        1
80
-#define DEFAULT_MTU        1280
81
 #define DEFAULT_LOOP       false
82
 #define DEFAULT_DSCP       34 /* Default to AES-67 AF41 (34) */
83
 
84
-#define DEFAULT_MIN_PTIME  2
85
-#define DEFAULT_MAX_PTIME  20
86
 #define DEFAULT_TS_OFFSET  -1
87
 
88
-#define USAGE  "sap.ip=<SAP IP address to send announce, default:"DEFAULT_SAP_IP"> "       \
89
-       "sap.port=<SAP port to send on, default:"SPA_STRINGIFY(DEFAULT_SAP_PORT)"> "    \
90
-       "source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> "           \
91
-       "destination.ip=<destination IP address, default:"DEFAULT_DESTINATION_IP"> "    \
92
-       "local.ifname=<local interface name to use> "                   \
93
-       "net.mtu=<desired MTU, default:"SPA_STRINGIFY(DEFAULT_MTU)"> "          \
94
-       "net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> "          \
95
-       "net.loop=<desired loopback, default:"SPA_STRINGIFY(DEFAULT_LOOP)"> "       \
96
-       "net.dscp=<desired DSCP, default:"SPA_STRINGIFY(DEFAULT_DSCP)"> "       \
97
-       "sess.name=<a name for the session> "                       \
98
-       "sess.min-ptime=<minimum packet time in milliseconds, default:2> "      \
99
-       "sess.max-ptime=<maximum packet time in milliseconds, default:20> "     \
100
-       "sess.media=<media type, audio or midi, default:audio> "            \
101
-       "audio.format=<format, default:"DEFAULT_FORMAT"> "              \
102
-       "audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> "      \
103
-       "audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> "\
104
-       "audio.position=<channel map, default:"DEFAULT_POSITION"> "         \
105
-       "stream.props= { key=value ... }"
106
+#define USAGE  "( source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> ) "           \
107
+       "( destination.ip=<destination IP address, default:"DEFAULT_DESTINATION_IP"> ) "    \
108
+       "( destination.port=<int, default random beteen 46000 and 47024> ) "            \
109
+       "( local.ifname=<local interface name to use> ) "                   \
110
+       "( net.mtu=<desired MTU, default:"SPA_STRINGIFY(DEFAULT_MTU)"> ) "          \
111
+       "( net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> ) "          \
112
+       "( net.loop=<desired loopback, default:"SPA_STRINGIFY(DEFAULT_LOOP)"> ) "       \
113
+       "( net.dscp=<desired DSCP, default:"SPA_STRINGIFY(DEFAULT_DSCP)"> ) "           \
114
+       "( sess.name=<a name for the session> ) "                       \
115
+       "( sess.min-ptime=<minimum packet time in milliseconds, default:2> ) "          \
116
+       "( sess.max-ptime=<maximum packet time in milliseconds, default:20> ) "         \
117
+       "( sess.media=<string, the media type audio|midi|opus, default audio> ) "       \
118
+       "( audio.format=<format, default:"DEFAULT_FORMAT"> ) "                  \
119
+       "( audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) "          \
120
+       "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) "   \
121
+       "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) "             \
122
+       "( stream.props= { key=value ... } ) "
123
 
124
 static const struct spa_dict_item module_info = {
125
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
126
@@ -165,10 +144,11 @@
127
 };
128
 
129
 struct impl {
130
+   struct pw_context *context;
131
+
132
    struct pw_impl_module *module;
133
    struct spa_hook module_listener;
134
    struct pw_properties *props;
135
-   struct pw_context *module_context;
136
 
137
    struct pw_loop *loop;
138
 
139
@@ -176,110 +156,47 @@
140
    struct spa_hook core_listener;
141
    struct spa_hook core_proxy_listener;
142
 
143
-   struct spa_source *timer;
144
-
145
    struct pw_properties *stream_props;
146
-   struct pw_stream *stream;
147
-   struct spa_hook stream_listener;
148
-
149
-   struct spa_io_position *io_position;
150
+   struct rtp_stream *stream;
151
 
152
    unsigned int do_disconnect:1;
153
 
154
    char *ifname;
155
    char *session_name;
156
-   uint32_t mtu;
157
    bool ttl;
158
    bool mcast_loop;
159
    uint32_t dscp;
160
-   float min_ptime;
161
-   float max_ptime;
162
-   uint32_t psamples;
163
-   uint32_t min_samples;
164
-   uint32_t max_samples;
165
 
166
    struct sockaddr_storage src_addr;
167
    socklen_t src_len;
168
 
169
-   uint16_t port;
170
+   uint16_t dst_port;
171
    struct sockaddr_storage dst_addr;
172
    socklen_t dst_len;
173
 
174
-   uint16_t sap_port;
175
-   struct sockaddr_storage sap_addr;
176
-   socklen_t sap_len;
177
-
178
-   uint16_t msg_id_hash;
179
-   uint32_t ntp;
180
-
181
-   struct spa_audio_info info;
182
-   const struct format_info *format_info;
183
-   uint32_t rate;
184
-   uint32_t stride;
185
-   int payload;
186
-   uint16_t seq;
187
-   uint32_t ssrc;
188
-   uint32_t ts_offset;
189
-   char ts_refclk64;
190
-
191
-   struct spa_ringbuffer ring;
192
-   uint8_t bufferBUFFER_SIZE;
193
-
194
    int rtp_fd;
195
-   int sap_fd;
196
-
197
-   unsigned sync:1;
198
-   unsigned has_sent_sap:1;
199
 };
200
 
201
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
201
 
1
@@ -19,14 +19,16 @@
2
 #include <spa/utils/result.h>
3
 #include <spa/utils/ringbuffer.h>
4
 #include <spa/utils/dll.h>
5
+#include <spa/utils/json.h>
6
 #include <spa/param/audio/format-utils.h>
7
 #include <spa/control/control.h>
8
+#include <spa/debug/types.h>
9
+#include <spa/debug/mem.h>
10
 
11
 #include <pipewire/pipewire.h>
12
 #include <pipewire/impl.h>
13
 
14
-#include <module-rtp/sap.h>
15
-#include <module-rtp/rtp.h>
16
+#include <module-rtp/stream.h>
17
 
18
 #ifdef __FreeBSD__
19
 #define ifr_ifindex ifr_index
20
@@ -35,63 +37,52 @@
21
 /** \page page_module_rtp_source PipeWire Module: RTP source
22
  *
23
  * The `rtp-source` module creates a PipeWire source that receives audio
24
- * RTP packets.
25
+ * and midi RTP packets.
26
  *
27
  * ## Module Options
28
  *
29
  * Options specific to the behavior of this module
30
  *
31
- * - `sap.ip = <str>`: IP address of the SAP messages, default "224.0.0.56"
32
- * - `sap.port = <str>`: port of the SAP messages, default 9875
33
  * - `local.ifname = <str>`: interface name to use
34
+ * - `node.always-process = <bool>`: true to receive even when not running
35
  * - `sess.latency.msec = <str>`: target network latency in milliseconds, default 100
36
+ * - `sess.media = <string>`: the media type audio|midi|opus, default audio
37
  * - `stream.props = {}`: properties to be passed to the stream
38
  *
39
  * ## General options
40
  *
41
  * Options with well-known behavior:
42
  *
43
- * - \ref PW_KEY_NODE_NAME
44
- * - \ref PW_KEY_NODE_DESCRIPTION
45
+ * - \ref PW_KEY_REMOTE_NAME
46
+ * - \ref PW_KEY_AUDIO_FORMAT
47
+ * - \ref PW_KEY_AUDIO_RATE
48
+ * - \ref PW_KEY_AUDIO_CHANNELS
49
+ * - \ref SPA_KEY_AUDIO_POSITION
50
  * - \ref PW_KEY_MEDIA_NAME
51
  * - \ref PW_KEY_MEDIA_CLASS
52
+ * - \ref PW_KEY_NODE_NAME
53
+ * - \ref PW_KEY_NODE_DESCRIPTION
54
+ * - \ref PW_KEY_NODE_GROUP
55
+ * - \ref PW_KEY_NODE_LATENCY
56
+ * - \ref PW_KEY_NODE_VIRTUAL
57
  *
58
  * ## Example configuration
59
  *\code{.unparsed}
60
  * context.modules = 
61
  * {   name = libpipewire-module-rtp-source
62
  *     args = {
63
- *         #sap.ip = 224.0.0.56
64
- *         #sap.port = 9875
65
  *         #local.ifname = eth0
66
  *         sess.latency.msec = 100
67
- *         #node.always-process = false # true to receive even when not running
68
+ *         #node.always-process = false
69
+ *         #sess.media = "audio"
70
+ *         #audio.format = "S16BE"
71
+ *         #audio.rate = 48000
72
+ *         #audio.channels = 2
73
+ *         #audio.position =  FL FR 
74
  *         stream.props = {
75
  *            #media.class = "Audio/Source"
76
- *            #node.name = "rtp-source"
77
+ *            node.name = "rtp-source"
78
  *         }
79
- *         stream.rules = 
80
- *             {   matches = 
81
- *                     # any of the items in matches needs to match, if one does,
82
- *                     # actions are emited.
83
- *                     {   # all keys must match the value. ~ in value starts regex.
84
- *                         #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0"
85
- *                         #rtp.payload = "127"
86
- *                         #rtp.fmt = "L16/48000/2"
87
- *                         #rtp.session = "PipeWire RTP Stream on fedora"
88
- *                         #rtp.ts-offset = 0
89
- *                         #rtp.ts-refclk = "private"
90
- *                     }
91
- *                 
92
- *                 actions = {
93
- *                     create-stream = {
94
- *                         #sess.latency.msec = 100
95
- *                         #sess.ts-direct = false
96
- *                         #target.object = ""
97
- *                     }
98
- *                 }
99
- *             }
100
- *         
101
  *     }
102
  * }
103
  * 
104
@@ -105,27 +96,21 @@
105
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
106
 #define PW_LOG_TOPIC_DEFAULT mod_topic
107
 
108
-#define SAP_MIME_TYPE          "application/sdp"
109
-
110
-#define ERROR_MSEC         2
111
-#define MAX_SESSIONS           16
112
-
113
-#define DEFAULT_CLEANUP_INTERVAL_SEC   90
114
-#define DEFAULT_SAP_IP         "224.0.0.56"
115
-#define DEFAULT_SAP_PORT       9875
116
-#define DEFAULT_SESS_LATENCY       100
117
+#define DEFAULT_CLEANUP_SEC        60
118
+#define DEFAULT_SOURCE_IP      "224.0.0.56"
119
 
120
-#define BUFFER_SIZE            (1u<<22)
121
-#define BUFFER_MASK            (BUFFER_SIZE-1)
122
-#define BUFFER_SIZE2           (BUFFER_SIZE>>1)
123
-#define BUFFER_MASK2           (BUFFER_SIZE2-1)
124
+#define DEFAULT_TS_OFFSET      -1
125
 
126
-#define USAGE  "sap.ip=<SAP IP address to listen on, default "DEFAULT_SAP_IP"> "               \
127
-       "sap.port=<SAP port to listen on, default "SPA_STRINGIFY(DEFAULT_SAP_PORT)"> "          \
128
-       "local.ifname=<local interface name to use> "                           \
129
-       "sess.latency.msec=<target network latency, default "SPA_STRINGIFY(DEFAULT_SESS_LATENCY)"> "    \
130
-       "stream.props= { key=value ... } "                              \
131
-       "stream.rules=<rules> "
132
+#define USAGE   "( local.ifname=<local interface name to use> ) "                      \
133
+       "( source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> ) "               \
134
+       "source.port=<int, source port> "                               \
135
+       "( sess.latency.msec=<target network latency, default "SPA_STRINGIFY(DEFAULT_SESS_LATENCY)"> ) "\
136
+       "( sess.media=<string, the media type audio|midi|opus, default audio> ) "           \
137
+       "( audio.format=<format, default:"DEFAULT_FORMAT"> ) "                      \
138
+       "( audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) "              \
139
+       "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) "       \
140
+       "( audio.position=<channel map, default:"DEFAULT_POSITION"> ) "                 \
141
+       "( stream.props= { key=value ... } ) "
142
 
143
 static const struct spa_dict_item module_info = {
144
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
145
@@ -138,7 +123,7 @@
146
    struct pw_impl_module *module;
147
    struct spa_hook module_listener;
148
    struct pw_properties *props;
149
-   struct pw_context *module_context;
150
+   struct pw_context *context;
151
 
152
    struct pw_loop *loop;
153
    struct pw_loop *data_loop;
154
@@ -146,577 +131,42 @@
155
    struct pw_core *core;
156
    struct spa_hook core_listener;
157
    struct spa_hook core_proxy_listener;
158
-
159
-   struct spa_source *timer;
160
-   struct spa_source *sap_source;
161
-
162
-   struct pw_properties *stream_props;
163
-
164
    unsigned int do_disconnect:1;
165
 
166
    char *ifname;
167
-   char *sap_ip;
168
    bool always_process;
169
-   int sap_port;
170
-   int sess_latency_msec;
171
    uint32_t cleanup_interval;
172
 
173
-   struct spa_list sessions;
174
-   uint32_t n_sessions;
175
-};
176
-
177
-struct format_info {
178
-   uint32_t media_subtype;
179
-   uint32_t format;
180
-   uint32_t size;
181
-   const char *mime;
182
-   const char *media_type;
183
-};
184
-
185
-static const struct format_info audio_format_info = {
186
-   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_U8, 1, "L8", "audio" },
187
-   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ALAW, 1, "PCMA", "audio" },
188
-   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ULAW, 1, "PCMU", "audio" },
189
-   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S16_BE, 2, "L16", "audio" },
190
-   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S24_BE, 3, "L24", "audio" },
191
-   { SPA_MEDIA_SUBTYPE_control, 0, 1, "rtp-midi", "audio" },
192
-};
193
-
194
-static const struct format_info *find_format_info(const char *mime)
195
-{
196
-   SPA_FOR_EACH_ELEMENT_VAR(audio_format_info, f)
197
-       if (spa_streq(f->mime, mime))
198
-           return f;
199
-   return NULL;
200
-}
201
pipewire-0.3.68.tar.gz/src/modules/module-rtp/apple-midi.h Added
54
 
1
@@ -0,0 +1,52 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef PIPEWIRE_APPLE_MIDI_H
7
+#define PIPEWIRE_APPLE_MIDI_H
8
+
9
+#include <stdint.h>
10
+
11
+#ifdef __cplusplus
12
+extern "C" {
13
+#endif
14
+
15
+struct rtp_apple_midi {
16
+   uint32_t cmd;
17
+   uint32_t protocol;
18
+   uint32_t initiator;
19
+   uint32_t ssrc;
20
+   char name0;
21
+} __attribute__ ((packed));
22
+
23
+struct rtp_apple_midi_ck {
24
+   uint32_t cmd;
25
+   uint32_t ssrc;
26
+   uint8_t count;
27
+   uint8_t padding3;
28
+   uint32_t ts1_h;
29
+   uint32_t ts1_l;
30
+   uint32_t ts2_h;
31
+   uint32_t ts2_l;
32
+   uint32_t ts3_h;
33
+   uint32_t ts3_l;
34
+} __attribute__ ((packed));
35
+
36
+struct rtp_apple_midi_rs {
37
+   uint32_t cmd;
38
+   uint32_t ssrc;
39
+   uint32_t seqnum;
40
+} __attribute__ ((packed));
41
+
42
+#define APPLE_MIDI_CMD_IN  (0xffff0000 | 'I'<<8 | 'N')
43
+#define APPLE_MIDI_CMD_NO  (0xffff0000 | 'N'<<8 | 'O')
44
+#define APPLE_MIDI_CMD_OK  (0xffff0000 | 'O'<<8 | 'K')
45
+#define APPLE_MIDI_CMD_CK  (0xffff0000 | 'C'<<8 | 'K')
46
+#define APPLE_MIDI_CMD_BY  (0xffff0000 | 'B'<<8 | 'Y')
47
+#define APPLE_MIDI_CMD_RS  (0xffff0000 | 'R'<<8 | 'S')
48
+
49
+#ifdef __cplusplus
50
+}
51
+#endif
52
+
53
+#endif /* PIPEWIRE_APPLE_MIDI_H */
54
pipewire-0.3.68.tar.gz/src/modules/module-rtp/audio.c Added
201
 
1
@@ -0,0 +1,313 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+static void rtp_audio_process_playback(void *data)
7
+{
8
+   struct impl *impl = data;
9
+   struct pw_buffer *buf;
10
+   struct spa_data *d;
11
+   uint32_t wanted, timestamp, target_buffer, stride, maxsize;
12
+   int32_t avail;
13
+
14
+   if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
15
+       pw_log_debug("Out of stream buffers: %m");
16
+       return;
17
+   }
18
+   d = buf->buffer->datas;
19
+
20
+   stride = impl->stride;
21
+
22
+   maxsize = d0.maxsize / stride;
23
+   wanted = buf->requested ? SPA_MIN(buf->requested, maxsize) : maxsize;
24
+
25
+   if (impl->io_position && impl->direct_timestamp) {
26
+       /* in direct mode, read directly from the timestamp index,
27
+        * because sender and receiver are in sync, this would keep
28
+        * target_buffer of samples available. */
29
+       spa_ringbuffer_read_update(&impl->ring,
30
+               impl->io_position->clock.position);
31
+   }
32
+   avail = spa_ringbuffer_get_read_index(&impl->ring, &timestamp);
33
+
34
+   target_buffer = impl->target_buffer;
35
+
36
+   if (avail < (int32_t)wanted) {
37
+       enum spa_log_level level;
38
+       memset(d0.data, 0, wanted * stride);
39
+       if (impl->have_sync) {
40
+           impl->have_sync = false;
41
+           level = SPA_LOG_LEVEL_WARN;
42
+       } else {
43
+           level = SPA_LOG_LEVEL_DEBUG;
44
+       }
45
+       pw_log(level, "underrun %d/%u < %u",
46
+                   avail, target_buffer, wanted);
47
+   } else {
48
+       float error, corr;
49
+       if (impl->first) {
50
+           if ((uint32_t)avail > target_buffer) {
51
+               uint32_t skip = avail - target_buffer;
52
+               pw_log_debug("first: avail:%d skip:%u target:%u",
53
+                           avail, skip, target_buffer);
54
+               timestamp += skip;
55
+               avail = target_buffer;
56
+           }
57
+           impl->first = false;
58
+       } else if (avail > (int32_t)SPA_MIN(target_buffer * 8, BUFFER_SIZE / stride)) {
59
+           pw_log_warn("overrun %u > %u", avail, target_buffer * 8);
60
+           timestamp += avail - target_buffer;
61
+           avail = target_buffer;
62
+       }
63
+       if (!impl->direct_timestamp) {
64
+           /* when not using direct timestamp and clocks are not
65
+            * in sync, try to adjust our playback rate to keep the
66
+            * requested target_buffer bytes in the ringbuffer */
67
+           error = (float)target_buffer - (float)avail;
68
+           error = SPA_CLAMP(error, -impl->max_error, impl->max_error);
69
+
70
+           corr = spa_dll_update(&impl->dll, error);
71
+
72
+           pw_log_debug("avail:%u target:%u error:%f corr:%f", avail,
73
+                   target_buffer, error, corr);
74
+
75
+           if (impl->io_rate_match) {
76
+               SPA_FLAG_SET(impl->io_rate_match->flags,
77
+                       SPA_IO_RATE_MATCH_FLAG_ACTIVE);
78
+               impl->io_rate_match->rate = 1.0f / corr;
79
+           }
80
+       }
81
+       spa_ringbuffer_read_data(&impl->ring,
82
+               impl->buffer,
83
+               BUFFER_SIZE,
84
+               (timestamp * stride) & BUFFER_MASK,
85
+               d0.data, wanted * stride);
86
+
87
+       timestamp += wanted;
88
+       spa_ringbuffer_read_update(&impl->ring, timestamp);
89
+   }
90
+   d0.chunk->size = wanted * stride;
91
+   d0.chunk->stride = stride;
92
+   d0.chunk->offset = 0;
93
+   buf->size = wanted;
94
+
95
+   pw_stream_queue_buffer(impl->stream, buf);
96
+}
97
+
98
+static int rtp_audio_receive(struct impl *impl, uint8_t *buffer, ssize_t len)
99
+{
100
+   struct rtp_header *hdr;
101
+   ssize_t hlen, plen;
102
+   uint16_t seq;
103
+   uint32_t timestamp, samples, write, expected_write;
104
+   uint32_t stride = impl->stride;
105
+   int32_t filled;
106
+
107
+   if (len < 12)
108
+       goto short_packet;
109
+
110
+   hdr = (struct rtp_header*)buffer;
111
+   if (hdr->v != 2)
112
+       goto invalid_version;
113
+
114
+   hlen = 12 + hdr->cc * 4;
115
+   if (hlen > len)
116
+       goto invalid_len;
117
+
118
+   if (impl->have_ssrc && impl->ssrc != hdr->ssrc)
119
+       goto unexpected_ssrc;
120
+   impl->ssrc = hdr->ssrc;
121
+   impl->have_ssrc = true;
122
+
123
+   seq = ntohs(hdr->sequence_number);
124
+   if (impl->have_seq && impl->seq != seq) {
125
+       pw_log_info("unexpected seq (%d != %d) SSRC:%u",
126
+               seq, impl->seq, hdr->ssrc);
127
+       impl->have_sync = false;
128
+   }
129
+   impl->seq = seq + 1;
130
+   impl->have_seq = true;
131
+
132
+   timestamp = ntohl(hdr->timestamp) - impl->ts_offset;
133
+
134
+   impl->receiving = true;
135
+
136
+   plen = len - hlen;
137
+   samples = plen / stride;
138
+
139
+   filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_write);
140
+
141
+   /* we always write to timestamp + delay */
142
+   write = timestamp + impl->target_buffer;
143
+
144
+   if (!impl->have_sync) {
145
+       pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u target:%u direct:%u",
146
+               timestamp, seq, impl->ts_offset, impl->ssrc,
147
+               impl->target_buffer, impl->direct_timestamp);
148
+
149
+       /* we read from timestamp, keeping target_buffer of data
150
+        * in the ringbuffer. */
151
+       impl->ring.readindex = timestamp;
152
+       impl->ring.writeindex = write;
153
+       filled = impl->target_buffer;
154
+
155
+       spa_dll_init(&impl->dll);
156
+       spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 128, impl->rate);
157
+       memset(impl->buffer, 0, BUFFER_SIZE);
158
+       impl->have_sync = true;
159
+   } else if (expected_write != write) {
160
+       pw_log_debug("unexpected write (%u != %u)",
161
+               write, expected_write);
162
+   }
163
+
164
+   if (filled + samples > BUFFER_SIZE / stride) {
165
+       pw_log_debug("capture overrun %u + %u > %u", filled, samples,
166
+               BUFFER_SIZE / stride);
167
+       impl->have_sync = false;
168
+   } else {
169
+       pw_log_debug("got samples:%u", samples);
170
+       spa_ringbuffer_write_data(&impl->ring,
171
+               impl->buffer,
172
+               BUFFER_SIZE,
173
+               (write * stride) & BUFFER_MASK,
174
+               &bufferhlen, (samples * stride));
175
+       write += samples;
176
+       spa_ringbuffer_write_update(&impl->ring, write);
177
+   }
178
+   return 0;
179
+
180
+short_packet:
181
+   pw_log_warn("short packet received");
182
+   return -EINVAL;
183
+invalid_version:
184
+   pw_log_warn("invalid RTP version");
185
+   spa_debug_mem(0, buffer, len);
186
+   return -EPROTO;
187
+invalid_len:
188
+   pw_log_warn("invalid RTP length");
189
+   return -EINVAL;
190
+unexpected_ssrc:
191
+   pw_log_warn("unexpected SSRC (expected %u != %u)",
192
+       impl->ssrc, hdr->ssrc);
193
+   return -EINVAL;
194
+}
195
+
196
+static inline void
197
+set_iovec(struct spa_ringbuffer *rbuf, void *buffer, uint32_t size,
198
+       uint32_t offset, struct iovec *iov, uint32_t len)
199
+{
200
+   iov0.iov_len = SPA_MIN(len, size - offset);
201
pipewire-0.3.68.tar.gz/src/modules/module-rtp/midi.c Added
201
 
1
@@ -0,0 +1,498 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+static void rtp_midi_process_playback(void *data)
7
+{
8
+   struct impl *impl = data;
9
+   struct pw_buffer *buf;
10
+   struct spa_data *d;
11
+   uint32_t timestamp, duration, maxsize, read, rate;
12
+   struct spa_pod_builder b;
13
+   struct spa_pod_frame f1;
14
+   void *ptr;
15
+   struct spa_pod *pod;
16
+   struct spa_pod_control *c;
17
+
18
+   if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
19
+       pw_log_debug("Out of stream buffers: %m");
20
+       return;
21
+   }
22
+   d = buf->buffer->datas;
23
+
24
+   maxsize = d0.maxsize;
25
+
26
+   /* we always use the graph position to select events, the receiver side is
27
+    * responsible for smoothing out the RTP timestamps to graph time */
28
+   if (impl->io_position) {
29
+       duration = impl->io_position->clock.duration;
30
+       timestamp = impl->io_position->clock.position;
31
+       rate = impl->io_position->clock.rate.denom;
32
+   } else {
33
+       duration = 8192;
34
+       timestamp = 0;
35
+       rate = impl->rate;
36
+   }
37
+
38
+   /* we copy events into the buffer based on the rtp timestamp + delay. */
39
+   spa_pod_builder_init(&b, d0.data, maxsize);
40
+   spa_pod_builder_push_sequence(&b, &f0, 0);
41
+
42
+   while (true) {
43
+       int32_t avail = spa_ringbuffer_get_read_index(&impl->ring, &read);
44
+       if (avail <= 0)
45
+           break;
46
+
47
+       ptr = SPA_PTROFF(impl->buffer, read & BUFFER_MASK2, void);
48
+
49
+       if ((pod = spa_pod_from_data(ptr, avail, 0, avail)) == NULL)
50
+           goto done;
51
+       if (!spa_pod_is_sequence(pod))
52
+           goto done;
53
+
54
+       /* the ringbuffer contains series of sequences, one for each
55
+        * received packet */
56
+       SPA_POD_SEQUENCE_FOREACH((struct spa_pod_sequence*)pod, c) {
57
+           /* try to render with given delay */
58
+           uint32_t target = c->offset + impl->target_buffer;
59
+           target = (uint64_t)target * rate / impl->rate;
60
+           if (timestamp != 0) {
61
+               /* skip old packets */
62
+               if (target < timestamp)
63
+                   continue;
64
+               /* event for next cycle */
65
+               if (target >= timestamp + duration)
66
+                   goto complete;
67
+           } else {
68
+               timestamp = target;
69
+           }
70
+           spa_pod_builder_control(&b, target - timestamp, SPA_CONTROL_Midi);
71
+           spa_pod_builder_bytes(&b,
72
+                   SPA_POD_BODY(&c->value),
73
+                   SPA_POD_BODY_SIZE(&c->value));
74
+       }
75
+       /* we completed a sequence (one RTP packet), advance ringbuffer
76
+        * and go to the next packet */
77
+       read += SPA_PTRDIFF(c, ptr);
78
+       spa_ringbuffer_read_update(&impl->ring, read);
79
+   }
80
+complete:
81
+   spa_pod_builder_pop(&b, &f0);
82
+
83
+   if (b.state.offset > maxsize) {
84
+       pw_log_warn("overflow buffer %u %u", b.state.offset, maxsize);
85
+       b.state.offset = 0;
86
+   }
87
+   d0.chunk->size = b.state.offset;
88
+   d0.chunk->stride = 1;
89
+   d0.chunk->offset = 0;
90
+done:
91
+   pw_stream_queue_buffer(impl->stream, buf);
92
+}
93
+
94
+static int parse_varlen(uint8_t *p, uint32_t avail, uint32_t *result)
95
+{
96
+   uint32_t value = 0, offs = 0;
97
+   while (offs < avail) {
98
+       uint8_t b = poffs++;
99
+       value = (value << 7) | (b & 0x7f);
100
+       if ((b & 0x80) == 0)
101
+           break;
102
+   }
103
+   *result = value;
104
+   return offs;
105
+}
106
+
107
+static int get_midi_size(uint8_t *p, uint32_t avail)
108
+{
109
+   int size;
110
+   uint32_t offs = 0, value;
111
+
112
+   switch (poffs++) {
113
+   case 0xc0 ... 0xdf:
114
+       size = 2;
115
+       break;
116
+   case 0x80 ... 0xbf:
117
+   case 0xe0 ... 0xef:
118
+       size = 3;
119
+       break;
120
+   case 0xff:
121
+   case 0xf0:
122
+   case 0xf7:
123
+       size = parse_varlen(&poffs, avail - offs, &value);
124
+       size += value + 1;
125
+       break;
126
+   default:
127
+       return -EINVAL;
128
+   }
129
+   return size;
130
+}
131
+static int parse_journal(struct impl *impl, uint8_t *packet, uint16_t seq, uint32_t len)
132
+{
133
+   struct rtp_midi_journal *j = (struct rtp_midi_journal*)packet;
134
+   uint16_t seqnum = ntohs(j->checkpoint_seqnum);
135
+   rtp_stream_emit_send_feedback(impl, seqnum);
136
+   return 0;
137
+}
138
+
139
+static double get_time(struct impl *impl)
140
+{
141
+   struct timespec ts;
142
+   struct spa_io_position *pos;
143
+   double t;
144
+
145
+   clock_gettime(CLOCK_MONOTONIC, &ts);
146
+   if ((pos = impl->io_position) != NULL) {
147
+       t = pos->clock.position / (double) pos->clock.rate.denom;
148
+       t += (SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec) / (double)SPA_NSEC_PER_SEC;
149
+   } else {
150
+       t = SPA_TIMESPEC_TO_NSEC(&ts);
151
+   }
152
+   return t;
153
+}
154
+
155
+static int rtp_midi_receive_midi(struct impl *impl, uint8_t *packet, uint32_t timestamp,
156
+       uint16_t seq, uint32_t payload_offset, uint32_t plen)
157
+{
158
+   uint32_t write;
159
+   struct rtp_midi_header *hdr;
160
+   int32_t filled;
161
+   struct spa_pod_builder b;
162
+   struct spa_pod_frame f1;
163
+   void *ptr;
164
+   uint32_t offs = payload_offset, len, end;
165
+   bool first = true;
166
+
167
+   if (impl->direct_timestamp) {
168
+       /* in direct timestamp we attach the RTP timestamp directly on the
169
+        * midi events and render them in the corresponding cycle */
170
+       if (!impl->have_sync) {
171
+           pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u direct:%d",
172
+               timestamp, seq, impl->ts_offset, impl->ssrc,
173
+               impl->direct_timestamp);
174
+           impl->have_sync = true;
175
+       }
176
+   } else {
177
+       /* in non-direct timestamp mode, we relate the graph clock against
178
+        * the RTP timestamps */
179
+       double ts = timestamp / (float) impl->rate;
180
+       double t = get_time(impl);
181
+       double elapsed, estimated, diff;
182
+
183
+       /* the elapsed time between RTP timestamps */
184
+       elapsed = ts - impl->last_timestamp;
185
+       /* for that elapsed time, our clock should have advanced
186
+        * by this amount since the last estimation */
187
+       estimated = impl->last_time + elapsed * impl->corr;
188
+       /* calculate the diff between estimated and current clock time in
189
+        * samples */
190
+       diff = (estimated - t) * impl->rate;
191
+
192
+       /* no sync or we drifted too far, resync */
193
+       if (!impl->have_sync || fabs(diff) > impl->target_buffer) {
194
+           impl->corr = 1.0;
195
+           spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 256, impl->rate);
196
+
197
+           pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u direct:%d",
198
+               timestamp, seq, impl->ts_offset, impl->ssrc,
199
+               impl->direct_timestamp);
200
+           impl->have_sync = true;
201
pipewire-0.3.68.tar.gz/src/modules/module-rtp/opus.c Added
201
 
1
@@ -0,0 +1,373 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifdef HAVE_OPUS
7
+
8
+#include <opus/opus.h>
9
+#include <opus/opus_multistream.h>
10
+
11
+static void rtp_opus_process_playback(void *data)
12
+{
13
+   struct impl *impl = data;
14
+   struct pw_buffer *buf;
15
+   struct spa_data *d;
16
+   uint32_t wanted, timestamp, target_buffer, stride, maxsize;
17
+   int32_t avail;
18
+
19
+   if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
20
+       pw_log_debug("Out of stream buffers: %m");
21
+       return;
22
+   }
23
+   d = buf->buffer->datas;
24
+
25
+   stride = impl->stride;
26
+
27
+   maxsize = d0.maxsize / stride;
28
+   wanted = buf->requested ? SPA_MIN(buf->requested, maxsize) : maxsize;
29
+
30
+   if (impl->io_position && impl->direct_timestamp) {
31
+       /* in direct mode, read directly from the timestamp index,
32
+        * because sender and receiver are in sync, this would keep
33
+        * target_buffer of samples available. */
34
+       spa_ringbuffer_read_update(&impl->ring,
35
+               impl->io_position->clock.position);
36
+   }
37
+   avail = spa_ringbuffer_get_read_index(&impl->ring, &timestamp);
38
+
39
+   target_buffer = impl->target_buffer;
40
+
41
+   if (avail < (int32_t)wanted) {
42
+       enum spa_log_level level;
43
+       memset(d0.data, 0, wanted * stride);
44
+       if (impl->have_sync) {
45
+           impl->have_sync = false;
46
+           level = SPA_LOG_LEVEL_WARN;
47
+       } else {
48
+           level = SPA_LOG_LEVEL_DEBUG;
49
+       }
50
+       pw_log(level, "underrun %d/%u < %u",
51
+                   avail, target_buffer, wanted);
52
+   } else {
53
+       float error, corr;
54
+       if (impl->first) {
55
+           if ((uint32_t)avail > target_buffer) {
56
+               uint32_t skip = avail - target_buffer;
57
+               pw_log_debug("first: avail:%d skip:%u target:%u",
58
+                           avail, skip, target_buffer);
59
+               timestamp += skip;
60
+               avail = target_buffer;
61
+           }
62
+           impl->first = false;
63
+       } else if (avail > (int32_t)SPA_MIN(target_buffer * 8, BUFFER_SIZE2 / stride)) {
64
+           pw_log_warn("overrun %u > %u", avail, target_buffer * 8);
65
+           timestamp += avail - target_buffer;
66
+           avail = target_buffer;
67
+       }
68
+       if (!impl->direct_timestamp) {
69
+           /* when not using direct timestamp and clocks are not
70
+            * in sync, try to adjust our playback rate to keep the
71
+            * requested target_buffer bytes in the ringbuffer */
72
+           error = (float)target_buffer - (float)avail;
73
+           error = SPA_CLAMP(error, -impl->max_error, impl->max_error);
74
+
75
+           corr = spa_dll_update(&impl->dll, error);
76
+
77
+           pw_log_debug("avail:%u target:%u error:%f corr:%f", avail,
78
+                   target_buffer, error, corr);
79
+
80
+           if (impl->io_rate_match) {
81
+               SPA_FLAG_SET(impl->io_rate_match->flags,
82
+                       SPA_IO_RATE_MATCH_FLAG_ACTIVE);
83
+               impl->io_rate_match->rate = 1.0f / corr;
84
+           }
85
+       }
86
+       spa_ringbuffer_read_data(&impl->ring,
87
+               impl->buffer,
88
+               BUFFER_SIZE2,
89
+               (timestamp * stride) & BUFFER_MASK2,
90
+               d0.data, wanted * stride);
91
+
92
+       timestamp += wanted;
93
+       spa_ringbuffer_read_update(&impl->ring, timestamp);
94
+   }
95
+   d0.chunk->size = wanted * stride;
96
+   d0.chunk->stride = stride;
97
+   d0.chunk->offset = 0;
98
+   buf->size = wanted;
99
+
100
+   pw_stream_queue_buffer(impl->stream, buf);
101
+}
102
+
103
+static int rtp_opus_receive(struct impl *impl, uint8_t *buffer, ssize_t len)
104
+{
105
+   struct rtp_header *hdr;
106
+   ssize_t hlen, plen;
107
+   uint16_t seq;
108
+   uint32_t timestamp, samples, write, expected_write;
109
+   uint32_t stride = impl->stride;
110
+   OpusMSDecoder *dec = impl->stream_data;
111
+   int32_t filled;
112
+   int res;
113
+
114
+   if (len < 12)
115
+       goto short_packet;
116
+
117
+   hdr = (struct rtp_header*)buffer;
118
+   if (hdr->v != 2)
119
+       goto invalid_version;
120
+
121
+   hlen = 12 + hdr->cc * 4;
122
+   if (hlen > len)
123
+       goto invalid_len;
124
+
125
+   if (impl->have_ssrc && impl->ssrc != hdr->ssrc)
126
+       goto unexpected_ssrc;
127
+   impl->ssrc = hdr->ssrc;
128
+   impl->have_ssrc = true;
129
+
130
+   seq = ntohs(hdr->sequence_number);
131
+   if (impl->have_seq && impl->seq != seq) {
132
+       pw_log_info("unexpected seq (%d != %d) SSRC:%u",
133
+               seq, impl->seq, hdr->ssrc);
134
+       impl->have_sync = false;
135
+   }
136
+   impl->seq = seq + 1;
137
+   impl->have_seq = true;
138
+
139
+   timestamp = ntohl(hdr->timestamp) - impl->ts_offset;
140
+
141
+   impl->receiving = true;
142
+
143
+   plen = len - hlen;
144
+
145
+   filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_write);
146
+
147
+   /* we always write to timestamp + delay */
148
+   write = timestamp + impl->target_buffer;
149
+
150
+   if (!impl->have_sync) {
151
+       pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u target:%u direct:%u",
152
+               timestamp, seq, impl->ts_offset, impl->ssrc,
153
+               impl->target_buffer, impl->direct_timestamp);
154
+
155
+       /* we read from timestamp, keeping target_buffer of data
156
+        * in the ringbuffer. */
157
+       impl->ring.readindex = timestamp;
158
+       impl->ring.writeindex = write;
159
+       filled = impl->target_buffer;
160
+
161
+       spa_dll_init(&impl->dll);
162
+       spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 128, impl->rate);
163
+       memset(impl->buffer, 0, BUFFER_SIZE);
164
+       impl->have_sync = true;
165
+   } else if (expected_write != write) {
166
+       pw_log_debug("unexpected write (%u != %u)",
167
+               write, expected_write);
168
+   }
169
+
170
+   if (filled + plen > BUFFER_SIZE2 / stride) {
171
+       pw_log_debug("capture overrun %u + %zd > %u", filled, plen,
172
+               BUFFER_SIZE2 / stride);
173
+       impl->have_sync = false;
174
+   } else {
175
+       uint32_t index = (write * stride) & BUFFER_MASK2, end;
176
+
177
+       res = opus_multistream_decode_float(dec,
178
+               &bufferhlen, plen,
179
+               (float*)&impl->bufferindex, 2880,
180
+               0);
181
+
182
+       end = index + (res * stride);
183
+       /* fold to the lower part of the ringbuffer when overflow */
184
+       if (end > BUFFER_SIZE2)
185
+           memmove(impl->buffer, &impl->bufferBUFFER_SIZE2, end - BUFFER_SIZE2);
186
+
187
+       pw_log_debug("receiving %zd len:%d timestamp:%d %u", plen, res, timestamp, index);
188
+       samples = res;
189
+
190
+       write += samples;
191
+       spa_ringbuffer_write_update(&impl->ring, write);
192
+   }
193
+   return 0;
194
+
195
+short_packet:
196
+   pw_log_warn("short packet received");
197
+   return -EINVAL;
198
+invalid_version:
199
+   pw_log_warn("invalid RTP version");
200
+   spa_debug_mem(0, buffer, len);
201
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
37
 
1
@@ -58,17 +58,34 @@
2
    unsigned z:1;
3
    unsigned j:1;
4
    unsigned b:1;
5
-   uint8_t len_b;
6
 #elif __BYTE_ORDER == __BIG_ENDIAN
7
    unsigned b:1;
8
    unsigned j:1;
9
    unsigned z:1;
10
    unsigned p:1;
11
    unsigned len:4;
12
+#endif
13
    uint8_t len_b;
14
+} __attribute__ ((packed));
15
+
16
+struct rtp_midi_journal {
17
+#if __BYTE_ORDER == __LITTLE_ENDIAN
18
+   unsigned totchan:4;
19
+   unsigned H:1;
20
+   unsigned A:1;
21
+   unsigned Y:1;
22
+   unsigned S:1;
23
+#elif __BYTE_ORDER == __BIG_ENDIAN
24
+   unsigned S:1;
25
+   unsigned Y:1;
26
+   unsigned A:1;
27
+   unsigned H:1;
28
+   unsigned totchan:4;
29
 #endif
30
+   uint16_t checkpoint_seqnum;
31
 } __attribute__ ((packed));
32
 
33
+
34
 #ifdef __cplusplus
35
 }
36
 #endif
37
pipewire-0.3.68.tar.gz/src/modules/module-rtp/stream.c Added
201
 
1
@@ -0,0 +1,521 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <sys/socket.h>
7
+#include <arpa/inet.h>
8
+
9
+#include <spa/utils/result.h>
10
+#include <spa/utils/json.h>
11
+#include <spa/utils/ringbuffer.h>
12
+#include <spa/utils/dll.h>
13
+#include <spa/param/audio/format-utils.h>
14
+#include <spa/control/control.h>
15
+#include <spa/debug/types.h>
16
+#include <spa/debug/mem.h>
17
+
18
+#include "config.h"
19
+
20
+#include <pipewire/pipewire.h>
21
+#include <pipewire/impl.h>
22
+
23
+#include <module-rtp/rtp.h>
24
+#include <module-rtp/stream.h>
25
+#include <module-rtp/apple-midi.h>
26
+
27
+#define BUFFER_SIZE            (1u<<22)
28
+#define BUFFER_MASK            (BUFFER_SIZE-1)
29
+#define BUFFER_SIZE2           (BUFFER_SIZE>>1)
30
+#define BUFFER_MASK2           (BUFFER_SIZE2-1)
31
+
32
+#define rtp_stream_emit(s,m,v,...)     spa_hook_list_call(&s->listener_list, \
33
+                           struct rtp_stream_events, m, v, ##__VA_ARGS__)
34
+#define rtp_stream_emit_destroy(s)     rtp_stream_emit(s, destroy, 0)
35
+#define rtp_stream_emit_state_changed(s,n,e)   rtp_stream_emit(s, state_changed,0,n,e)
36
+#define rtp_stream_emit_send_packet(s,i,l) rtp_stream_emit(s, send_packet,0,i,l)
37
+#define rtp_stream_emit_send_feedback(s,seq)   rtp_stream_emit(s, send_feedback,0,seq)
38
+
39
+struct impl {
40
+   struct spa_audio_info info;
41
+   struct spa_audio_info stream_info;
42
+
43
+   struct pw_stream *stream;
44
+   struct spa_hook stream_listener;
45
+   struct pw_stream_events stream_events;
46
+
47
+   struct spa_hook_list listener_list;
48
+   struct spa_hook listener;
49
+
50
+   const struct format_info *format_info;
51
+
52
+   void *stream_data;
53
+
54
+   uint32_t rate;
55
+   uint32_t stride;
56
+   uint8_t payload;
57
+   uint32_t ssrc;
58
+   uint16_t seq;
59
+   unsigned have_ssrc:1;
60
+   unsigned have_seq:1;
61
+   uint32_t ts_offset;
62
+   uint32_t psamples;
63
+   uint32_t mtu;
64
+
65
+   struct spa_ringbuffer ring;
66
+   uint8_t bufferBUFFER_SIZE;
67
+
68
+   struct spa_io_rate_match *io_rate_match;
69
+   struct spa_io_position *io_position;
70
+   struct spa_dll dll;
71
+   double corr;
72
+   uint32_t target_buffer;
73
+   float max_error;
74
+
75
+   float last_timestamp;
76
+   float last_time;
77
+
78
+   unsigned direct_timestamp:1;
79
+   unsigned always_process:1;
80
+   unsigned started:1;
81
+   unsigned have_sync:1;
82
+   unsigned receiving:1;
83
+   unsigned first:1;
84
+
85
+   int (*receive_rtp)(struct impl *impl, uint8_t *buffer, ssize_t len);
86
+};
87
+
88
+#include "module-rtp/audio.c"
89
+#include "module-rtp/midi.c"
90
+#include "module-rtp/opus.c"
91
+
92
+struct format_info {
93
+   uint32_t media_subtype;
94
+   uint32_t format;
95
+   uint32_t size;
96
+   const char *mime;
97
+   const char *media_type;
98
+};
99
+
100
+static const struct format_info audio_format_info = {
101
+   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_U8, 1, "L8", "audio" },
102
+   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ALAW, 1, "PCMA", "audio" },
103
+   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ULAW, 1, "PCMU", "audio" },
104
+   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S16_BE, 2, "L16", "audio" },
105
+   { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S24_BE, 3, "L24", "audio" },
106
+   { SPA_MEDIA_SUBTYPE_control, 0, 1, "rtp-midi", "audio" },
107
+   { SPA_MEDIA_SUBTYPE_opus, 0, 4, "opus", "audio" },
108
+};
109
+
110
+static void stream_io_changed(void *data, uint32_t id, void *area, uint32_t size)
111
+{
112
+   struct impl *impl = data;
113
+   switch (id) {
114
+   case SPA_IO_RateMatch:
115
+       impl->io_rate_match = area;
116
+       break;
117
+   case SPA_IO_Position:
118
+       impl->io_position = area;
119
+       break;
120
+   }
121
+}
122
+
123
+static void stream_destroy(void *d)
124
+{
125
+   struct impl *impl = d;
126
+   spa_hook_remove(&impl->stream_listener);
127
+   impl->stream = NULL;
128
+}
129
+
130
+static int stream_start(struct impl *impl)
131
+{
132
+   if (impl->started)
133
+       return 0;
134
+
135
+   rtp_stream_emit_state_changed(impl, true, NULL);
136
+
137
+   impl->started = true;
138
+   return 0;
139
+}
140
+
141
+static int stream_stop(struct impl *impl)
142
+{
143
+   if (!impl->started)
144
+       return 0;
145
+
146
+   rtp_stream_emit_state_changed(impl, false, NULL);
147
+
148
+   impl->started = false;
149
+   return 0;
150
+}
151
+
152
+static void on_stream_state_changed(void *d, enum pw_stream_state old,
153
+       enum pw_stream_state state, const char *error)
154
+{
155
+   struct impl *impl = d;
156
+
157
+   switch (state) {
158
+       case PW_STREAM_STATE_UNCONNECTED:
159
+           pw_log_info("stream disconnected");
160
+           break;
161
+       case PW_STREAM_STATE_ERROR:
162
+           pw_log_error("stream error: %s", error);
163
+           rtp_stream_emit_state_changed(impl, false, error);
164
+           break;
165
+       case PW_STREAM_STATE_STREAMING:
166
+           if ((errno = -stream_start(impl)) < 0)
167
+               pw_log_error("failed to start RTP stream: %m");
168
+           break;
169
+       case PW_STREAM_STATE_PAUSED:
170
+           if (!impl->always_process)
171
+               stream_stop(impl);
172
+           impl->have_sync = false;
173
+           break;
174
+       default:
175
+           break;
176
+   }
177
+}
178
+
179
+static const struct pw_stream_events stream_events = {
180
+   PW_VERSION_STREAM_EVENTS,
181
+   .destroy = stream_destroy,
182
+   .state_changed = on_stream_state_changed,
183
+   .io_changed = stream_io_changed,
184
+};
185
+
186
+static const struct format_info *find_audio_format_info(const struct spa_audio_info *info)
187
+{
188
+   SPA_FOR_EACH_ELEMENT_VAR(audio_format_info, f)
189
+       if (f->media_subtype == info->media_subtype &&
190
+           (f->format == 0 || f->format == info->info.raw.format))
191
+           return f;
192
+   return NULL;
193
+}
194
+
195
+static inline uint32_t format_from_name(const char *name, size_t len)
196
+{
197
+   int i;
198
+   for (i = 0; spa_type_audio_formati.name; i++) {
199
+       if (strncmp(name, spa_debug_type_short_name(spa_type_audio_formati.name), len) == 0)
200
+           return spa_type_audio_formati.type;
201
pipewire-0.3.68.tar.gz/src/modules/module-rtp/stream.h Added
56
 
1
@@ -0,0 +1,54 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef PIPEWIRE_RTP_STREAM_H
7
+#define PIPEWIRE_RTP_STREAM_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+struct rtp_stream;
14
+
15
+#define DEFAULT_FORMAT     "S16BE"
16
+#define DEFAULT_RATE       48000
17
+#define DEFAULT_CHANNELS   2
18
+#define DEFAULT_POSITION   " FL FR "
19
+
20
+#define ERROR_MSEC         2
21
+#define DEFAULT_SESS_LATENCY       100
22
+
23
+#define DEFAULT_MTU        1280
24
+#define DEFAULT_MIN_PTIME  2
25
+#define DEFAULT_MAX_PTIME  20
26
+
27
+struct rtp_stream_events {
28
+#define RTP_VERSION_STREAM_EVENTS        0
29
+   uint32_t version;
30
+
31
+   void (*destroy) (void *data);
32
+
33
+   void (*state_changed) (void *data, bool started, const char *error);
34
+
35
+   void (*send_packet) (void *data, struct iovec *iov, size_t iovlen);
36
+
37
+   void (*send_feedback) (void *data, uint32_t senum);
38
+};
39
+
40
+struct rtp_stream *rtp_stream_new(struct pw_core *core,
41
+       enum pw_direction direction, struct pw_properties *props,
42
+       const struct rtp_stream_events *events, void *data);
43
+
44
+void rtp_stream_destroy(struct rtp_stream *s);
45
+
46
+int rtp_stream_receive_packet(struct rtp_stream *s, uint8_t *buffer, size_t len);
47
+
48
+uint64_t rtp_stream_get_time(struct rtp_stream *s, uint64_t *rate);
49
+
50
+
51
+#ifdef __cplusplus
52
+}
53
+#endif
54
+
55
+#endif /* PIPEWIRE_RTP_STREAM_H */
56
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
16
 
1
@@ -230,10 +230,10 @@
2
 static const struct spa_dict_item module_x11_bell_info = {
3
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
4
    { PW_KEY_MODULE_DESCRIPTION, "X11 Bell interceptor" },
5
-   { PW_KEY_MODULE_USAGE,  "sink.name=<name for the sink> "
6
-               "sample.name=<the sample name> "
7
-               "x11.display=<the X11 display> "
8
-               "x11.xauthority=<the X11 XAuthority> " },
9
+   { PW_KEY_MODULE_USAGE,  "( sink.name=<name for the sink> ) "
10
+               "( sample.name=<the sample name> ) "
11
+               "( x11.display=<the X11 display> ) "
12
+               ".x11.xauthority=<the X11 XAuthority> )" },
13
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
14
 };
15
 SPA_EXPORT
16
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
10
 
1
@@ -55,7 +55,7 @@
2
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
3
 #define PW_LOG_TOPIC_DEFAULT mod_topic
4
 
5
-#define MODULE_USAGE   "pulse.latency=<latency in msec> "
6
+#define MODULE_USAGE   "( pulse.latency=<latency in msec, default 200> ) "
7
 
8
 static const struct spa_dict_item module_props = {
9
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
10
pipewire-0.3.67.tar.gz/src/pipewire/buffers.c -> pipewire-0.3.68.tar.gz/src/pipewire/buffers.c Changed
19
 
1
@@ -292,6 +292,9 @@
2
        align = SPA_MAX(align, qalign);
3
        types = qtypes;
4
 
5
+       if (SPA_FLAG_IS_SET(flags, PW_BUFFERS_FLAG_ASYNC))
6
+           max_buffers = SPA_MAX(2u, max_buffers);
7
+
8
        pw_log_debug("%p: %d %d %d %d %d %d -> %d %zd %zd %d %zd %d", result,
9
                qblocks, qminsize, qstride, qmax_buffers, qalign, qtypes,
10
                blocks, minsize, stride, max_buffers, align, types);
11
@@ -300,6 +303,7 @@
12
        minsize = 8192;
13
        max_buffers = 2;
14
    }
15
+
16
    if (SPA_FLAG_IS_SET(flags, PW_BUFFERS_FLAG_SHARED_MEM)) {
17
        if (types != SPA_ID_INVALID)
18
            SPA_FLAG_CLEAR(types, 1<<SPA_DATA_MemPtr);
19
pipewire-0.3.67.tar.gz/src/pipewire/buffers.h -> pipewire-0.3.68.tar.gz/src/pipewire/buffers.h Changed
9
 
1
@@ -29,6 +29,7 @@
2
 #define PW_BUFFERS_FLAG_DYNAMIC        (1<<2)  /**< buffers have dynamic data */
3
 #define PW_BUFFERS_FLAG_SHARED_MEM (1<<3)  /**< buffers need shared memory */
4
 #define PW_BUFFERS_FLAG_IN_PRIORITY    (1<<4)  /**< input parameters have priority */
5
+#define PW_BUFFERS_FLAG_ASYNC      (1<<5)  /**< one of the nodes is async */
6
 
7
 struct pw_buffers {
8
    struct pw_memblock *mem;    /**< allocated buffer memory */
9
pipewire-0.3.67.tar.gz/src/pipewire/context.c -> pipewire-0.3.68.tar.gz/src/pipewire/context.c Changed
201
 
1
@@ -768,13 +768,89 @@
2
 static int ensure_state(struct pw_impl_node *node, bool running)
3
 {
4
    enum pw_node_state state = node->info.state;
5
-   if (node->active && !SPA_FLAG_IS_SET(node->spa_flags, SPA_NODE_FLAG_NEED_CONFIGURE) && running)
6
+   if (node->active && node->runnable &&
7
+       !SPA_FLAG_IS_SET(node->spa_flags, SPA_NODE_FLAG_NEED_CONFIGURE) && running)
8
        state = PW_NODE_STATE_RUNNING;
9
    else if (state > PW_NODE_STATE_IDLE)
10
        state = PW_NODE_STATE_IDLE;
11
    return pw_impl_node_set_state(node, state);
12
 }
13
 
14
+/* From a node (that is runnable) follow all prepared links and groups to
15
+ * active nodes up to the driver and make them recursively runnable as well.
16
+ *
17
+ * We stop at driver nodes so that other paths linked to the driver will stay
18
+ * unrunnable when no other runnable path exists.
19
+ */
20
+static inline int run_nodes(struct pw_context *context, struct pw_impl_node *node, struct spa_list *nodes)
21
+{
22
+   struct pw_impl_node *t;
23
+   struct pw_impl_port *p;
24
+   struct pw_impl_link *l;
25
+
26
+   if (!node->runnable)
27
+       return 0;
28
+
29
+   pw_log_debug("node %p: '%s'", node, node->name);
30
+
31
+   spa_list_for_each(p, &node->input_ports, link) {
32
+       spa_list_for_each(l, &p->links, input_link) {
33
+           t = l->output->node;
34
+
35
+           if (!t->active || !l->prepared || t->runnable)
36
+               continue;
37
+
38
+           pw_log_debug("  peer %p: '%s'", t, t->name);
39
+           t->runnable = true;
40
+           if (!t->driver)
41
+               run_nodes(context, t, nodes);
42
+       }
43
+   }
44
+   spa_list_for_each(p, &node->output_ports, link) {
45
+       spa_list_for_each(l, &p->links, output_link) {
46
+           t = l->input->node;
47
+
48
+           if (!t->active || !l->prepared || t->runnable)
49
+               continue;
50
+
51
+           pw_log_debug("  peer %p: '%s'", t, t->name);
52
+           t->runnable = true;
53
+           if (!t->driver)
54
+               run_nodes(context, t, nodes);
55
+       }
56
+   }
57
+   /* now go through all the nodes that have the same link group and
58
+    * that are not yet visited. Note how nodes with the same group
59
+    * don't get included here. They were added to the same driver but
60
+    * need to otherwise stay idle unless some non-passive link activates
61
+    * them. */
62
+   if (node->link_group != NULL) {
63
+       spa_list_for_each(t, nodes, sort_link) {
64
+           if (t->exported || !t->active || t->runnable)
65
+               continue;
66
+           if (!spa_streq(t->link_group, node->link_group))
67
+               continue;
68
+
69
+           pw_log_debug("  group %p: '%s'", t, t->name);
70
+           t->runnable = true;
71
+           if (!t->driver)
72
+               run_nodes(context, t, nodes);
73
+       }
74
+   }
75
+   return 0;
76
+}
77
+
78
+/* Follow all prepared links and groups from node, activate the links.
79
+ * If a non-passive link is found, we set the peer runnable flag.
80
+ *
81
+ * After this is done, we end up with a list of nodes in collect that are all
82
+ * linked to node.
83
+ * Some of the nodes have the runnable flag set. We then start from those nodes
84
+ * and make all linked nodes and groups runnable as well. (see run_nodes).
85
+ *
86
+ * This ensures that we only activate the paths from the runnable nodes to the
87
+ * driver nodes and leave the other nodes idle.
88
+ */
89
 static int collect_nodes(struct pw_context *context, struct pw_impl_node *node, struct spa_list *collect)
90
 {
91
    struct spa_list queue;
92
@@ -794,7 +870,6 @@
93
    spa_list_consume(n, &queue, sort_link) {
94
        spa_list_remove(&n->sort_link);
95
        spa_list_append(collect, &n->sort_link);
96
-       n->runnable = n->always_process;
97
 
98
        pw_log_debug(" next node %p: '%s' runnable:%u", n, n->name, n->runnable);
99
 
100
@@ -810,16 +885,13 @@
101
 
102
                pw_impl_link_prepare(l);
103
 
104
-               if (!l->prepared)
105
+               if (!l->prepared || t->visited)
106
                    continue;
107
 
108
                if (!l->passive)
109
-                   n->runnable = true;
110
-
111
-               if (!t->visited) {
112
-                   t->visited = true;
113
-                   spa_list_append(&queue, &t->sort_link);
114
-               }
115
+                   t->runnable = true;
116
+               t->visited = true;
117
+               spa_list_append(&queue, &t->sort_link);
118
            }
119
        }
120
        spa_list_for_each(p, &n->output_ports, link) {
121
@@ -831,33 +903,35 @@
122
 
123
                pw_impl_link_prepare(l);
124
 
125
-               if (!l->prepared)
126
+               if (!l->prepared || t->visited)
127
                    continue;
128
 
129
                if (!l->passive)
130
-                   n->runnable = true;
131
-
132
-               if (!t->visited) {
133
-                   t->visited = true;
134
-                   spa_list_append(&queue, &t->sort_link);
135
-               }
136
+                   t->runnable = true;
137
+               t->visited = true;
138
+               spa_list_append(&queue, &t->sort_link);
139
            }
140
        }
141
        /* now go through all the nodes that have the same group and
142
         * that are not yet visited */
143
-       if (n->group != NULL) {
144
+       if (n->group != NULL || n->link_group != NULL) {
145
            spa_list_for_each(t, &context->node_list, link) {
146
                if (t->exported || !t->active || t->visited)
147
                    continue;
148
-               if (!spa_streq(t->group, n->group))
149
+               if ((t->group == NULL || !spa_streq(t->group, n->group)) &&
150
+                   (t->link_group == NULL || !spa_streq(t->link_group, n->link_group)))
151
                    continue;
152
-               pw_log_debug("%p: %s join group %s",
153
-                       t, t->name, t->group);
154
+               pw_log_debug("%p: %s join group:%s link-group:%s",
155
+                       t, t->name, n->group, n->link_group);
156
                t->visited = true;
157
                spa_list_append(&queue, &t->sort_link);
158
            }
159
        }
160
+       pw_log_debug(" next node %p: '%s' runnable:%u", n, n->name, n->runnable);
161
    }
162
+   spa_list_for_each(n, collect, sort_link)
163
+       run_nodes(context, n, collect);
164
+
165
    return 0;
166
 }
167
 
168
@@ -868,8 +942,7 @@
169
    pw_log_debug("driver: %p %s runnable:%u", driver, driver->name, driver->runnable);
170
    spa_list_consume(n, nodes, sort_link) {
171
        spa_list_remove(&n->sort_link);
172
-       if (n->runnable)
173
-           driver->runnable = true;
174
+
175
        pw_log_debug(" follower: %p %s runnable:%u driver-runnable:%u", n, n->name,
176
                n->runnable, driver->runnable);
177
        pw_impl_node_set_driver(n, driver);
178
@@ -1104,6 +1177,12 @@
179
 again:
180
    impl->recalc = true;
181
 
182
+   /* clean up the flags first */
183
+   spa_list_for_each(n, &context->node_list, link) {
184
+       n->visited = false;
185
+       n->runnable = n->always_process;
186
+   }
187
+
188
    get_quantums(context, &def_quantum, &min_quantum, &max_quantum, &lim_quantum, &rate_quantum);
189
    rates = get_rates(context, &def_rate, &n_rates, &global_force_rate);
190
 
191
@@ -1181,6 +1260,7 @@
192
            /* is any active and want a driver */
193
            if (t->want_driver && t->active && t->runnable) {
194
                driver = target;
195
+               driver->runnable = true;
196
                break;
197
            }
198
        }
199
@@ -1188,13 +1268,10 @@
200
            /* driver needed for this group */
201
pipewire-0.3.67.tar.gz/src/pipewire/core.c -> pipewire-0.3.68.tar.gz/src/pipewire/core.c Changed
37
 
1
@@ -69,9 +69,8 @@
2
    struct pw_proxy *proxy;
3
 
4
    pw_log_debug("%p: proxy id %u bound %u", this, id, global_id);
5
-   if ((proxy = pw_map_lookup(&this->objects, id)) != NULL) {
6
+   if ((proxy = pw_map_lookup(&this->objects, id)) != NULL)
7
        pw_proxy_set_bound_id(proxy, global_id);
8
-   }
9
 }
10
 
11
 static void core_event_add_mem(void *data, uint32_t id, uint32_t type, int fd, uint32_t flags)
12
@@ -90,6 +89,16 @@
13
    }
14
 }
15
 
16
+static void core_event_bound_props(void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props)
17
+{
18
+   struct pw_core *this = data;
19
+   struct pw_proxy *proxy;
20
+
21
+   pw_log_debug("%p: proxy id %u bound %u", this, id, global_id);
22
+   if ((proxy = pw_map_lookup(&this->objects, id)) != NULL)
23
+       pw_proxy_emit_bound_props(proxy, global_id, props);
24
+}
25
+
26
 static void core_event_remove_mem(void *data, uint32_t id)
27
 {
28
    struct pw_core *this = data;
29
@@ -106,6 +115,7 @@
30
    .bound_id = core_event_bound_id,
31
    .add_mem = core_event_add_mem,
32
    .remove_mem = core_event_remove_mem,
33
+   .bound_props = core_event_bound_props,
34
 };
35
 
36
 SPA_EXPORT
37
pipewire-0.3.67.tar.gz/src/pipewire/core.h -> pipewire-0.3.68.tar.gz/src/pipewire/core.h Changed
51
 
1
@@ -34,7 +34,7 @@
2
 #define PW_TYPE_INTERFACE_Core     PW_TYPE_INFO_INTERFACE_BASE "Core"
3
 #define PW_TYPE_INTERFACE_Registry PW_TYPE_INFO_INTERFACE_BASE "Registry"
4
 
5
-#define PW_VERSION_CORE        3
6
+#define PW_VERSION_CORE        4
7
 struct pw_core;
8
 #define PW_VERSION_REGISTRY    3
9
 struct pw_registry;
10
@@ -80,21 +80,22 @@
11
 
12
 /** Core */
13
 
14
-#define PW_CORE_EVENT_INFO 0
15
-#define PW_CORE_EVENT_DONE 1
16
-#define PW_CORE_EVENT_PING 2
17
-#define PW_CORE_EVENT_ERROR    3
18
-#define PW_CORE_EVENT_REMOVE_ID    4
19
-#define PW_CORE_EVENT_BOUND_ID 5
20
-#define PW_CORE_EVENT_ADD_MEM  6
21
+#define PW_CORE_EVENT_INFO     0
22
+#define PW_CORE_EVENT_DONE     1
23
+#define PW_CORE_EVENT_PING     2
24
+#define PW_CORE_EVENT_ERROR        3
25
+#define PW_CORE_EVENT_REMOVE_ID        4
26
+#define PW_CORE_EVENT_BOUND_ID     5
27
+#define PW_CORE_EVENT_ADD_MEM      6
28
 #define PW_CORE_EVENT_REMOVE_MEM   7
29
-#define PW_CORE_EVENT_NUM      8
30
+#define PW_CORE_EVENT_BOUND_PROPS  8
31
+#define PW_CORE_EVENT_NUM      9
32
 
33
 /** \struct pw_core_events
34
  *  \brief Core events
35
  */
36
 struct pw_core_events {
37
-#define PW_VERSION_CORE_EVENTS 0
38
+#define PW_VERSION_CORE_EVENTS 1
39
    uint32_t version;
40
 
41
    /**
42
@@ -188,6 +189,8 @@
43
     * \param id the memory id to remove
44
     */
45
    void (*remove_mem) (void *data, uint32_t id);
46
+
47
+   void (*bound_props) (void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props);
48
 };
49
 
50
 #define PW_CORE_METHOD_ADD_LISTENER    0
51
pipewire-0.3.67.tar.gz/src/pipewire/filter.c -> pipewire-0.3.68.tar.gz/src/pipewire/filter.c Changed
201
 
1
@@ -144,7 +144,6 @@
2
 
3
    unsigned int disconnecting:1;
4
    unsigned int disconnect_core:1;
5
-   unsigned int subscribe:1;
6
    unsigned int draining:1;
7
    unsigned int allow_mlock:1;
8
    unsigned int warn_mlock:1;
9
@@ -1133,10 +1132,12 @@
10
            PW_FILTER_STATE_ERROR, message);
11
 }
12
 
13
-static void proxy_bound(void *_data, uint32_t global_id)
14
+static void proxy_bound_props(void *_data, uint32_t global_id, const struct spa_dict *props)
15
 {
16
    struct pw_filter *filter = _data;
17
    filter->node_id = global_id;
18
+   if (props)
19
+       pw_properties_update(filter->properties, props);
20
    filter_set_state(filter, PW_FILTER_STATE_PAUSED, NULL);
21
 }
22
 
23
@@ -1145,7 +1146,7 @@
24
    .removed = proxy_removed,
25
    .destroy = proxy_destroy,
26
    .error = proxy_error,
27
-   .bound = proxy_bound,
28
+   .bound_props = proxy_bound_props,
29
 };
30
 
31
 static void on_core_error(void *_data, uint32_t id, int seq, int res, const char *message)
32
@@ -1191,6 +1192,8 @@
33
    struct match match;
34
    int res;
35
 
36
+   ensure_loop(context->main_loop, return NULL);
37
+
38
    impl = calloc(1, sizeof(struct filter));
39
    if (impl == NULL) {
40
        res = -errno;
41
@@ -1355,21 +1358,58 @@
42
    return "invalid-state";
43
 }
44
 
45
+static int filter_disconnect(struct filter *impl)
46
+{
47
+   struct pw_filter *filter = &impl->this;
48
+   pw_log_debug("%p: disconnect", impl);
49
+
50
+   if (impl->disconnecting)
51
+       return -EBUSY;
52
+
53
+   impl->disconnecting = true;
54
+
55
+   if (filter->proxy) {
56
+       pw_proxy_destroy(filter->proxy);
57
+       filter->proxy = NULL;
58
+   }
59
+   if (impl->disconnect_core) {
60
+       impl->disconnect_core = false;
61
+       spa_hook_remove(&filter->core_listener);
62
+       spa_list_remove(&filter->link);
63
+       pw_core_disconnect(filter->core);
64
+       filter->core = NULL;
65
+   }
66
+   return 0;
67
+}
68
+
69
+static void free_port(struct filter *impl, struct port *port)
70
+{
71
+   spa_list_remove(&port->link);
72
+   spa_node_emit_port_info(&impl->hooks, port->direction, port->id, NULL);
73
+   pw_map_remove(&impl->portsport->direction, port->id);
74
+   clear_buffers(port);
75
+   clear_params(impl, port, SPA_ID_INVALID);
76
+   pw_properties_free(port->props);
77
+   free(port);
78
+}
79
+
80
 SPA_EXPORT
81
 void pw_filter_destroy(struct pw_filter *filter)
82
 {
83
    struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this);
84
    struct port *p;
85
 
86
+   ensure_loop(impl->context->main_loop, return);
87
+
88
    pw_log_debug("%p: destroy", filter);
89
 
90
    pw_filter_emit_destroy(filter);
91
 
92
    if (!impl->disconnecting)
93
-       pw_filter_disconnect(filter);
94
+       filter_disconnect(impl);
95
 
96
    spa_list_consume(p, &impl->port_list, link)
97
-       pw_filter_remove_port(p->user_data);
98
+       free_port(impl, p);
99
 
100
    if (filter->core) {
101
        spa_hook_remove(&filter->core_listener);
102
@@ -1412,6 +1452,9 @@
103
                void *data)
104
 {
105
    struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this);
106
+
107
+   ensure_loop(impl->context->main_loop);
108
+
109
    spa_hook_list_append(&filter->listener_list, listener, events, data);
110
    if (events->process && impl->rt_callbacks.funcs == NULL) {
111
        impl->rt_callbacks = SPA_CALLBACKS_INIT(events, data);
112
@@ -1460,6 +1503,8 @@
113
    struct port *port = SPA_CONTAINER_OF(port_data, struct port, user_data);
114
    int changed = 0;
115
 
116
+   ensure_loop(impl->context->main_loop, return -EIO);
117
+
118
    if (port_data) {
119
        changed = pw_properties_update(port->props, dict);
120
        port->info.props = &port->props->dict;
121
@@ -1497,6 +1542,11 @@
122
    uint32_t i;
123
    struct spa_dict_item items1;
124
 
125
+   ensure_loop(impl->context->main_loop, return -EIO);
126
+
127
+   if (filter->proxy != NULL || filter->state != PW_FILTER_STATE_UNCONNECTED)
128
+       return -EBUSY;
129
+
130
    pw_log_debug("%p: connect", filter);
131
    impl->flags = flags;
132
 
133
@@ -1532,6 +1582,8 @@
134
    }
135
 
136
    impl->disconnecting = false;
137
+   impl->draining = false;
138
+   impl->driving = false;
139
    filter_set_state(filter, PW_FILTER_STATE_CONNECTING, NULL);
140
 
141
    if (flags & PW_FILTER_FLAG_DRIVER)
142
@@ -1585,22 +1637,8 @@
143
 int pw_filter_disconnect(struct pw_filter *filter)
144
 {
145
    struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this);
146
-
147
-   pw_log_debug("%p: disconnect", filter);
148
-   impl->disconnecting = true;
149
-
150
-   if (filter->proxy) {
151
-       pw_proxy_destroy(filter->proxy);
152
-       filter->proxy = NULL;
153
-   }
154
-   if (impl->disconnect_core) {
155
-       impl->disconnect_core = false;
156
-       spa_hook_remove(&filter->core_listener);
157
-       spa_list_remove(&filter->link);
158
-       pw_core_disconnect(filter->core);
159
-       filter->core = NULL;
160
-   }
161
-   return 0;
162
+   ensure_loop(impl->context->main_loop, return -EIO);
163
+   return filter_disconnect(impl);
164
 }
165
 
166
 static void add_port_params(struct filter *impl, struct port *port)
167
@@ -1682,6 +1720,8 @@
168
    struct port *p;
169
    const char *str;
170
 
171
+   ensure_loop(impl->context->main_loop, return NULL);
172
+
173
    if (props == NULL)
174
        props = pw_properties_new(NULL, NULL);
175
    if (props == NULL)
176
@@ -1738,22 +1778,14 @@
177
    return NULL;
178
 }
179
 
180
-static inline void free_port(struct filter *impl, struct port *port)
181
-{
182
-   spa_list_remove(&port->link);
183
-   spa_node_emit_port_info(&impl->hooks, port->direction, port->id, NULL);
184
-   pw_map_remove(&impl->portsport->direction, port->id);
185
-   clear_buffers(port);
186
-   clear_params(impl, port, SPA_ID_INVALID);
187
-   pw_properties_free(port->props);
188
-   free(port);
189
-}
190
-
191
 SPA_EXPORT
192
 int pw_filter_remove_port(void *port_data)
193
 {
194
    struct port *port = SPA_CONTAINER_OF(port_data, struct port, user_data);
195
    struct filter *impl = port->filter;
196
+
197
+   ensure_loop(impl->context->main_loop, return -EIO);
198
+
199
    free_port(impl, port);
200
    return 0;
201
pipewire-0.3.67.tar.gz/src/pipewire/impl-core.c -> pipewire-0.3.68.tar.gz/src/pipewire/impl-core.c Changed
20
 
1
@@ -5,9 +5,6 @@
2
 #include "config.h"
3
 
4
 #include <unistd.h>
5
-#ifndef ENODATA
6
-#define ENODATA 9919
7
-#endif
8
 
9
 #include <spa/debug/types.h>
10
 #include <spa/utils/string.h>
11
@@ -175,6 +172,8 @@
12
    pw_log_debug("%p: hello %d from resource %p", context, version, resource);
13
    pw_map_for_each(&client->objects, destroy_resource, client);
14
 
15
+   resource->version = version;
16
+
17
    pw_mempool_clear(client->pool);
18
 
19
    this->info.change_mask = PW_CORE_CHANGE_MASK_ALL;
20
pipewire-0.3.67.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.68.tar.gz/src/pipewire/impl-link.c Changed
26
 
1
@@ -1203,7 +1203,10 @@
2
 
3
    /* passive means that this link does not make the nodes active */
4
    str = pw_properties_get(properties, PW_KEY_LINK_PASSIVE);
5
-   this->passive = str ? spa_atob(str) : output->passive | input->passive;
6
+   this->passive = str ? spa_atob(str) :
7
+       (output->passive && input_node->can_suspend) ||
8
+       (input->passive && output_node->can_suspend) ||
9
+       (input->passive && output->passive);
10
    if (this->passive && str == NULL)
11
         pw_properties_set(properties, PW_KEY_LINK_PASSIVE, "true");
12
 
13
@@ -1256,10 +1259,9 @@
14
             output_node, output->port_id, this->rt.out_mix.port.port_id,
15
             input_node, input->port_id, this->rt.in_mix.port.port_id);
16
 
17
-   if (asprintf(&this->name, "%d.%d -> %d.%d",
18
+   this->name = spa_aprintf("%d.%d -> %d.%d",
19
            output_node->info.id, output->port_id,
20
-           input_node->info.id, input->port_id) < 0)
21
-       this->name = NULL;
22
+           input_node->info.id, input->port_id);
23
    pw_log_info("(%s) (%s) -> (%s)", this->name, output_node->name, input_node->name);
24
 
25
    pw_impl_port_emit_link_added(output, this);
26
pipewire-0.3.67.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.68.tar.gz/src/pipewire/impl-node.c Changed
144
 
1
@@ -688,8 +688,9 @@
2
        pw_log_debug("%p: set position %p", node, &node->rt.activation->position);
3
        node->rt.position = &node->rt.activation->position;
4
 
5
-       node->current_rate = node->rt.position->clock.rate;
6
-       node->current_quantum = node->rt.position->clock.duration;
7
+       node->target_rate = node->rt.position->clock.target_rate;
8
+       node->target_quantum = node->rt.position->clock.target_duration;
9
+       node->target_pending = false;
10
    } else if (node->driver) {
11
        pw_log_warn("%p: can't set position on driver", node);
12
    }
13
@@ -804,8 +805,8 @@
14
    pw_log_trace("%p: set position %p", node, &driver->rt.activation->position);
15
    node->rt.position = &driver->rt.activation->position;
16
 
17
-   node->current_rate = node->rt.position->clock.rate;
18
-   node->current_quantum = node->rt.position->clock.duration;
19
+   node->target_rate = node->rt.position->clock.target_rate;
20
+   node->target_quantum = node->rt.position->clock.target_duration;
21
 
22
    if (node->source.loop != NULL) {
23
        remove_node(node);
24
@@ -841,14 +842,16 @@
25
    remove_segment_owner(old, node->info.id);
26
 
27
    if (old != node && old->driving && driver->info.state < PW_NODE_STATE_RUNNING) {
28
-       driver->current_rate = old->current_rate;
29
-       driver->current_quantum = old->current_quantum;
30
-       driver->current_pending = true;
31
-       pw_log_info("move quantum:%"PRIu64" rate:%d (%s-%d -> %s-%d)",
32
-               driver->current_quantum,
33
-               driver->current_rate.denom,
34
+       pw_log_info("move quantum:%"PRIu64"->%"PRIu64" rate:%d->%d (%s-%d -> %s-%d)",
35
+               driver->target_quantum,
36
+               old->target_quantum,
37
+               driver->target_rate.denom,
38
+               old->target_rate.denom,
39
                old->name, old->info.id,
40
                driver->name, driver->info.id);
41
+       driver->target_rate = old->target_rate;
42
+       driver->target_quantum = old->target_quantum;
43
+       driver->target_pending = true;
44
    }
45
    was_driving = node->driving;
46
    node->driving = node->driver && driver == node;
47
@@ -892,7 +895,7 @@
48
    const char *str, *recalc_reason = NULL;
49
    struct spa_fraction frac;
50
    uint32_t value;
51
-   bool driver;
52
+   bool driver, trigger;
53
 
54
    if ((str = pw_properties_get(node->properties, PW_KEY_PRIORITY_DRIVER))) {
55
        node->priority_driver = pw_properties_parse_int(str);
56
@@ -925,8 +928,14 @@
57
    }
58
 
59
    /* not scheduled automatically so we add an additional required trigger */
60
-   if (pw_properties_get_bool(node->properties, PW_KEY_NODE_TRIGGER, false))
61
-       node->rt.activation->state0.required++;
62
+   trigger = pw_properties_get_bool(node->properties, PW_KEY_NODE_TRIGGER, false);
63
+   if (trigger != node->trigger) {
64
+       node->trigger = trigger;
65
+       if (trigger)
66
+           node->rt.activation->state0.required++;
67
+       else
68
+           node->rt.activation->state0.required--;
69
+   }
70
 
71
    /* group defines what nodes are scheduled together */
72
    str = pw_properties_get(node->properties, PW_KEY_NODE_GROUP);
73
@@ -947,6 +956,12 @@
74
        recalc_reason = "link group changed";
75
    }
76
 
77
+   if ((str = pw_properties_get(node->properties, PW_KEY_MEDIA_CLASS)) != NULL &&
78
+       (strstr(str, "/Sink") != NULL || strstr(str, "/Source") != NULL)) {
79
+       node->can_suspend = true;
80
+   } else {
81
+       node->can_suspend = false;
82
+   }
83
    if ((str = pw_properties_get(node->properties, PW_KEY_NODE_PASSIVE)) == NULL)
84
        str = "false";
85
    if (spa_streq(str, "out"))
86
@@ -1009,11 +1024,16 @@
87
    node->lock_rate = pw_properties_get_bool(node->properties, PW_KEY_NODE_LOCK_RATE, false);
88
 
89
    if ((str = pw_properties_get(node->properties, PW_KEY_NODE_FORCE_RATE))) {
90
-       if (spa_atou32(str, &value, 0) &&
91
-           node->force_rate != value) {
92
-           node->force_rate = value;
93
-           node->stamp = ++context->stamp;
94
-           recalc_reason = "force rate changed";
95
+       if (spa_atou32(str, &value, 0)) {
96
+           if (value == 0)
97
+               value = node->rate.denom;
98
+           if (node->force_rate != value) {
99
+               pw_log_info("(%s-%u) force-rate:%u -> %u", node->name,
100
+                           node->info.id, node->force_rate, value);
101
+               node->force_rate = value;
102
+               node->stamp = ++context->stamp;
103
+               recalc_reason = "force rate changed";
104
+           }
105
        }
106
    }
107
 
108
@@ -1218,11 +1238,11 @@
109
    uint32_t quantum = s->clock_force_quantum == 0 ? s->clock_quantum : s->clock_force_quantum;
110
    uint32_t rate = s->clock_force_rate == 0 ? s->clock_rate : s->clock_force_rate;
111
 
112
-   this->current_rate = SPA_FRACTION(1, rate);
113
-   this->current_quantum = quantum;
114
+   this->target_rate = SPA_FRACTION(1, rate);
115
+   this->target_quantum = quantum;
116
 
117
-   pos->clock.rate = this->current_rate;
118
-   pos->clock.duration = this->current_quantum;
119
+   pos->clock.rate = pos->clock.target_rate = this->target_rate;
120
+   pos->clock.duration = pos->clock.target_duration = this->target_quantum;
121
    pos->video.flags = SPA_IO_VIDEO_SIZE_VALID;
122
    pos->video.size = s->video_size;
123
    pos->video.stride = pos->video.size.width * 16;
124
@@ -1681,10 +1701,15 @@
125
            node->rt.target.signal_func(node->rt.target.data);
126
        }
127
 
128
-       if (node->current_pending) {
129
-           node->rt.position->clock.duration = node->current_quantum;
130
-           node->rt.position->clock.rate = node->current_rate;
131
-           node->current_pending = false;
132
+       /* This update is done too late, the driver should do this
133
+        * before calling the ready callback so that it can use the new target
134
+        * duration and rate to schedule the next update. We do this here to
135
+        * help drivers that don't support this yet */
136
+       if (node->rt.position->clock.duration != node->rt.position->clock.target_duration ||
137
+           node->rt.position->clock.rate.denom != node->rt.position->clock.target_rate.denom) {
138
+           pw_log_warn("driver %s did not update duration/rate", node->name);
139
+           node->rt.position->clock.duration = node->rt.position->clock.target_duration;
140
+           node->rt.position->clock.rate = node->rt.position->clock.target_rate;
141
        }
142
 
143
        sync_type = check_updates(node, &reposition_owner);
144
pipewire-0.3.67.tar.gz/src/pipewire/impl-port.c -> pipewire-0.3.68.tar.gz/src/pipewire/impl-port.c Changed
16
 
1
@@ -1574,9 +1574,12 @@
2
 
3
        /* try dynamic data */
4
        alloc_flags = PW_BUFFERS_FLAG_DYNAMIC;
5
+       if (SPA_FLAG_IS_SET(node->spa_flags, SPA_NODE_FLAG_ASYNC))
6
+           alloc_flags |= PW_BUFFERS_FLAG_ASYNC;
7
 
8
-       pw_log_debug("%p: %d.%d negotiate %d buffers on node: %p",
9
-               port, port->direction, port->port_id, n_buffers, node->node);
10
+       pw_log_debug("%p: %d.%d negotiate %d buffers on node: %p flags:%08x",
11
+               port, port->direction, port->port_id, n_buffers, node->node,
12
+               alloc_flags);
13
 
14
        if (port->added) {
15
            pw_loop_invoke(node->data_loop, do_remove_port, SPA_ID_INVALID, NULL, 0, true, port);
16
pipewire-0.3.67.tar.gz/src/pipewire/keys.h -> pipewire-0.3.68.tar.gz/src/pipewire/keys.h Changed
11
 
1
@@ -149,7 +149,8 @@
2
 #define PW_KEY_NODE_LOCK_RATE      "node.lock-rate"    /**< don't change rate when this node
3
                                  *  is active */
4
 #define PW_KEY_NODE_FORCE_RATE     "node.force-rate"   /**< force a rate while the node is
5
-                                 *  active */
6
+                                 *  active. A value of 0 takes the denominator
7
+                                 *  of node.rate */
8
 
9
 #define PW_KEY_NODE_DONT_RECONNECT "node.dont-reconnect"   /**< don't reconnect this node. The node is
10
                                  *  initially linked to target.object or the
11
pipewire-0.3.67.tar.gz/src/pipewire/loop.c -> pipewire-0.3.68.tar.gz/src/pipewire/loop.c Changed
44
 
1
@@ -9,6 +9,7 @@
2
 #include <spa/utils/result.h>
3
 
4
 #include <pipewire/pipewire.h>
5
+#include <pipewire/private.h>
6
 #include <pipewire/loop.h>
7
 #include <pipewire/log.h>
8
 #include <pipewire/type.h>
9
@@ -23,6 +24,9 @@
10
 
11
    struct spa_handle *system_handle;
12
    struct spa_handle *loop_handle;
13
+
14
+   void *user_data;
15
+   const struct pw_loop_callbacks *cb;
16
 };
17
 /** \endcond */
18
 
19
@@ -140,3 +144,24 @@
20
    pw_unload_spa_handle(impl->system_handle);
21
    free(impl);
22
 }
23
+
24
+void
25
+pw_loop_set_callbacks(struct pw_loop *loop, const struct pw_loop_callbacks *cb, void *data)
26
+{
27
+   struct impl *impl = SPA_CONTAINER_OF(loop, struct impl, this);
28
+
29
+   impl->user_data = data;
30
+   impl->cb = cb;
31
+}
32
+
33
+SPA_EXPORT
34
+int pw_loop_check(struct pw_loop *loop)
35
+{
36
+   struct impl *impl = SPA_CONTAINER_OF(loop, struct impl, this);
37
+   int res;
38
+   if (impl->cb && impl->cb->check)
39
+       res = impl->cb->check(impl->user_data, loop);
40
+   else
41
+       res = spa_loop_control_check(loop->control);
42
+   return res;
43
+}
44
pipewire-0.3.67.tar.gz/src/pipewire/private.h -> pipewire-0.3.68.tar.gz/src/pipewire/private.h Changed
79
 
1
@@ -346,6 +346,7 @@
2
 #define pw_core_resource_bound_id(r,...)   pw_core_resource(r,bound_id,0,__VA_ARGS__)
3
 #define pw_core_resource_add_mem(r,...)        pw_core_resource(r,add_mem,0,__VA_ARGS__)
4
 #define pw_core_resource_remove_mem(r,...) pw_core_resource(r,remove_mem,0,__VA_ARGS__)
5
+#define pw_core_resource_bound_props(r,...)    pw_core_resource(r,bound_props,1,__VA_ARGS__)
6
 
7
 static inline SPA_PRINTF_FUNC(5,0) void
8
 pw_core_resource_errorv(struct pw_resource *resource, uint32_t id, int seq,
9
@@ -373,6 +374,29 @@
10
    va_end(args);
11
 }
12
 
13
+struct pw_loop_callbacks {
14
+#define PW_VERSION_LOOP_CALLBACKS  0
15
+   uint32_t version;
16
+
17
+   int (*check) (void *data, struct pw_loop *loop);
18
+};
19
+
20
+void
21
+pw_loop_set_callbacks(struct pw_loop *loop, const struct pw_loop_callbacks *cb, void *data);
22
+
23
+int pw_loop_check(struct pw_loop *loop);
24
+
25
+#define ensure_loop(loop,...) ({                           \
26
+   int res = pw_loop_check(loop);                          \
27
+   if (res != 1) {                                 \
28
+       pw_log_warn("%s called from wrong context, check thread and locking: %s",       \
29
+               __func__, spa_strerror(res));               \
30
+       fprintf(stderr, "*** %s called from wrong context, check thread and locking: %s\n",\
31
+               __func__, spa_strerror(res));               \
32
+       /* __VA_ARGS__ */                           \
33
+   }                                       \
34
+})
35
+
36
 #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__)
37
 #define pw_context_driver_emit_start(c,n)  pw_context_driver_emit(c, start, 0, n)
38
 #define pw_context_driver_emit_xrun(c,n)   pw_context_driver_emit(c, xrun, 0, n)
39
@@ -695,12 +719,16 @@
40
    unsigned int lock_quantum:1;    /**< don't change graph quantum */
41
    unsigned int lock_rate:1;   /**< don't change graph rate */
42
    unsigned int transport_sync:1;  /**< supports transport sync */
43
-   unsigned int current_pending:1; /**< a quantum/rate update is pending */
44
+   unsigned int target_pending:1;  /**< a quantum/rate update is pending */
45
    unsigned int moved:1;       /**< the node was moved drivers */
46
    unsigned int added:1;       /**< the node was add to graph */
47
    unsigned int pause_on_idle:1;   /**< Pause processing when IDLE */
48
    unsigned int suspend_on_idle:1;
49
    unsigned int reconfigure:1;
50
+   unsigned int forced_rate:1;
51
+   unsigned int trigger:1;     /**< has the TRIGGER property and needs an extra
52
+                     *  trigger to start processing. */
53
+   unsigned int can_suspend:1;
54
 
55
    uint32_t port_user_data_size;   /**< extra size for port user data */
56
 
57
@@ -748,10 +776,10 @@
58
 
59
        struct ratelimit rate_limit;
60
    } rt;
61
-   struct spa_fraction current_rate;
62
-   uint64_t current_quantum;
63
+   struct spa_fraction target_rate;
64
+   uint64_t target_quantum;
65
 
66
-        void *user_data;                /**< extra user data */
67
+   void *user_data;                /**< extra user data */
68
 };
69
 
70
 struct pw_impl_port_mix {
71
@@ -965,6 +993,7 @@
72
 #define pw_proxy_emit_removed(p)   pw_proxy_emit(p, removed, 0)
73
 #define pw_proxy_emit_done(p,s)        pw_proxy_emit(p, done, 0, s)
74
 #define pw_proxy_emit_error(p,s,r,m)   pw_proxy_emit(p, error, 0, s, r, m)
75
+#define pw_proxy_emit_bound_props(p,g,r) pw_proxy_emit(p, bound_props, 1, g, r)
76
 
77
 struct pw_proxy {
78
    struct spa_interface impl;  /**< object implementation */
79
pipewire-0.3.67.tar.gz/src/pipewire/proxy.h -> pipewire-0.3.68.tar.gz/src/pipewire/proxy.h Changed
19
 
1
@@ -89,7 +89,7 @@
2
 
3
 /** Proxy events, use \ref pw_proxy_add_listener */
4
 struct pw_proxy_events {
5
-#define PW_VERSION_PROXY_EVENTS        0
6
+#define PW_VERSION_PROXY_EVENTS        1
7
         uint32_t version;
8
 
9
    /** The proxy is destroyed */
10
@@ -107,6 +107,8 @@
11
 
12
    /** an error occurred on the proxy */
13
         void (*error) (void *data, int seq, int res, const char *message);
14
+
15
+        void (*bound_props) (void *data, uint32_t global_id, const struct spa_dict *props);
16
 };
17
 
18
 /* Make a new proxy object. The id can be used to bind to a remote object and
19
pipewire-0.3.67.tar.gz/src/pipewire/resource.c -> pipewire-0.3.68.tar.gz/src/pipewire/resource.c Changed
23
 
1
@@ -194,9 +194,19 @@
2
    struct pw_impl_client *client = resource->client;
3
 
4
    resource->bound_id = global_id;
5
+
6
    if (client->core_resource != NULL) {
7
-       pw_log_debug("%p: %u global_id:%u", resource, resource->id, global_id);
8
-       pw_core_resource_bound_id(client->core_resource, resource->id, global_id);
9
+       struct pw_global *global = pw_map_lookup(&resource->context->globals, global_id);
10
+       const struct spa_dict *dict = global ? &global->properties->dict : NULL;
11
+
12
+       pw_log_debug("%p: %u global_id:%u %d", resource, resource->id, global_id,
13
+               client->core_resource->version);
14
+
15
+       if (client->core_resource->version >= 4)
16
+           pw_core_resource_bound_props(client->core_resource, resource->id, global_id,
17
+                   dict);
18
+       else
19
+           pw_core_resource_bound_id(client->core_resource, resource->id, global_id);
20
    }
21
    return 0;
22
 }
23
pipewire-0.3.67.tar.gz/src/pipewire/stream.c -> pipewire-0.3.68.tar.gz/src/pipewire/stream.c Changed
201
 
1
@@ -17,6 +17,7 @@
2
 #include <spa/pod/filter.h>
3
 #include <spa/pod/dynamic.h>
4
 #include <spa/debug/types.h>
5
+#include <spa/debug/dict.h>
6
 
7
 #define PW_ENABLE_DEPRECATED
8
 
9
@@ -1044,7 +1045,6 @@
10
            impl->drained = false;
11
            io->buffer_id = b->id;
12
            res = io->status = SPA_STATUS_HAVE_DATA;
13
-           pw_log_trace_fp("%p: pop %d %p", stream, b->id, io);
14
            /* we have a buffer, if we are not rt and don't follow
15
             * any rate matching and there are no more
16
             * buffers queued and there is a buffer to dequeue, ask for
17
@@ -1055,6 +1055,8 @@
18
            ask_more = !impl->process_rt && impl->rate_match == NULL &&
19
                queue_is_empty(impl, &impl->queued) &&
20
                !queue_is_empty(impl, &impl->dequeued);
21
+           pw_log_trace_fp("%p: pop %d %p ask_more:%u %p", stream, b->id, io,
22
+                   ask_more, impl->rate_match);
23
        } else if (impl->draining || impl->drained) {
24
            impl->draining = true;
25
            impl->drained = true;
26
@@ -1137,10 +1139,12 @@
27
            PW_STREAM_STATE_ERROR, message);
28
 }
29
 
30
-static void proxy_bound(void *data, uint32_t global_id)
31
+static void proxy_bound_props(void *data, uint32_t global_id, const struct spa_dict *props)
32
 {
33
    struct pw_stream *stream = data;
34
    stream->node_id = global_id;
35
+   if (props)
36
+       pw_properties_update(stream->properties, props);
37
    stream_set_state(stream, PW_STREAM_STATE_PAUSED, NULL);
38
 }
39
 
40
@@ -1149,7 +1153,7 @@
41
    .removed = proxy_removed,
42
    .destroy = proxy_destroy,
43
    .error = proxy_error,
44
-   .bound = proxy_bound,
45
+   .bound_props = proxy_bound_props,
46
 };
47
 
48
 static struct control *find_control(struct pw_stream *stream, uint32_t id)
49
@@ -1332,6 +1336,14 @@
50
    return 0;
51
 }
52
 
53
+static void node_event_destroy(void *data)
54
+{
55
+   struct pw_stream *stream = data;
56
+   struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
57
+   spa_hook_remove(&stream->node_listener);
58
+   impl->node = NULL;
59
+}
60
+
61
 static void node_event_info(void *data, const struct pw_node_info *info)
62
 {
63
    struct pw_stream *stream = data;
64
@@ -1359,6 +1371,7 @@
65
 
66
 static const struct pw_impl_node_events node_events = {
67
    PW_VERSION_IMPL_NODE_EVENTS,
68
+   .destroy = node_event_destroy,
69
    .info_changed = node_event_info,
70
 };
71
 
72
@@ -1423,6 +1436,8 @@
73
    struct match match;
74
    int res;
75
 
76
+   ensure_loop(context->main_loop, return NULL);
77
+
78
    impl = calloc(1, sizeof(struct stream));
79
    if (impl == NULL) {
80
        res = -errno;
81
@@ -1596,18 +1611,52 @@
82
    return "invalid-state";
83
 }
84
 
85
+static int stream_disconnect(struct stream *impl)
86
+{
87
+   struct pw_stream *stream = &impl->this;
88
+
89
+   pw_log_debug("%p: disconnect", stream);
90
+
91
+   if (impl->disconnecting)
92
+       return -EBUSY;
93
+
94
+   impl->disconnecting = true;
95
+
96
+   if (impl->node)
97
+       pw_impl_node_set_active(impl->node, false);
98
+
99
+   if (stream->proxy) {
100
+       pw_proxy_destroy(stream->proxy);
101
+       stream->proxy = NULL;
102
+   }
103
+
104
+   if (impl->node)
105
+       pw_impl_node_destroy(impl->node);
106
+
107
+   if (impl->disconnect_core) {
108
+       impl->disconnect_core = false;
109
+       spa_hook_remove(&stream->core_listener);
110
+       spa_list_remove(&stream->link);
111
+       pw_core_disconnect(stream->core);
112
+       stream->core = NULL;
113
+   }
114
+   return 0;
115
+}
116
+
117
 SPA_EXPORT
118
 void pw_stream_destroy(struct pw_stream *stream)
119
 {
120
    struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
121
    struct control *c;
122
 
123
+   ensure_loop(impl->context->main_loop, return);
124
+
125
    pw_log_debug("%p: destroy", stream);
126
 
127
    pw_stream_emit_destroy(stream);
128
 
129
    if (!impl->disconnecting)
130
-       pw_stream_disconnect(stream);
131
+       stream_disconnect(impl);
132
 
133
    if (stream->core) {
134
        spa_hook_remove(&stream->core_listener);
135
@@ -1656,6 +1705,9 @@
136
                void *data)
137
 {
138
    struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
139
+
140
+   ensure_loop(impl->context->main_loop);
141
+
142
    spa_hook_list_append(&stream->listener_list, listener, events, data);
143
 
144
    if (events->process && impl->rt_callbacks.funcs == NULL) {
145
@@ -1692,6 +1744,8 @@
146
    int changed, res = 0;
147
    struct match match;
148
 
149
+   ensure_loop(impl->context->main_loop, return -EIO);
150
+
151
    changed = pw_properties_update(stream->properties, dict);
152
    if (!changed)
153
        return 0;
154
@@ -1786,8 +1840,7 @@
155
    }
156
 }
157
 
158
-SPA_EXPORT
159
-int
160
+SPA_EXPORT int
161
 pw_stream_connect(struct pw_stream *stream,
162
          enum pw_direction direction,
163
          uint32_t target_id,
164
@@ -1802,7 +1855,13 @@
165
    uint32_t i;
166
    int res;
167
 
168
+   ensure_loop(impl->context->main_loop, return -EIO);
169
+
170
    pw_log_debug("%p: connect target:%d", stream, target_id);
171
+
172
+   if (impl->node != NULL || stream->state != PW_STREAM_STATE_UNCONNECTED)
173
+       return -EBUSY;
174
+
175
    impl->direction =
176
        direction == PW_DIRECTION_INPUT ? SPA_DIRECTION_INPUT : SPA_DIRECTION_OUTPUT;
177
    impl->flags = flags;
178
@@ -1879,6 +1938,11 @@
179
        return res;
180
 
181
    impl->disconnecting = false;
182
+   impl->drained = false;
183
+   impl->draining = false;
184
+   impl->driving = false;
185
+   impl->trigger = false;
186
+   impl->using_trigger = false;
187
    stream_set_state(stream, PW_STREAM_STATE_CONNECTING, NULL);
188
 
189
    if ((str = getenv("PIPEWIRE_NODE")) != NULL)
190
@@ -2022,40 +2086,18 @@
191
 int pw_stream_disconnect(struct pw_stream *stream)
192
 {
193
    struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
194
-
195
-   pw_log_debug("%p: disconnect", stream);
196
-
197
-   if (impl->disconnecting)
198
-       return 0;
199
-
200
-   impl->disconnecting = true;
201
pipewire-0.3.67.tar.gz/src/pipewire/thread-loop.c -> pipewire-0.3.68.tar.gz/src/pipewire/thread-loop.c Changed
190
 
1
@@ -9,6 +9,7 @@
2
 #include <spa/support/thread.h>
3
 #include <spa/utils/result.h>
4
 
5
+#include "private.h"
6
 #include "log.h"
7
 #include "thread.h"
8
 #include "thread-loop.h"
9
@@ -32,6 +33,7 @@
10
    pthread_cond_t accept_cond;
11
 
12
    pthread_t thread;
13
+   int recurse;
14
 
15
    struct spa_hook hook;
16
 
17
@@ -44,22 +46,71 @@
18
 };
19
 /** \endcond */
20
 
21
-static void before(void *data)
22
+static int do_lock(struct pw_thread_loop *this)
23
+{
24
+   int res;
25
+   if ((res = pthread_mutex_lock(&this->lock)) != 0)
26
+       pw_log_error("%p: thread:%lu: %s", this, pthread_self(), strerror(res));
27
+   else
28
+       this->recurse++;
29
+   return -res;
30
+}
31
+
32
+static int do_unlock(struct pw_thread_loop *this)
33
+{
34
+   int res;
35
+   spa_return_val_if_fail(this->recurse > 0, -EIO);
36
+   this->recurse--;
37
+   if ((res = pthread_mutex_unlock(&this->lock)) != 0) {
38
+       pw_log_error("%p: thread:%lu: %s", this, pthread_self(), strerror(res));
39
+       this->recurse++;
40
+   }
41
+   return -res;
42
+}
43
+
44
+static void impl_before(void *data)
45
 {
46
    struct pw_thread_loop *this = data;
47
-   pthread_mutex_unlock(&this->lock);
48
+   do_unlock(this);
49
 }
50
 
51
-static void after(void *data)
52
+static void impl_after(void *data)
53
 {
54
    struct pw_thread_loop *this = data;
55
-   pthread_mutex_lock(&this->lock);
56
+   do_lock(this);
57
 }
58
 
59
 static const struct spa_loop_control_hooks impl_hooks = {
60
    SPA_VERSION_LOOP_CONTROL_HOOKS,
61
-   before,
62
-   after,
63
+   .before = impl_before,
64
+   .after = impl_after,
65
+};
66
+
67
+static int impl_check(void *data, struct pw_loop *loop)
68
+{
69
+   struct pw_thread_loop *this = data;
70
+   int res;
71
+
72
+   /* we are in the thread running the loop */
73
+   if (spa_loop_control_check(this->loop->control) == 1)
74
+       return 1;
75
+
76
+   /* if lock taken by something else, error */
77
+   if ((res = pthread_mutex_trylock(&this->lock)) != 0) {
78
+       pw_log_debug("%p: thread:%lu: %s", this, pthread_self(), strerror(res));
79
+       return -res;
80
+   }
81
+   /* we could take the lock, check if we actually locked it somewhere */
82
+   res = this->recurse > 0 ? 1 : -EPERM;
83
+   if (res < 0)
84
+       pw_log_debug("%p: thread:%lu: recurse:%d", this, pthread_self(), this->recurse);
85
+   pthread_mutex_unlock(&this->lock);
86
+   return res;
87
+}
88
+
89
+static const struct pw_loop_callbacks impl_callbacks = {
90
+   PW_VERSION_LOOP_CALLBACKS,
91
+   .check = impl_check,
92
 };
93
 
94
 static void do_stop(void *data, uint64_t count)
95
@@ -121,6 +172,7 @@
96
        goto clean_acceptcond;
97
    }
98
 
99
+   pw_loop_set_callbacks(loop, &impl_callbacks, this);
100
    pw_loop_add_hook(loop, &this->hook, &impl_hooks, this);
101
 
102
    return this;
103
@@ -225,7 +277,7 @@
104
    struct pw_thread_loop *this = user_data;
105
    int res;
106
 
107
-   pthread_mutex_lock(&this->lock);
108
+   do_lock(this);
109
    pw_log_debug("%p: enter thread", this);
110
    pw_loop_enter(this->loop);
111
 
112
@@ -239,7 +291,7 @@
113
    }
114
    pw_log_debug("%p: leave thread", this);
115
    pw_loop_leave(this->loop);
116
-   pthread_mutex_unlock(&this->lock);
117
+   do_unlock(this);
118
 
119
    return NULL;
120
 }
121
@@ -306,7 +358,7 @@
122
 SPA_EXPORT
123
 void pw_thread_loop_lock(struct pw_thread_loop *loop)
124
 {
125
-   pthread_mutex_lock(&loop->lock);
126
+   do_lock(loop);
127
    pw_log_trace("%p", loop);
128
 }
129
 
130
@@ -319,7 +371,7 @@
131
 void pw_thread_loop_unlock(struct pw_thread_loop *loop)
132
 {
133
    pw_log_trace("%p", loop);
134
-   pthread_mutex_unlock(&loop->lock);
135
+   do_unlock(loop);
136
 }
137
 
138
 /** Signal the thread
139
@@ -342,8 +394,11 @@
140
    if (wait_for_accept) {
141
        loop->n_waiting_for_accept++;
142
 
143
-       while (loop->n_waiting_for_accept > 0)
144
-           pthread_cond_wait(&loop->accept_cond, &loop->lock);
145
+       while (loop->n_waiting_for_accept > 0) {
146
+           int res;
147
+           if ((res = pthread_cond_wait(&loop->accept_cond, &loop->lock)) != 0)
148
+               pw_log_error("%p: thread:%lu: %s", loop, pthread_self(), strerror(res));
149
+       }
150
    }
151
 }
152
 
153
@@ -355,9 +410,15 @@
154
 SPA_EXPORT
155
 void pw_thread_loop_wait(struct pw_thread_loop *loop)
156
 {
157
-   pw_log_trace("%p, waiting %d", loop, loop->n_waiting);
158
+   int res;
159
+
160
+   pw_log_trace("%p, waiting:%d recurse:%d", loop, loop->n_waiting, loop->recurse);
161
+   spa_return_if_fail(loop->recurse > 0);
162
    loop->n_waiting++;
163
-   pthread_cond_wait(&loop->cond, &loop->lock);
164
+   loop->recurse--;
165
+   if ((res = pthread_cond_wait(&loop->cond, &loop->lock)) != 0)
166
+       pw_log_error("%p: thread:%lu: %s", loop, pthread_self(), strerror(res));
167
+   loop->recurse++;
168
    loop->n_waiting--;
169
    pw_log_trace("%p, waiting done %d", loop, loop->n_waiting);
170
 }
171
@@ -418,8 +479,11 @@
172
 int pw_thread_loop_timed_wait_full(struct pw_thread_loop *loop, const struct timespec *abstime)
173
 {
174
    int ret;
175
+   spa_return_val_if_fail(loop->recurse > 0, -EIO);
176
    loop->n_waiting++;
177
+   loop->recurse--;
178
    ret = pthread_cond_timedwait(&loop->cond, &loop->lock, abstime);
179
+   loop->recurse++;
180
    loop->n_waiting--;
181
    return -ret;
182
 }
183
@@ -445,5 +509,5 @@
184
 SPA_EXPORT
185
 bool pw_thread_loop_in_thread(struct pw_thread_loop *loop)
186
 {
187
-   return loop->running && pthread_self() == loop->thread;
188
+   return loop->running && pthread_equal(loop->thread, pthread_self());
189
 }
190
pipewire-0.3.67.tar.gz/src/pipewire/utils.h -> pipewire-0.3.68.tar.gz/src/pipewire/utils.h Changed
12
 
1
@@ -16,6 +16,10 @@
2
 # include <sys/mount.h>
3
 #endif
4
 
5
+#ifndef ENODATA
6
+#define ENODATA 9919
7
+#endif
8
+
9
 #include <spa/utils/defs.h>
10
 #include <spa/pod/pod.h>
11
 
12
pipewire-0.3.67.tar.gz/src/tests/test-interfaces.c -> pipewire-0.3.68.tar.gz/src/tests/test-interfaces.c Changed
19
 
1
@@ -42,6 +42,7 @@
2
        void (*bound_id) (void *data, uint32_t id, uint32_t global_id);
3
        void (*add_mem) (void *data, uint32_t id, uint32_t type, int fd, uint32_t flags);
4
        void (*remove_mem) (void *data, uint32_t id);
5
+       void (*bound_props) (void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props);
6
    } events = { PW_VERSION_CORE_EVENTS, };
7
 
8
    struct pw_core_events e;
9
@@ -68,7 +69,8 @@
10
    TEST_FUNC(e, events, bound_id);
11
    TEST_FUNC(e, events, add_mem);
12
    TEST_FUNC(e, events, remove_mem);
13
-   spa_assert_se(PW_VERSION_CORE_EVENTS == 0);
14
+   TEST_FUNC(e, events, bound_props);
15
+   spa_assert_se(PW_VERSION_CORE_EVENTS == 1);
16
    spa_assert_se(sizeof(e) == sizeof(events));
17
 }
18
 
19
pipewire-0.3.67.tar.gz/src/tools/meson.build -> pipewire-0.3.68.tar.gz/src/tools/meson.build Changed
32
 
1
@@ -58,16 +58,26 @@
2
     'pw-encplay',
3
   
4
 
5
-  executable('pw-cat',
6
+  pw_cat = executable('pw-cat',
7
     pwcat_sources,
8
     install: true,
9
     dependencies : pwcat_deps, pipewire_dep, mathlib,
10
   )
11
 
12
   foreach alias : pwcat_aliases
13
-    dst = pipewire_bindir / alias
14
-    cmd = 'ln -fs @0@ $DESTDIR@1@'.format('pw-cat', dst)
15
-    meson.add_install_script('sh', '-c', cmd)
16
+    custom_target(
17
+      alias,
18
+      build_by_default: true,
19
+      install: false,
20
+      command: ln, '-sf', meson.project_build_root() + '/@INPUT@', '@OUTPUT@',
21
+      input: pw_cat,
22
+      output: alias,
23
+    )
24
+    install_symlink(
25
+      alias,
26
+      pointing_to: pw_cat.name(),
27
+      install_dir: pipewire_bindir,
28
+    )
29
   endforeach
30
 elif not sndfile_dep.found() and get_option('pw-cat').enabled()
31
   error('pw-cat is enabled but required dependency `sndfile` was not found.')
32
pipewire-0.3.67.tar.gz/src/tools/pw-cli.c -> pipewire-0.3.68.tar.gz/src/tools/pw-cli.c Changed
69
 
1
@@ -1502,7 +1502,7 @@
2
    return global_port_found;
3
 }
4
 
5
-static void create_link_with_properties(struct data *data, struct pw_properties *props)
6
+static void create_link_with_properties(struct data *data, const struct pw_properties *props)
7
 {
8
    struct remote_data *rd = data->current;
9
    uint32_t id;
10
@@ -1534,6 +1534,7 @@
11
    char *a5;
12
    int n;
13
    struct pw_properties *props = NULL;
14
+   bool res = false;
15
 
16
    n = pw_split_ip(args, WHITESPACE, 5, a);
17
    if (n < 4) {
18
@@ -1562,12 +1563,12 @@
19
        global_out = find_global(rd, a0);
20
        if (global_out == NULL) {
21
            *error = spa_aprintf("%s: unknown global '%s'", cmd, a0);
22
-           return false;
23
+           goto done;
24
        }
25
        global_in = find_global(rd, a2);
26
        if (global_in == NULL) {
27
            *error = spa_aprintf("%s: unknown global '%s'", cmd, a2);
28
-           return false;
29
+           goto done;
30
        }
31
 
32
        pd_out = pw_proxy_get_user_data(global_out->proxy);
33
@@ -1578,7 +1579,7 @@
34
 
35
        if (n_output_ports != n_input_ports) {
36
            *error = spa_aprintf("%s: Number of ports don't match (%u != %u)", cmd, n_output_ports, n_input_ports);
37
-           return false;
38
+           goto done;
39
        }
40
 
41
        for (uint32_t i = 0; i < n_output_ports; i++) {
42
@@ -1601,9 +1602,12 @@
43
    } else
44
        create_link_with_properties(data, props);
45
 
46
+   res = true;
47
+
48
+done:
49
    pw_properties_free(props);
50
 
51
-   return true;
52
+   return res;
53
 }
54
 
55
 static bool do_export_node(struct data *data, const char *cmd, char *args, char **error)
56
@@ -2336,8 +2340,10 @@
57
            fprintf(stderr, "Error: \"%s\"\n", error);
58
            free(error);
59
        }
60
-       data.current->prompt_pending = pw_core_sync(data.current->core, 0, 0);
61
-       while (!data.quit && data.current) {
62
+       if (data.current != NULL)
63
+           data.current->prompt_pending = pw_core_sync(data.current->core, 0, 0);
64
+
65
+       while (!data.quit && data.current != NULL) {
66
            pw_main_loop_run(data.loop);
67
            if (!monitor)
68
                break;
69
pipewire-0.3.67.tar.gz/src/tools/pw-top.c -> pipewire-0.3.68.tar.gz/src/tools/pw-top.c Changed
11
 
1
@@ -171,7 +171,8 @@
2
    {
3
        uint32_t media_type, media_subtype;
4
 
5
-       spa_format_parse(param, &media_type, &media_subtype);
6
+       if (spa_format_parse(param, &media_type, &media_subtype) < 0)
7
+           goto done;
8
 
9
        switch(media_type) {
10
        case SPA_MEDIA_TYPE_audio:
11