diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2918ce62e..448bfa06e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -410,15 +410,13 @@ build_on_fedora_html_docs:
-Dsndfile=enabled
-Dsession-managers=[]
before_script:
- - git fetch origin 1.0 1.2 1.4 1.6 master
+ - git fetch origin 1.0 1.2 1.4 master
- git branch -f 1.0 origin/1.0
- git clone -b 1.0 . branch-1.0
- git branch -f 1.2 origin/1.2
- git clone -b 1.2 . branch-1.2
- git branch -f 1.4 origin/1.4
- git clone -b 1.4 . branch-1.4
- - git branch -f 1.6 origin/1.6
- - git clone -b 1.6 . branch-1.6
- git branch -f master origin/master
- git clone -b master . branch-master
- !reference [.build, before_script]
@@ -435,10 +433,6 @@ build_on_fedora_html_docs:
- meson setup builddir $MESON_OPTIONS
- meson compile -C builddir doc/pipewire-docs
- cd ..
- - cd branch-1.6
- - meson setup builddir $MESON_OPTIONS
- - meson compile -C builddir doc/pipewire-docs
- - cd ..
- cd branch-master
- meson setup builddir $MESON_OPTIONS
- meson compile -C builddir doc/pipewire-docs
@@ -664,7 +658,7 @@ doccheck:
- cat pipewire_module_pages
- |
for page in $(cat pipewire_module_pages); do
- git grep -q -e "\\\subpage $page" || (echo "\\page $page is missing \\subpage entry in doc/dox/modules.dox" && false)
+ git grep -q -e "\\\subpage $page" || (echo "\\page $page is missing \\subpage entry in doc/pipewire-modules.dox" && false)
done
check_missing_headers:
@@ -688,13 +682,12 @@ pages:
- job: build_on_fedora_html_docs
artifacts: true
script:
- - mkdir public public/1.0 public/1.2 public/1.4 public/1.6 public/devel
+ - mkdir public public/1.0 public/1.2 public/1.4 public/devel
- cp -R branch-1.0/builddir/doc/html/* public/1.0/
- cp -R branch-1.2/builddir/doc/html/* public/1.2/
- cp -R branch-1.4/builddir/doc/html/* public/1.4/
- - cp -R branch-1.6/builddir/doc/html/* public/1.6/
- cp -R branch-master/builddir/doc/html/* public/devel/
- - (cd public && ln -s 1.6/* .)
+ - (cd public && ln -s 1.4/* .)
artifacts:
paths:
- public
diff --git a/NEWS b/NEWS
index 1f0a39a28..6e8b7f6f5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,69 @@
+# PipeWire 1.6.2 (2026-03-16)
+
+This is a bugfix release that is API and ABI compatible with the previous
+1.6.x releases.
+
+## Highlights
+ - Fix a potential crash when the wrong memory was freed.
+ - Fix a optimization with shared memory over some links that could
+ cause errors later on.
+ - Fix SOFA filter and default control input in LADSPA and LV2.
+ - Some other small fixes and improvements.
+
+
+## PipeWire
+ - Remove an optimization to skip share mem in links, it causes problems
+ later on. (#5159)
+
+## Modules
+ - Don't try to free invalid memory or close invalid fds when the client
+ aborted before allocating buffer memory. (#5162)
+
+## SPA
+ - support ACP_IGNORE_DB in udev.
+ - Use 0x as a prefix for hex values.
+ - Mark Props as write-only in libcamera.
+ - Small optimization in the audio mixer.
+ - Fix initialization of control properties for SOFA and biquads in the
+ filter-graph. (#5152)
+ - Fix min/max default values for LADSPA and LV2.
+
+## JACK
+ - Fix jack_port_type_id(). Return values that are compatible with JACK1/2.
+
+
+Older versions:
+
+# PipeWire 1.6.1 (2026-03-09)
+
+This is a bugfix release that is API and ABI compatible with the previous
+1.6.x releases.
+
+## Highlights
+ - Fix socket activation, which could cause a failure to start PipeWire in
+ some setups.
+ - Fix crashes in many JACK apps when nodes/ports are quickly added/removed
+ such as when there are notifications (like when changing the volume in
+ KDE).
+ - Fix playback of encoded formats in pw-cat again.
+ - Some other smaller fixes and improvements.
+
+## Modules
+ - Fix socket activation. (#5140)
+ - Remove node.link-group from driver nodes.
+
+## SPA
+ - Fix the libcamera stop sequence.
+
+## JACK
+ - Never return NULL from jack_port_by_id(). (#3512)
+
+## GStreamer
+ - Improve the timestamps on buffers.
+
+## Tools
+ - Fix playback of encoded formats. (#5155)
+
# PipeWire 1.6.0 (2026-02-19)
This is the 1.6 release that is API and ABI compatible with previous
@@ -95,8 +161,6 @@ the 1.4 release last year, including:
- Add some more options to pw-cat to list supported containers
and formats. (#5117)
-Older versions:
-
# PipeWire 1.5.85 (2026-01-19)
This is the fifth and hopefully last 1.6 release candidate that
diff --git a/doc/dox/config/pipewire-client.conf.5.md b/doc/dox/config/pipewire-client.conf.5.md
index 321538a79..db43839a0 100644
--- a/doc/dox/config/pipewire-client.conf.5.md
+++ b/doc/dox/config/pipewire-client.conf.5.md
@@ -80,9 +80,6 @@ stream.properties = {
#channelmix.fc-cutoff = 12000.0
#channelmix.rear-delay = 12.0
#channelmix.stereo-widen = 0.0
- #channelmix.center-level = 0.707106781
- #channelmix.surround-level = 0.707106781
- #channelmix.lfe-level = 0.5
#channelmix.hilbert-taps = 0
#dither.noise = 0
#dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5
diff --git a/doc/dox/config/pipewire-props.7.md b/doc/dox/config/pipewire-props.7.md
index 44b16f505..e561979d5 100644
--- a/doc/dox/config/pipewire-props.7.md
+++ b/doc/dox/config/pipewire-props.7.md
@@ -450,25 +450,11 @@ Whether the node target may be changed using metadata.
@PAR@ node-prop node.passive = false
\parblock
-This can be used to configure the port.passive property for all ports of this node.
-
-Possible values are:
-
- * "out": output ports are passive, They will not make the peers active and active peers will
- not make this node active.
- * "in": input ports are passive, They will not make the peers active and active peers will
- not make this node active.
- * "true": A combination in "in" and "out", both input and output ports are passive.
- * "out-follow": output ports will not make the peer active but when the peer is activated via
- some other way, this node will also become active.
- * "in-follow": input ports will not make the peer active but when the peer is activated via
- some other way, this node will also become active.
- * "follow": A combination of "in-follow" and "out-follow".
+This is a passive node and so it should not keep sinks/sources busy. This property makes the session manager create passive links to the sink/sources. If the node is not otherwise linked (via a non-passive link), the node and the sink it is linked to are idle (and eventually suspended).
This is used for filter nodes that sit in front of sinks/sources and need to suspend together with the sink/source.
\endparblock
-
@PAR@ node-prop node.link-group = ID
Add the node to a certain link group. Nodes from the same link group are not automatically linked to each other by the session manager. And example is a coupled stream where you don't want the output to link to the input streams, making a useless loop.
@@ -783,15 +769,6 @@ more to the center speaker and leaves the ambient sound in the stereo channels.
This is only active when up-mix is enabled and a Front Center channel is mixed.
\endparblock
-@PAR@ node-prop channelmix.center-level = 0.707106781
-The level of the center channel when up/downmixing.
-
-@PAR@ node-prop channelmix.surround-level = 0.707106781
-The level of the surround channels when up/downmixing.
-
-@PAR@ node-prop channelmix.lfe-level = 0.5
-The level of the LFE channel when up/downmixing.
-
@PAR@ node-prop channelmix.hilbert-taps = 0
\parblock
This option will apply a 90 degree phase shift to the rear channels to improve specialization.
@@ -1398,9 +1375,9 @@ Default: as per QoS preset.
@PAR@ device-prop bluez5.bap.force-target-latency = "balanced" # string
BAP QoS target latency profile forced for QoS configuration selection.
-If not set or set to "balanced", both low-latency and high-reliability QoS configuration table are used.
+If not set or set to "balanced", both low-latency and high-reliabilty QoS configuration table are used.
This property is experimental.
-Available: low-latency, high-reliability, balanced
+Available: low-latency, high-reliabilty, balanced
## Node properties
@@ -1443,11 +1420,6 @@ them. Below are some port properties may interesting for users:
\copydoc PW_KEY_PORT_ALIAS
\endparblock
-@PAR@ port-prop port.passive # string
-\parblock
-\copydoc PW_KEY_PORT_PASSIVE
-\endparblock
-
\see pw_keys in the API documentation for a full list.
# LINK PROPERTIES @IDX@ props
diff --git a/doc/dox/config/pipewire-pulse.conf.5.md b/doc/dox/config/pipewire-pulse.conf.5.md
index 9cc8d4c48..ad1b213c7 100644
--- a/doc/dox/config/pipewire-pulse.conf.5.md
+++ b/doc/dox/config/pipewire-pulse.conf.5.md
@@ -93,9 +93,6 @@ stream.properties = {
#channelmix.fc-cutoff = 12000.0
#channelmix.rear-delay = 12.0
#channelmix.stereo-widen = 0.0
- #channelmix.center-level = 0.707106781
- #channelmix.surround-level = 0.707106781
- #channelmix.lfe-level = 0.5
#channelmix.hilbert-taps = 0
#dither.noise = 0
#dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5
diff --git a/doc/dox/internals/index.dox b/doc/dox/internals/index.dox
index 357e2f126..89d2e9da3 100644
--- a/doc/dox/internals/index.dox
+++ b/doc/dox/internals/index.dox
@@ -10,7 +10,6 @@
- \subpage page_objects_design
- \subpage page_library
- \subpage page_dma_buf
-- \subpage page_running
- \subpage page_scheduling
- \subpage page_driver
- \subpage page_latency
diff --git a/doc/dox/internals/latency.dox b/doc/dox/internals/latency.dox
index efc2b4c8b..0ff2cbe95 100644
--- a/doc/dox/internals/latency.dox
+++ b/doc/dox/internals/latency.dox
@@ -103,7 +103,7 @@ down and upstream.
# Async nodes
When a node has the node.async property set to true, it will be considered an async
-node and will be scheduled differently, see \ref page_scheduling .
+node and will be scheduled differently, see scheduling.dox.
A link between a port of an async node and another port (async or not) is called an
async link and will have the link.async=true property.
diff --git a/doc/dox/internals/midi.dox b/doc/dox/internals/midi.dox
index e89b24578..4c86c516b 100644
--- a/doc/dox/internals/midi.dox
+++ b/doc/dox/internals/midi.dox
@@ -62,13 +62,6 @@ As of 1.4, SPA_CONTROL_UMP (Universal Midi Packet) is the prefered format
for the MIDI 1.0 and 2.0 messages in the \ref spa_pod_sequence. Conversion
to SPA_CONTROL_Midi is performed for legacy applications.
-As of 1.7 the prefered format is Midi1 again because most devices and
-applications are still Midi1 and conversions between Midi1 and UMP are not
-completely transparent in ALSA and PipeWire. UMP in the ALSA sequencer
-and consumers must be enabled explicitly. UMP in producers is supported
-still and will be converted to Midi1 by all consumers that did not explicitly
-enable UMP support.
-
## The PipeWire Daemon
Nothing special is implemented for MIDI. Negotiation of formats
@@ -111,14 +104,13 @@ filtering out the \ref SPA_CONTROL_Midi, \ref SPA_CONTROL_OSC and
\ref SPA_CONTROL_UMP types. On output ports, the JACK event stream is
converted to control messages in a similar way.
-Normally, all MIDI and UMP input messages are converted to MIDI1 jack
-events unless the JACK port was created with an explcit "32 bit raw UMP"
-format or with the JackPortIsMIDI2 flag, in which case the messages are
-converted to UMP or passed on directly.
-
-For output ports, the JACK events are assumed to be
-MIDI1 unless the port has the "32 bit raw UMP" format or the JackPortIsMIDI2
-flag, in which case the control messages are assumed to be UMP.
+Normally, all MIDI and UMP messages are converted to MIDI1 jack events unless
+the JACK port was created with an explcit "32 bit raw UMP" format or with
+the JackPortIsMIDI2 flag, in which case the raw UMP is passed to the JACK
+application directly. For output ports,
+the JACK events are assumed to be MIDI1 and converted to UMP unless the port
+has the "32 bit raw UMP" format or the JackPortIsMIDI2 flag, in which case
+the UMP messages are simply passed on.
There is a 1 to 1 mapping between the JACK events and control
messages so there is no information loss or need for complicated
diff --git a/doc/dox/internals/running.dox b/doc/dox/internals/running.dox
deleted file mode 100644
index e7ffe06db..000000000
--- a/doc/dox/internals/running.dox
+++ /dev/null
@@ -1,393 +0,0 @@
-/** \page page_running Node running
-
-This document tries to explain how the Nodes in a PipeWire graph become
-runnable so that they can be scheduled later.
-
-It also describes how nodes are grouped together and scheduled together.
-
-# Runnable Nodes
-
-A runnable node is a node that will participate in the dataflow in the
-PipeWire graph.
-
-Not all nodes should participate by default. For example, filters or device
-nodes that are not linked to any runnable nodes should not do useless
-processing.
-
-Nodes form one or more groups depending on properties and how they are
-linked. For each group, one driver is selected to run the group. Inside the
-group, all runnable nodes are scheduled by the group driver. If there are no
-runnable nodes in a group, the driver is not started.
-
-PipeWire provides mechanisms to precisely describe how and when nodes should
-be scheduled and grouped using:
-
- - port properties to control how port links control runnable state
- - node properties to control processing
- - grouping of nodes and internal links between nodes
-
-# Port passive modes
-
-A Port has 4 passive modes, this depends on the value of the `port.passive` property:
-
- - `false`, the port will make the peer active and an active peer will make this port
- active.
- - `true`, the port will not make the peer active and an active peer will not make this
- port active.
- - `follow`, the port will not make the peer active but an active peer will make this
- port active.
- - `follow-suspend`, the port will only make another follow-suspend peer active but any
- active peer will make this port active.
-
-The combination of these 4 modes on the output and input ports of a link results in a
-wide range of use cases.
-
-# Node passive modes
-
-A Node can have 10 passive modes, `node.passive` can be set to a comma separated list
-of the following values:
-
- - `false`, both input and output ports have `port.passive = false`
- - `in`, input ports have `port.passive = true`
- - `out`, output ports have `port.passive = true`
- - `true`, both input and output ports have `port.passive = true`. This is the same
- as `in,out`.
- - `in-follow`, input ports have `port.passive = follow`
- - `out-follow`, output ports have `port.passive = follow`
- - `follow`, input and output ports have `port.passive = follow`. This is the same
- as `in-follow,out-follow`.
- - `in-follow-suspend`, input ports have `port.passive = follow-suspend`
- - `out-follow-suspend`, output ports have `port.passive = follow-suspend`
- - `follow-suspend`, input and output ports have `port.passive = follow-suspend`.
- This is the same as `in-follow-suspend,out-follow-suspend`.
-
-Nodes by default have the `false` mode but nodes with the `media.class` property
-containing `Sink`, `Source` or `Duplex` receive the `follow-suspend` mode by default.
-
-Unless explicitly configured, ports inherit the mode from their parent node.
-
-# Updating the node runnable state
-
-We iterate all nodes A in the graph and look at its peers B.
-
-Based on the port passive modes of the port links we can decide if the nodes are
-runnable or not. A link will always make both nodes runnable or none.
-
-The following table decides the runnability of the 2 nodes based on the port.passive
-mode of the link between the 2 ports:
-
-```
- B-false B-true B-follow B-follow-suspend
-
-A-false X X X X
-A-true
-A-follow
-A-follow-suspend X
- Table 1
-```
-
-
-When a node is made runnable, the port passive mode will then decide if the peer ports
-should become active as well with the following table.
-
-```
- B-false B-true B-follow B-follow-suspend
-
-A-false X X X
-A-true X X X
-A-follow X X X
-A-follow-suspend X X X
- Table 2
-```
-
-So when A is runnable, all peers are activated except those with `port.passive=true`.
-
-When A is runnable, all the nodes that share the same group or link-group will also
-be made runnable.
-
-# Use cases
-
-Let's check some cases that we want to solve with these node and port properties.
-
-## Device nodes
-
-```
- +--------+ +--------+
- | ALSA | | ALSA |
- | Source | | Sink |
- | FL FL |
- | FR FR |
- +--------+ +--------+
-```
-
-Unlinked device nodes are supposed to stay suspended when nothing is linked to
-them.
-
-```
- +----------+ +--------+
- | playback | | ALSA |
- | | | Sink |
- | FL ------ FL |
- | FR ------ FR |
- +----------+ +--------+
-```
-
-An (active) player node linked to a device node should make both nodes runnable.
-
-Device nodes have the `port.passive = follow-suspend` property by default. The
-playback node has the `port.passive = false` by default.
-
-If we look at the playback node as A and the sink as B, both nodes will be made
-runnable according to Table 1.
-
-The two runnable nodes form a group and will be scheduled together. One of the
-nodes of a group with the `node.driver = true` property is selected as the
-driver. In the above case, that will be the ALSA Sink.
-
-Likewise, a capture node linked to an ALSA Source should make both nodes runnable.
-
-```
- +--------+ +---------+
- | ALSA | | capture |
- | Source | | |
- | FL ------ FL |
- | FR ------ FR |
- +--------+ +---------+
-```
-
-The ALSA Source is now the driver.
-
-Also, linking 2 device nodes together should make them runnable:
-
-```
- +--------+ +--------+
- | ALSA | | ALSA |
- | Source | | Sink |
- | FL ----------------------- FL |
- | FR ----------------------- FR |
- +--------+ +--------+
-```
-
-This is the case because in Table 1, the two `port.passive = follow-suspend` ports
-from the Source and Sink activate each other.
-
-## Filter nodes
-
-When there is a filter in front of the ALSA Sink, it should not make the filter and
-sink runnable.
-
-```
- +--------+ +--------+
- | filter | | ALSA |
- | | | Sink |
- FL FL ------ FL |
- FR FR ------ FR |
- +--------+ +--------+
-```
-
-The links between the filter and ALSA Sink are `port.passive = true` and don't make
-the nodes runnable.
-
-The filter needs to be made runnable via some other means to also make the ALSA
-Sink runnable, for example by linking a playback node:
-
-```
- +----------+ +--------+ +--------+
- | playback | | filter | | ALSA |
- | | | | | Sink |
- | FL ------ FL FL ------ FL |
- | FR ------ FR FR ------ FR |
- +----------+ +--------+ +--------+
-```
-
-The input port of the filter is `port.passive = follow-suspend' and so it can be
-activated by the playback node.
-
-Likewise, if the ALSA Sink is runnable, it should not automatically make the
-filter runnable. For example:
-
-```
- +--------+ +--------+
- | filter | | ALSA |
- | | | Sink |
- FL FL ---+-- FL |
- FR FR ---|+- FR |
- +--------+ || +--------+
- ||
- +----------+ ||
- | playback | ||
- | | ||
- | FL ---+|
- | FR ----+
- +----------+
-```
-
-Here the playback node makes the ALSA Sink runnable but the filter
-stays not-runnable because the output port is `port.passive = true`.
-
-## Device node monitor
-
-Consider the case where we have an ALSA Sink and a monitor stream
-connected to the sink monitor ports.
-
-```
- +-------+ +--------++
- | ALSA | | monitor |
- | Sink | | |
- FL FL ------ FL |
- FR FR ------ FR |
- +-------+ +---------+
-```
-
-We would like to keep the monitor stream and the ALSA sink suspended
-unless something else activates the ALSA Sink:
-
-
-```
- +----------+ +-------+ +---------+
- | playback | | ALSA | | monitor |
- | | | Sink | | |
- | FL ------ FL FL ------ FL |
- | FR ------ FR FR ------ FR |
- +----------+ +-------+ +---------+
-```
-
-We can do this by making the monitor stream input ports `port.passive = follow`
-and leave the ALSA Sink monitor output ports as `port.passive = follow-suspend`.
-
-According to Table 1, both nodes will not activate each other but when ALSA Sink
-becomes runnable because of playback, according to Table 2, the monitor will
-become runnable as well.
-
-Note how we need the distinction between `follow` and `follow-suspend` for this
-use case.
-
-## Node groups
-
-Normally when an application makes a capture and playback node, both nodes will
-be scheduled in different groups, consider:
-
-
-```
- +--------+ +---------+
- | ALSA | | capture |
- | Source | | |
- | FL ------ FL |
- | FR ------ FR |
- +--------+ +---------+
-
- +----------+ +--------+
- | playback | | ALSA |
- | | | Sink |
- | FL ------ FL |
- | FR ------ FR |
- +----------+ +--------+
-```
-
-Here we see 2 groups with the ALSA Source and ALSA Sink respectively as the
-drivers. Depending on the clocks of the nodes, the capture and playback will not
-be in sync. They will each run in their own time domain depending on the rate of
-the drivers.
-
-When we place a node.group property with the same value on the capture and playback
-nodes, they will be grouped together and this whole graph becomes one single group.
-
-Because there are 2 potential drivers in the group, the one with the highest
-`priority.driver` property is selected as the driver in the group. The other nodes
-in the group (including the other driver) become followers in the group.
-
-When a node becomes runnable, all other nodes with the same node.group property
-become runnable as well.
-
-## Node link groups
-
-When we have a filter that is constructed from two nodes, an input and an output
-node, we could use the `node.group` property to make sure they are both scheduled
-and made runnable together.
-
-```
- +--------+ +-------+ +--------+ +-------+
- | ALSA | | input | | output | | ALSA |
- | Source | | | | | | Sink |
- | FL ------ FL -- processing-- FL ------ FL |
- | FR ------ FR | | FR ------ FR |
- +--------+ +-------+ +--------+ +-------+
-```
-
-This would work fine but it does not describe that there is an implicit internal
-link between the input and output node. This information is important for the
-session manager to avoid linking the output node to the input node and make a
-loop.
-
-The `node.link-group` property can be used to both group the nodes together and
-descibe that they are internally linked together.
-
-When a node becomes runnable, all other nodes with the same node.link-group property
-become runnable as well.
-
-For the 2 node filters, like loopback and filter-chain, the same `port.passive`
-property rules apply as for the filter nodes. Note that for the virtual devices,
-the Source/Sink nodes will be `follow-suspend` by default and the other node should
-be set to `node.passive = true` to make the ports passive.
-
-## Want driver
-
-When there is no driver node in the group, nothing should be scheduled. This can
-happen when a playback node is linked to a capture node:
-
-```
- +--------+ +---------+
- | player | | capture |
- | | | |
- | FL ----------- FL |
- | FR ----------- FR |
- +--------+ +---------+
-```
-
-None of these nodes is a driver so there is no driver in the group and nothing
-will be scheduled.
-
-When one of the nodes has `node.want-driver = true` they are grouped and
-scheduled with a random driver node. This is often the driver node with the
-highest priority (usually the Dummy-Driver) or otherwise a driver that is already
-scheduling some other nodes.
-
-## Always process nodes
-
-A simple node, unlinked to anything should normally not run.
-
-```
- +--------+
- | player |
- | |
- | FL
- | FR
- +--------+
-```
-
-When the `node.always-process = true` property is set, the node will however be
-made runnable even if unlinked. This is done by adding the node to a random driver.
-
-`node.always-process = true` implies the `node.want-driver = true` property.
-
-## Sync groups
-
-In some cases, you only want to group nodes together depending on some condition.
-
-For example, when the JACK transport is activated, all nodes in the graph should share
-the same driver node, regardless of the grouping or linking of the nodes.
-
-This is done by setting the same node.sync-group property on all nodes (by default all
-nodes have `node.sync-group = group.sync.0`). When a node sets `node.sync = true` all
-the other nodes with the same `node.sync-group` property are grouped together.
-
-This can be used to implement the JACK transport. When the transport is started, the
-`node.sync=true` property is set and all nodes join one group with a shared driver
-and timing information.
-
-
-
-
-*/
-
-
diff --git a/doc/dox/internals/scheduling.dox b/doc/dox/internals/scheduling.dox
index c74124480..38b05596b 100644
--- a/doc/dox/internals/scheduling.dox
+++ b/doc/dox/internals/scheduling.dox
@@ -23,9 +23,6 @@ node is scheduled to run.
This document describes the processing that happens in the data processing
thread after the main thread has configured it.
-Before scheduling of the node happens, the scheduler will collect a list of
-nodes that are runnable, see \ref page_running
-
# Nodes
Nodes are objects with 0 or more input and output ports.
diff --git a/doc/dox/modules.dox b/doc/dox/modules.dox
index 85b0a1418..4e9358197 100644
--- a/doc/dox/modules.dox
+++ b/doc/dox/modules.dox
@@ -81,7 +81,6 @@ List of known modules:
- \subpage page_module_raop_discover
- \subpage page_module_roc_sink
- \subpage page_module_roc_source
-- \subpage page_module_scheduler_v1
- \subpage page_module_rtp_sap
- \subpage page_module_rtp_sink
- \subpage page_module_rtp_source
@@ -91,8 +90,6 @@ List of known modules:
- \subpage page_module_spa_node_factory
- \subpage page_module_spa_device
- \subpage page_module_spa_device_factory
-- \subpage page_module_sendspin_recv
-- \subpage page_module_sendspin_send
- \subpage page_module_session_manager
- \subpage page_module_snapcast_discover
- \subpage page_module_vban_recv
diff --git a/doc/dox/overview.dox b/doc/dox/overview.dox
index 9e2530163..1490cd444 100644
--- a/doc/dox/overview.dox
+++ b/doc/dox/overview.dox
@@ -77,7 +77,7 @@ Certain properties are, by convention, expected for specific object types.
Each object type has a list of methods that it needs to implement.
-The session manager is responsible for defining the list of permissions each client has. Each permission entry is an object ID and five flags. The five flags are:
+The session manager is responsible for defining the list of permissions each client has. Each permission entry is an object ID and four flags. The four flags are:
- Read: the object can be seen and events can be received;
- Write: the object can be modified, usually through methods (which requires the execute flag)
@@ -109,7 +109,7 @@ Modules in PipeWire can only be loaded in their own process. A client, for examp
Nodes are the core data processing entities in PipeWire.
They may produce data (capture devices, signal generators, ...), consume data (playback devices, network endpoints, ...) or both (filters).
-Nodes have a method `process`, which eats up data from input ports and provides data for each output port.
+Notes have a method `process`, which eats up data from input ports and provides data for each output port.
#### Ports
diff --git a/doc/dox/programs/index.md b/doc/dox/programs/index.md
index 01ba07720..991ec7263 100644
--- a/doc/dox/programs/index.md
+++ b/doc/dox/programs/index.md
@@ -4,7 +4,6 @@ Manual pages:
- \subpage page_man_pipewire_1
- \subpage page_man_pipewire-pulse_1
-- \subpage page_man_pw-audioconvert_1
- \subpage page_man_pw-cat_1
- \subpage page_man_pw-cli_1
- \subpage page_man_pw-config_1
diff --git a/doc/dox/programs/pw-audioconvert.1.md b/doc/dox/programs/pw-audioconvert.1.md
deleted file mode 100644
index e78e4c7ec..000000000
--- a/doc/dox/programs/pw-audioconvert.1.md
+++ /dev/null
@@ -1,62 +0,0 @@
-\page page_man_pw-audioconvert_1 pw-audioconvert
-
-The PipeWire audioconvert utility
-
-# SYNOPSIS
-
-**pw-audioconvert** \[*OPTIONS*\] *INFILE* *OUTFILE*
-
-# DESCRIPTION
-
-Use the PipeWire audioconvert to convert input file to output file,
-following the given options.
-
-This is useful only for doing audio conversion but also apply effects
-on the audio using a filter-graph.
-
-It understands all audio file formats supported by `libsndfile` for input
-and output. The filename extension is used to guess the output file
-container and format with the WAV file format as the default.
-
-# OPTIONS
-
-\par -r RATE | \--rate=RATE
-Output sample rate. Default the same as the input sample rate.
-
-\par -f FORMAT | \--format=FORMAT
-Output sample format (s8 | s16 | s32 | f32 | f64). Default the same
-as the input format.
-
-\par -b BLOCKSIZE | \--blocksize=BLOCKSIZE
-Number of samples per iteration (default 4096)
-
-\par -P PROPERTIES | \--properties=PROPERTIES
-Set extra stream properties as a JSON object. One can also use @filename to
-read the JSON object with properties from filename.
-
-\par -c CHANNELS | \--channels=CHANNELS
-The number of output channels, default the same as the input.
-
-\par \--channel-map=VALUE
-The channelmap. Possible values include are either a predefined channel layout
-such as **Mono**, **Stereo**, **2.1**, **Quad**, **2.2**, **5.1**,
-or comma separated array of channel names such as **FL,FR**.
-
-\par -h
-Show help.
-
-\par -v
-Verbose operation.
-
-# EXAMPLES
-
-**pw-audioconvert** -r 48000 -f s32 in.wav out.wav
-
-# AUTHORS
-
-The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
-PipeWire is available from <$(PACKAGE_URL)>
-
-# SEE ALSO
-
-\ref page_man_pipewire_1 "pipewire(1)"
diff --git a/doc/dox/programs/pw-cat.1.md b/doc/dox/programs/pw-cat.1.md
index 8ec02c711..b681e54a1 100644
--- a/doc/dox/programs/pw-cat.1.md
+++ b/doc/dox/programs/pw-cat.1.md
@@ -124,9 +124,6 @@ Set a node target (default auto). The value can be:
- \: The object.serial or the node.name of a target node
\endparblock
-\par -C | \--monitor
-In recording mode, record from monitor ports.
-
\par \--latency=VALUE\[*units*\]
\parblock
Set the node latency (default 100ms)
diff --git a/doc/dox/programs/pw-top.1.md b/doc/dox/programs/pw-top.1.md
index 353015d1a..1207e3205 100644
--- a/doc/dox/programs/pw-top.1.md
+++ b/doc/dox/programs/pw-top.1.md
@@ -188,11 +188,6 @@ Quit
Clear the ERR counters. This does *not* clear the counters globally,
it will only reset the counters in this instance of *pw-top*.
-\par [f|F]
-Cycle through filter presets. If any nodes are filtered from view,
-the current preset will be indicated in the header bar. Only nodes
-with the indicated state or higher, will be printed.
-
# OPTIONS
\par -h | \--help
@@ -204,9 +199,6 @@ Run in non-interactive batch mode, similar to top\'s batch mode.
\par -n | \--iterations=NUMBER
Exit after NUMBER of batch iterations. Only used in batch mode.
-\par -f | \--filter=NUMBER
-Start with filter preset NUMBER selected.
-
\par -r | \--remote=NAME
The name the *remote* instance to monitor. If left unspecified, a
connection is made to the default PipeWire instance.
diff --git a/doc/meson.build b/doc/meson.build
index 645b4b26a..f4aa4ba6a 100644
--- a/doc/meson.build
+++ b/doc/meson.build
@@ -67,7 +67,6 @@ extra_docs = [
'dox/internals/session-manager.dox',
'dox/internals/objects.dox',
'dox/internals/audio.dox',
- 'dox/internals/running.dox',
'dox/internals/scheduling.dox',
'dox/internals/driver.dox',
'dox/internals/protocol.dox',
@@ -100,7 +99,6 @@ manpage_docs = [
'dox/config/libpipewire-modules.7.md',
'dox/programs/pipewire-pulse.1.md',
'dox/programs/pipewire.1.md',
- 'dox/programs/pw-audioconvert.1.md',
'dox/programs/pw-cat.1.md',
'dox/programs/pw-cli.1.md',
'dox/programs/pw-config.1.md',
diff --git a/meson.build b/meson.build
index 37badca51..536502a68 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
project('pipewire', ['c' ],
- version : '1.7.0',
+ version : '1.6.2',
license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
meson_version : '>= 0.61.1',
default_options : [ 'warning_level=3',
@@ -368,7 +368,7 @@ cdata.set('HAVE_OPUS', opus_dep.found())
summary({'readline (for pw-cli)': readline_dep.found()}, bool_yn: true, section: 'Misc dependencies')
cdata.set('HAVE_READLINE', readline_dep.found())
ncurses_dep = dependency('ncursesw', required : false)
-sndfile_dep = dependency('sndfile', version : '>= 1.1.0', required : get_option('sndfile'))
+sndfile_dep = dependency('sndfile', version : '>= 1.0.20', required : get_option('sndfile'))
summary({'sndfile': sndfile_dep.found()}, bool_yn: true, section: 'pw-cat/pw-play/pw-dump/filter-chain')
cdata.set('HAVE_SNDFILE', sndfile_dep.found())
pulseaudio_dep = dependency('libpulse', required : get_option('libpulse'))
@@ -412,7 +412,7 @@ gst_deps_def = {
'gio-unix-2.0': {},
'gstreamer-1.0': {'version': '>= 1.10.0'},
'gstreamer-base-1.0': {},
- 'gstreamer-video-1.0': {'version': '>= 1.22.0'},
+ 'gstreamer-video-1.0': {},
'gstreamer-audio-1.0': {},
'gstreamer-allocators-1.0': {},
}
diff --git a/meson_options.txt b/meson_options.txt
index 7c8cc15ba..206d68659 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -294,10 +294,6 @@ option('legacy-rtkit',
description: 'Build legacy rtkit module',
type: 'boolean',
value: true)
-option('avb-virtual',
- description: 'Enable AVB Virtual code for testing',
- type: 'feature',
- value: 'disabled')
option('avb',
description: 'Enable AVB code',
type: 'feature',
diff --git a/pipewire-jack/src/pipewire-jack.c b/pipewire-jack/src/pipewire-jack.c
index a84e4254c..c7017e936 100644
--- a/pipewire-jack/src/pipewire-jack.c
+++ b/pipewire-jack/src/pipewire-jack.c
@@ -73,6 +73,7 @@ PW_LOG_TOPIC_STATIC(jack_log_topic, "jack");
#define TYPE_ID_IS_EVENT(t) ((t) >= TYPE_ID_MIDI && (t) <= TYPE_ID_UMP)
#define TYPE_ID_CAN_OSC(t) ((t) == TYPE_ID_MIDI || (t) == TYPE_ID_OSC)
+#define TYPE_ID_IS_HIDDEN(t) ((t) >= TYPE_ID_OTHER)
#define TYPE_ID_IS_COMPATIBLE(a,b)(((a) == (b)) || (TYPE_ID_IS_EVENT(a) && TYPE_ID_IS_EVENT(b)))
#define SELF_CONNECT_ALLOW 0
@@ -85,7 +86,7 @@ PW_LOG_TOPIC_STATIC(jack_log_topic, "jack");
#define OTHER_CONNECT_FAIL -1
#define OTHER_CONNECT_IGNORE 0
-#define NOTIFY_BUFFER_SIZE (1u<<16)
+#define NOTIFY_BUFFER_SIZE (1u<<13)
#define NOTIFY_BUFFER_MASK (NOTIFY_BUFFER_SIZE-1)
struct notify {
@@ -103,8 +104,8 @@ struct notify {
#define NOTIFY_TYPE_TOTAL_LATENCY ((9<<4)|NOTIFY_ACTIVE_FLAG)
#define NOTIFY_TYPE_PORT_RENAME ((10<<4)|NOTIFY_ACTIVE_FLAG)
int type;
- int arg1;
struct object *object;
+ int arg1;
const char *msg;
};
@@ -1447,9 +1448,8 @@ static size_t convert_from_event(void *midi, void *buffer, size_t size, uint32_t
switch (type) {
case TYPE_ID_MIDI:
- event_type = SPA_CONTROL_Midi;
- break;
case TYPE_ID_OSC:
+ /* we handle MIDI as OSC, check below */
event_type = SPA_CONTROL_OSC;
break;
case TYPE_ID_UMP:
@@ -1466,15 +1466,27 @@ static size_t convert_from_event(void *midi, void *buffer, size_t size, uint32_t
for (i = 0; i < count; i++) {
jack_midi_event_t ev;
jack_midi_event_get(&ev, midi, i);
- uint32_t ev_type;
- if (type == TYPE_ID_MIDI && is_osc(&ev))
- ev_type = SPA_CONTROL_OSC;
- else
- ev_type = event_type;
+ if (type != TYPE_ID_MIDI || is_osc(&ev)) {
+ /* no midi port or it's OSC */
+ spa_pod_builder_control(&b, ev.time, event_type);
+ spa_pod_builder_bytes(&b, ev.buffer, ev.size);
+ } else {
+ /* midi port and it's not OSC, convert to UMP */
+ uint8_t *data = ev.buffer;
+ size_t size = ev.size;
+ uint64_t state = 0;
- spa_pod_builder_control(&b, ev.time, ev_type);
- spa_pod_builder_bytes(&b, ev.buffer, ev.size);
+ while (size > 0) {
+ uint32_t ump[4];
+ int ump_size = spa_ump_from_midi(&data, &size,
+ ump, sizeof(ump), 0, &state);
+ if (ump_size <= 0)
+ break;
+ spa_pod_builder_control(&b, ev.time, SPA_CONTROL_UMP);
+ spa_pod_builder_bytes(&b, ump, ump_size);
+ }
+ }
}
spa_pod_builder_pop(&b, &f);
return b.state.offset;
@@ -1766,7 +1778,7 @@ static inline void process_empty(struct port *p, uint32_t frames)
case TYPE_ID_AUDIO:
ptr = get_buffer_output(p, frames, sizeof(float), NULL);
if (SPA_LIKELY(ptr != NULL))
- spa_memcpy(ptr, src, frames * sizeof(float));
+ memcpy(ptr, src, frames * sizeof(float));
break;
case TYPE_ID_MIDI:
case TYPE_ID_OSC:
@@ -1780,7 +1792,7 @@ static inline void process_empty(struct port *p, uint32_t frames)
* to do this concurrently */
b->datas[0].chunk->size = convert_from_event(src, midi_scratch,
MIDI_SCRATCH_FRAMES * sizeof(float), type);
- spa_memcpy(ptr, midi_scratch, b->datas[0].chunk->size);
+ memcpy(ptr, midi_scratch, b->datas[0].chunk->size);
}
break;
}
@@ -2198,7 +2210,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask)
}
} else if (SPA_LIKELY(mask & SPA_IO_IN)) {
uint32_t buffer_frames;
- int status = -EBUSY;
+ int status = 0;
buffer_frames = cycle_run(c);
@@ -3893,9 +3905,8 @@ static void registry_event_global(void *data, uint32_t id,
const char *name;
if ((str = spa_dict_lookup(props, PW_KEY_FORMAT_DSP)) == NULL)
- goto exit;
- if ((type_id = string_to_type(str)) == SPA_ID_INVALID ||
- !type_is_dsp(type_id))
+ str = "other";
+ if ((type_id = string_to_type(str)) == SPA_ID_INVALID)
goto exit;
if ((str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL)
@@ -4864,7 +4875,7 @@ int jack_activate (jack_client_t *client)
freeze_callbacks(c);
/* reemit buffer_frames */
- c->buffer_frames = (uint32_t)-1;
+ c->buffer_frames = 0;
pw_data_loop_start(c->loop);
c->active = true;
@@ -5445,7 +5456,7 @@ SPA_EXPORT
jack_nframes_t jack_get_buffer_size (jack_client_t *client)
{
struct client *c = (struct client *) client;
- uint32_t res = -1;
+ jack_nframes_t res = -1;
return_val_if_fail(c != NULL, 0);
@@ -5462,7 +5473,7 @@ jack_nframes_t jack_get_buffer_size (jack_client_t *client)
}
c->buffer_frames = res;
pw_log_debug("buffer_frames: %u", res);
- return (jack_nframes_t)res;
+ return res;
}
SPA_EXPORT
@@ -5531,8 +5542,7 @@ jack_port_t * jack_port_register (jack_client_t *client,
return NULL;
}
- if ((type_id = string_to_type(port_type)) == SPA_ID_INVALID ||
- !type_is_dsp(type_id)) {
+ if ((type_id = string_to_type(port_type)) == SPA_ID_INVALID) {
pw_log_warn("unknown port type %s", port_type);
return NULL;
}
@@ -5851,11 +5861,11 @@ static void *get_buffer_input_midi(struct port *p, jack_nframes_t frames)
* the per port buffer. This makes it possible to call this function concurrently
* but also have different pointers per port */
convert_to_event(mix_info, n_mix_info, mb, p->client->fix_midi_events, p->object->port.type_id);
- spa_memcpy(ptr, mb, sizeof(struct midi_buffer) + (mb->event_count
+ memcpy(ptr, mb, sizeof(struct midi_buffer) + (mb->event_count
* sizeof(struct midi_event)));
if (mb->write_pos > 0) {
size_t offs = mb->buffer_size - mb->write_pos;
- spa_memcpy(SPA_PTROFF(ptr, offs, void), SPA_PTROFF(mb, offs, void), mb->write_pos);
+ memcpy(SPA_PTROFF(ptr, offs, void), SPA_PTROFF(mb, offs, void), mb->write_pos);
}
return ptr;
}
@@ -6514,10 +6524,10 @@ int jack_connect (jack_client_t *client,
if ((res = check_connect(c, src, dst)) != 1)
goto exit;
- snprintf(val[0], sizeof(val[0]), "%u", src->port.node_id);
- snprintf(val[1], sizeof(val[1]), "%u", src->id);
- snprintf(val[2], sizeof(val[2]), "%u", dst->port.node_id);
- snprintf(val[3], sizeof(val[3]), "%u", dst->id);
+ snprintf(val[0], sizeof(val[0]), "%d", src->port.node_id);
+ snprintf(val[1], sizeof(val[1]), "%d", src->id);
+ snprintf(val[2], sizeof(val[2]), "%d", dst->port.node_id);
+ snprintf(val[3], sizeof(val[3]), "%d", dst->id);
props = SPA_DICT_INIT(items, 0);
items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_OUTPUT_NODE, val[0]);
@@ -6936,6 +6946,8 @@ const char ** jack_get_ports (jack_client_t *client,
continue;
pw_log_debug("%p: check port type:%d flags:%08lx name:\"%s\"", c,
o->port.type_id, o->port.flags, o->port.name);
+ if (TYPE_ID_IS_HIDDEN(o->port.type_id))
+ continue;
if (!SPA_FLAG_IS_SET(o->port.flags, flags))
continue;
if (str != NULL && o->port.node != NULL) {
diff --git a/pipewire-jack/src/ringbuffer.c b/pipewire-jack/src/ringbuffer.c
index be4014c23..ba1cc23f3 100644
--- a/pipewire-jack/src/ringbuffer.c
+++ b/pipewire-jack/src/ringbuffer.c
@@ -132,10 +132,10 @@ size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt)
n2 = 0;
}
- spa_memcpy (dest, &(rb->buf[rb->read_ptr]), n1);
+ memcpy (dest, &(rb->buf[rb->read_ptr]), n1);
rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask;
if (n2) {
- spa_memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2);
+ memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2);
rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask;
}
return to_read;
@@ -167,11 +167,11 @@ size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt)
n2 = 0;
}
- spa_memcpy (dest, &(rb->buf[tmp_read_ptr]), n1);
+ memcpy (dest, &(rb->buf[tmp_read_ptr]), n1);
tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask;
if (n2)
- spa_memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2);
+ memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2);
return to_read;
}
@@ -249,10 +249,10 @@ size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src,
n2 = 0;
}
- spa_memcpy (&(rb->buf[rb->write_ptr]), src, n1);
+ memcpy (&(rb->buf[rb->write_ptr]), src, n1);
rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask;
if (n2) {
- spa_memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2);
+ memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2);
rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask;
}
return to_write;
diff --git a/pipewire-v4l2/src/pipewire-v4l2.c b/pipewire-v4l2/src/pipewire-v4l2.c
index 7a5e5c057..8fc07151a 100644
--- a/pipewire-v4l2/src/pipewire-v4l2.c
+++ b/pipewire-v4l2/src/pipewire-v4l2.c
@@ -2570,10 +2570,7 @@ static void *v4l2_mmap(void *addr, size_t length, int prot,
buf = &file->buffers[id];
data = &buf->buf->buffer->datas[0];
- if (pw_map_range_init(&range, data->mapoffset, data->maxsize, 1024) < 0) {
- res = MAP_FAILED;
- goto error_unlock;
- }
+ pw_map_range_init(&range, data->mapoffset, data->maxsize, 1024);
if (!SPA_FLAG_IS_SET(data->flags, SPA_DATA_FLAG_READABLE))
prot &= ~PROT_READ;
diff --git a/po/LINGUAS b/po/LINGUAS
index f5ef59cf7..8ebdbea2e 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -7,10 +7,9 @@ bn_IN
ca
cs
da
-de
de_CH
+de
el
-eo
es
fi
fr
@@ -37,15 +36,14 @@ oc
or
pa
pl
-pt
pt_BR
+pt
ro
ru
-si
sk
sl
-sr
sr@latin
+sr
sv
ta
te
@@ -53,3 +51,5 @@ tr
uk
zh_CN
zh_TW
+eo
+si
diff --git a/po/kk.po b/po/kk.po
index 507c967e0..6a00211f3 100644
--- a/po/kk.po
+++ b/po/kk.po
@@ -1,14 +1,15 @@
# Kazakh translation of pipewire.
# Copyright (C) 2020 The pipewire authors.
# This file is distributed under the same license as the pipewire package.
-# Baurzhan Muftakhidinov , 2020-2026.
+# Baurzhan Muftakhidinov , 2020.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
-"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues\n"
-"POT-Creation-Date: 2026-03-09 12:19+0000\n"
-"PO-Revision-Date: 2026-03-17 00:04+0500\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
+"issues/new\n"
+"POT-Creation-Date: 2021-04-18 16:54+0800\n"
+"PO-Revision-Date: 2020-06-30 08:04+0500\n"
"Last-Translator: Baurzhan Muftakhidinov \n"
"Language-Team: \n"
"Language: kk\n"
@@ -16,199 +17,96 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Poedit 3.9\n"
+"X-Generator: Poedit 2.3.1\n"
-#: src/daemon/pipewire.c:29
+#: src/daemon/pipewire.c:43
#, c-format
msgid ""
"%s [options]\n"
" -h, --help Show this help\n"
-" -v, --verbose Increase verbosity by one level\n"
" --version Show version\n"
" -c, --config Load config (Default %s)\n"
-" -P --properties Set context properties\n"
msgstr ""
-"%s [опциялар]\n"
-" -h, --help Осы көмекті көрсету\n"
-" -v, --verbose Ақпараттылығын бір деңгейге арттыру\n"
-" --version Нұсқасын көрсету\n"
-" -c, --config Конфигурацияны жүктеу (Бастапқы %s)\n"
-" -P --properties Контекст қасиеттерін орнату\n"
-
-#: src/daemon/pipewire.desktop.in:3
-msgid "PipeWire Media System"
-msgstr "PipeWire медиа жүйесі"
#: src/daemon/pipewire.desktop.in:4
+msgid "PipeWire Media System"
+msgstr ""
+
+#: src/daemon/pipewire.desktop.in:5
msgid "Start the PipeWire Media System"
-msgstr "PipeWire медиа жүйесін іске қосу"
+msgstr ""
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
-#, c-format
-msgid "Tunnel to %s%s%s"
-msgstr "%s%s%s бағытына туннель"
+#: src/examples/media-session/alsa-monitor.c:526
+#: spa/plugins/alsa/acp/compat.c:187
+msgid "Built-in Audio"
+msgstr "Құрамындағы аудио"
-#: src/modules/module-fallback-sink.c:40
-msgid "Dummy Output"
-msgstr "Жалған шығыс"
+#: src/examples/media-session/alsa-monitor.c:530
+#: spa/plugins/alsa/acp/compat.c:192
+msgid "Modem"
+msgstr "Модем"
-#: src/modules/module-pulse-tunnel.c:761
-#, c-format
-msgid "Tunnel for %s@%s"
-msgstr "%s@%s үшін туннель"
-
-#: src/modules/module-zeroconf-discover.c:290
+#: src/examples/media-session/alsa-monitor.c:539
msgid "Unknown device"
-msgstr "Белгісіз құрылғы"
+msgstr ""
-#: src/modules/module-zeroconf-discover.c:302
-#, c-format
-msgid "%s on %s@%s"
-msgstr "%s, %s@%s ішінде"
-
-#: src/modules/module-zeroconf-discover.c:306
-#, c-format
-msgid "%s on %s"
-msgstr "%s, %s ішінде"
-
-#: src/tools/pw-cat.c:269
-#, c-format
-msgid "Supported formats:\n"
-msgstr "Қолдау көрсетілетін пішімдер:\n"
-
-#: src/tools/pw-cat.c:754
-#, c-format
-msgid "Supported channel layouts:\n"
-msgstr "Қолдау көрсетілетін арна жаймалары:\n"
-
-#: src/tools/pw-cat.c:764
-#, c-format
-msgid "Supported channel layout aliases:\n"
-msgstr "Қолдау көрсетілетін арна жаймаларының алиастары:\n"
-
-#: src/tools/pw-cat.c:766
-#, c-format
-msgid " %s -> %s\n"
-msgstr " %s -> %s\n"
-
-#: src/tools/pw-cat.c:771
-#, c-format
-msgid "Supported channel names:\n"
-msgstr "Қолдау көрсетілетін арна атаулары:\n"
-
-#: src/tools/pw-cat.c:1182
+#: src/tools/pw-cat.c:991
#, c-format
msgid ""
-"%s [options] [|-]\n"
+"%s [options] \n"
" -h, --help Show this help\n"
" --version Show version\n"
" -v, --verbose Enable verbose operations\n"
"\n"
msgstr ""
-"%s [опциялар] [<файл>|-]\n"
-" -h, --help Осы көмекті көрсету\n"
-" --version Нұсқасын көрсету\n"
-" -v, --verbose Толық ақпаратты іске қосу\n"
-"\n"
-#: src/tools/pw-cat.c:1189
+#: src/tools/pw-cat.c:998
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
" --media-type Set media type (default %s)\n"
" --media-category Set media category (default %s)\n"
" --media-role Set media role (default %s)\n"
-" --target Set node target serial or name (default %s)\n"
+" --target Set node target (default %s)\n"
" 0 means don't link\n"
" --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n"
-" the rate is the one of the source file\n"
-" -P --properties Set node properties\n"
+" the rate is the one of the source "
+"file\n"
+" --list-targets List available targets for --target\n"
"\n"
msgstr ""
-" -R, --remote Қашықтағы қызмет атауы\n"
-" --media-type Медиа түрін орнату (бастапқы %s)\n"
-" --media-category Медиа категориясын орнату (бастапқы %s)\n"
-" --media-role Медиа рөлін орнату (бастапқы %s)\n"
-" --target Түйін мақсатының сериялық нөмірін немесе атын орнату (бастапқы "
-"%s)\n"
-" 0 байланыстырмауды білдіреді\n"
-" --latency Түйін кідірісін орнату (бастапқы %s)\n"
-" Xюнит (юнит = с, мс, мкс, нс)\n"
-" немесе тікелей үлгілер (256)\n"
-" жиілік бастапқы файлдың жиілігі болып табылады\n"
-" -P --properties Түйін қасиеттерін орнату\n"
-"\n"
-#: src/tools/pw-cat.c:1207
+#: src/tools/pw-cat.c:1016
#, c-format
msgid ""
-" --rate Sample rate (default %u)\n"
-" --channels Number of channels (default %u)\n"
+" --rate Sample rate (req. for rec) (default "
+"%u)\n"
+" --channels Number of channels (req. for rec) "
+"(default %u)\n"
" --channel-map Channel map\n"
-" a channel layout: \"Stereo\", \"5.1\",... or\n"
-" comma separated list of channel names: eg. \"FL,FR\"\n"
-" --list-layouts List supported channel layouts\n"
-" --list-channel-names List supported channel maps\n"
-" --format Sample format (default %s)\n"
-" --list-formats List supported sample formats\n"
-" --container Container format\n"
-" --list-containers List supported containers and extensions\n"
+" one of: \"stereo\", "
+"\"surround-51\",... or\n"
+" comma separated list of channel "
+"names: eg. \"FL,FR\"\n"
+" --format Sample format %s (req. for rec) "
+"(default %s)\n"
" --volume Stream volume 0-1.0 (default %.3f)\n"
-" -q --quality Resampler quality (0 - 15) (default %d)\n"
-" -a, --raw RAW mode\n"
-" -M, --force-midi Force midi format, one of \"midi\" or \"ump\", (default ump)\n"
-" -n, --sample-count COUNT Stop after COUNT samples\n"
+" -q --quality Resampler quality (0 - 15) (default "
+"%d)\n"
"\n"
msgstr ""
-" --rate Дискреттеу жиілігі (бастапқы %u)\n"
-" --channels Арналар саны (бастапқы %u)\n"
-" --channel-map Арналар картасы\n"
-" арна жаймасы: «Stereo», «5.1»,... немесе\n"
-" үтірмен ажыратылған арна атауларының тізімі: мысалы, "
-"«FL,FR»\n"
-" --list-layouts Қолдау көрсетілетін арна жаймаларын тізімдеу\n"
-" --list-channel-names Қолдау көрсетілетін арналар картасын тізімдеу\n"
-" --format Дискреттеу пішімі (бастапқы %s)\n"
-" --list-formats Қолдау көрсетілетін дискреттеу пішімдерін тізімдеу\n"
-" --container Контейнер пішімі\n"
-" --list-containers Қолдау көрсетілетін контейнерлер мен кеңейтулерді тізімдеу\n"
-" --volume Ағын дыбыс деңгейі 0-1.0 (бастапқы %.3f)\n"
-" -q --quality Қайта дискреттеу сапасы (0 - 15) (бастапқы %d)\n"
-" -a, --raw RAW режимі\n"
-" -M, --force-midi Midi пішімін мәжбүрлеу, «midi» немесе «ump» біреуі, (бастапқы "
-"ump)\n"
-" -n, --sample-count COUNT COUNT үлгісінен кейін тоқтату\n"
-"\n"
-#: src/tools/pw-cat.c:1232
+#: src/tools/pw-cat.c:1033
msgid ""
" -p, --playback Playback mode\n"
" -r, --record Recording mode\n"
" -m, --midi Midi mode\n"
-" -d, --dsd DSD mode\n"
-" -o, --encoded Encoded mode\n"
-" -s, --sysex SysEx mode\n"
-" -c, --midi-clip MIDI clip mode\n"
"\n"
msgstr ""
-" -p, --playback Ойнату режимі\n"
-" -r, --record Жазу режимі\n"
-" -m, --midi Midi режимі\n"
-" -d, --dsd DSD режимі\n"
-" -o, --encoded Шифрленген режим\n"
-" -s, --sysex SysEx режимі\n"
-" -c, --midi-clip MIDI clip режимі\n"
-"\n"
-#: src/tools/pw-cat.c:1837
-#, c-format
-msgid "Supported containers and extensions:\n"
-msgstr "Қолдау көрсетілетін контейнерлер мен кеңейтулер:\n"
-
-#: src/tools/pw-cli.c:2386
+#: src/tools/pw-cli.c:2932
#, c-format
msgid ""
"%s [options] [command]\n"
@@ -216,506 +114,465 @@ msgid ""
" --version Show version\n"
" -d, --daemon Start as daemon (Default false)\n"
" -r, --remote Remote daemon name\n"
-" -m, --monitor Monitor activity\n"
"\n"
msgstr ""
-"%s [опциялар] [команда]\n"
-" -h, --help Осы көмекті көрсету\n"
-" --version Нұсқасын көрсету\n"
-" -d, --daemon Қызмет ретінде іске қосу (Бастапқы false)\n"
-" -r, --remote Қашықтағы қызмет атауы\n"
-" -m, --monitor Белсенділікті бақылау\n"
-"\n"
-#: spa/plugins/alsa/acp/acp.c:361
+#: spa/plugins/alsa/acp/acp.c:290
msgid "Pro Audio"
-msgstr "Кәсіби аудио"
+msgstr ""
-#: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
-#: spa/plugins/bluez5/bluez5-device.c:2021
+#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704
+#: spa/plugins/bluez5/bluez5-device.c:1000
msgid "Off"
msgstr "Сөнд."
-#: spa/plugins/alsa/acp/acp.c:618
-#, c-format
-msgid "%s [ALSA UCM error]"
-msgstr "%s [ALSA UCM қатесі]"
+#: spa/plugins/alsa/acp/channelmap.h:466
+msgid "(invalid)"
+msgstr "(жарамсыз)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2721
+#: spa/plugins/alsa/acp/alsa-mixer.c:2709
msgid "Input"
msgstr "Кіріс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2722
+#: spa/plugins/alsa/acp/alsa-mixer.c:2710
msgid "Docking Station Input"
msgstr "Док-станция кірісі"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2723
+#: spa/plugins/alsa/acp/alsa-mixer.c:2711
msgid "Docking Station Microphone"
msgstr "Док-станция микрофоны"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2724
+#: spa/plugins/alsa/acp/alsa-mixer.c:2712
msgid "Docking Station Line In"
msgstr "Док-станцияның сызықтық кірісі"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2725 spa/plugins/alsa/acp/alsa-mixer.c:2816
+#: spa/plugins/alsa/acp/alsa-mixer.c:2713
+#: spa/plugins/alsa/acp/alsa-mixer.c:2804
msgid "Line In"
msgstr "Сызықтық кіріс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2726 spa/plugins/alsa/acp/alsa-mixer.c:2810
-#: spa/plugins/bluez5/bluez5-device.c:2422
+#: spa/plugins/alsa/acp/alsa-mixer.c:2714
+#: spa/plugins/alsa/acp/alsa-mixer.c:2798
+#: spa/plugins/bluez5/bluez5-device.c:1145
msgid "Microphone"
msgstr "Микрофон"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2727 spa/plugins/alsa/acp/alsa-mixer.c:2811
+#: spa/plugins/alsa/acp/alsa-mixer.c:2715
+#: spa/plugins/alsa/acp/alsa-mixer.c:2799
msgid "Front Microphone"
msgstr "Алдыңғы микрофон"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2728 spa/plugins/alsa/acp/alsa-mixer.c:2812
+#: spa/plugins/alsa/acp/alsa-mixer.c:2716
+#: spa/plugins/alsa/acp/alsa-mixer.c:2800
msgid "Rear Microphone"
msgstr "Артқы микрофон"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2729
+#: spa/plugins/alsa/acp/alsa-mixer.c:2717
msgid "External Microphone"
msgstr "Сыртқы микрофон"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2730 spa/plugins/alsa/acp/alsa-mixer.c:2814
+#: spa/plugins/alsa/acp/alsa-mixer.c:2718
+#: spa/plugins/alsa/acp/alsa-mixer.c:2802
msgid "Internal Microphone"
msgstr "Ішкі микрофон"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2731 spa/plugins/alsa/acp/alsa-mixer.c:2817
+#: spa/plugins/alsa/acp/alsa-mixer.c:2719
+#: spa/plugins/alsa/acp/alsa-mixer.c:2805
msgid "Radio"
msgstr "Радио"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2732 spa/plugins/alsa/acp/alsa-mixer.c:2818
+#: spa/plugins/alsa/acp/alsa-mixer.c:2720
+#: spa/plugins/alsa/acp/alsa-mixer.c:2806
msgid "Video"
msgstr "Видео"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2733
+#: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Automatic Gain Control"
msgstr "Күшейтуді автореттеу"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2734
+#: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "No Automatic Gain Control"
msgstr "Күшейтуді автореттеу жоқ"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2735
+#: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Boost"
msgstr "Күшейту"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2736
+#: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "No Boost"
msgstr "Күшейту жоқ"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2737
+#: spa/plugins/alsa/acp/alsa-mixer.c:2725
msgid "Amplifier"
msgstr "Күшейткіш"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2738
+#: spa/plugins/alsa/acp/alsa-mixer.c:2726
msgid "No Amplifier"
msgstr "Күшейткіш жоқ"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2739
+#: spa/plugins/alsa/acp/alsa-mixer.c:2727
msgid "Bass Boost"
msgstr "Бас күшейту"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2740
+#: spa/plugins/alsa/acp/alsa-mixer.c:2728
msgid "No Bass Boost"
msgstr "Бас күшейту жоқ"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2741 spa/plugins/bluez5/bluez5-device.c:2428
+#: spa/plugins/alsa/acp/alsa-mixer.c:2729
+#: spa/plugins/bluez5/bluez5-device.c:1150
msgid "Speaker"
msgstr "Динамик"
-#. Don't call it "headset", the HF one has the mic
-#: spa/plugins/alsa/acp/alsa-mixer.c:2742 spa/plugins/alsa/acp/alsa-mixer.c:2820
-#: spa/plugins/bluez5/bluez5-device.c:2434 spa/plugins/bluez5/bluez5-device.c:2501
+#: spa/plugins/alsa/acp/alsa-mixer.c:2730
+#: spa/plugins/alsa/acp/alsa-mixer.c:2808
msgid "Headphones"
msgstr "Құлаққаптар"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2809
+#: spa/plugins/alsa/acp/alsa-mixer.c:2797
msgid "Analog Input"
msgstr "Аналогтық кіріс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2813
+#: spa/plugins/alsa/acp/alsa-mixer.c:2801
msgid "Dock Microphone"
msgstr "Док-станция микрофоны"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2815
+#: spa/plugins/alsa/acp/alsa-mixer.c:2803
msgid "Headset Microphone"
msgstr "Гарнитура микрофоны"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2819
+#: spa/plugins/alsa/acp/alsa-mixer.c:2807
msgid "Analog Output"
msgstr "Аналогтық шығыс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2821
+#: spa/plugins/alsa/acp/alsa-mixer.c:2809
+#, fuzzy
msgid "Headphones 2"
-msgstr "Құлаққап 2"
+msgstr "Құлаққаптар"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2822
+#: spa/plugins/alsa/acp/alsa-mixer.c:2810
msgid "Headphones Mono Output"
msgstr "Құлаққаптардың моно шығысы"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2823
+#: spa/plugins/alsa/acp/alsa-mixer.c:2811
msgid "Line Out"
msgstr "Сызықтық шығыс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2824
+#: spa/plugins/alsa/acp/alsa-mixer.c:2812
msgid "Analog Mono Output"
msgstr "Аналогтық моно шығысы"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2825
+#: spa/plugins/alsa/acp/alsa-mixer.c:2813
msgid "Speakers"
msgstr "Динамиктер"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2826
+#: spa/plugins/alsa/acp/alsa-mixer.c:2814
msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2827
+#: spa/plugins/alsa/acp/alsa-mixer.c:2815
msgid "Digital Output (S/PDIF)"
msgstr "Цифрлық шығыс (S/PDIF)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2828
+#: spa/plugins/alsa/acp/alsa-mixer.c:2816
msgid "Digital Input (S/PDIF)"
msgstr "Цифрлық кіріс (S/PDIF)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2829
+#: spa/plugins/alsa/acp/alsa-mixer.c:2817
msgid "Multichannel Input"
msgstr "Көпарналы кіріс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2830
+#: spa/plugins/alsa/acp/alsa-mixer.c:2818
msgid "Multichannel Output"
msgstr "Көпарналы шығыс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2831
+#: spa/plugins/alsa/acp/alsa-mixer.c:2819
msgid "Game Output"
msgstr "Ойын шығысы"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2832 spa/plugins/alsa/acp/alsa-mixer.c:2833
+#: spa/plugins/alsa/acp/alsa-mixer.c:2820
+#: spa/plugins/alsa/acp/alsa-mixer.c:2821
msgid "Chat Output"
msgstr "Чат шығысы"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2834
+#: spa/plugins/alsa/acp/alsa-mixer.c:2822
+#, fuzzy
msgid "Chat Input"
-msgstr "Чат кірісі"
+msgstr "Чат шығысы"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2835
+#: spa/plugins/alsa/acp/alsa-mixer.c:2823
+#, fuzzy
msgid "Virtual Surround 7.1"
-msgstr "Виртуалды көлемді дыбыс 7.1"
+msgstr "Виртуалды көлемді аудиоқабылдағыш"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4522
+#: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Analog Mono"
msgstr "Аналогтық моно"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4523
+#: spa/plugins/alsa/acp/alsa-mixer.c:4528
+#, fuzzy
msgid "Analog Mono (Left)"
-msgstr "Аналогты моно (Сол жақ)"
+msgstr "Аналогтық моно"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4524
+#: spa/plugins/alsa/acp/alsa-mixer.c:4529
+#, fuzzy
msgid "Analog Mono (Right)"
-msgstr "Аналогты моно (Оң жақ)"
+msgstr "Аналогтық моно"
#. Note: Not translated to "Analog Stereo Input", because the source
#. * name gets "Input" appended to it automatically, so adding "Input"
#. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output.
-#: spa/plugins/alsa/acp/alsa-mixer.c:4525 spa/plugins/alsa/acp/alsa-mixer.c:4533
-#: spa/plugins/alsa/acp/alsa-mixer.c:4534
+#: spa/plugins/alsa/acp/alsa-mixer.c:4530
+#: spa/plugins/alsa/acp/alsa-mixer.c:4538
+#: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Stereo"
msgstr "Аналогтық стерео"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4526
+#: spa/plugins/alsa/acp/alsa-mixer.c:4531
msgid "Mono"
msgstr "Моно"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4527
+#: spa/plugins/alsa/acp/alsa-mixer.c:4532
msgid "Stereo"
msgstr "Стерео"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4535 spa/plugins/alsa/acp/alsa-mixer.c:4693
-#: spa/plugins/bluez5/bluez5-device.c:2410
+#: spa/plugins/alsa/acp/alsa-mixer.c:4540
+#: spa/plugins/alsa/acp/alsa-mixer.c:4698
+#: spa/plugins/bluez5/bluez5-device.c:1135
msgid "Headset"
msgstr "Гарнитура"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4536 spa/plugins/alsa/acp/alsa-mixer.c:4694
+#: spa/plugins/alsa/acp/alsa-mixer.c:4541
+#: spa/plugins/alsa/acp/alsa-mixer.c:4699
+#, fuzzy
msgid "Speakerphone"
msgstr "Динамик"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4537 spa/plugins/alsa/acp/alsa-mixer.c:4538
+#: spa/plugins/alsa/acp/alsa-mixer.c:4542
+#: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Multichannel"
msgstr "Көпарналы"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4539
+#: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 2.1"
msgstr "Аналогтық көлемді 2.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4540
+#: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 3.0"
msgstr "Аналогтық көлемді 3.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4541
+#: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 3.1"
msgstr "Аналогтық көлемді 3.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4542
+#: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 4.0"
msgstr "Аналогтық көлемді 4.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4543
+#: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 4.1"
msgstr "Аналогтық көлемді 4.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4544
+#: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 5.0"
msgstr "Аналогтық көлемді 5.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4545
+#: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Analog Surround 5.1"
msgstr "Аналогтық көлемді 5.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4546
+#: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Analog Surround 6.0"
msgstr "Аналогтық көлемді 6.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4547
+#: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Analog Surround 6.1"
msgstr "Аналогтық көлемді 6.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4548
+#: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Analog Surround 7.0"
msgstr "Аналогтық көлемді 7.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4549
+#: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Analog Surround 7.1"
msgstr "Аналогтық көлемді 7.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4550
+#: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Stereo (IEC958)"
msgstr "Цифрлық стерео (IEC958)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4551
+#: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Цифрлық көлемді 4.0 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4552
+#: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Цифрлық көлемді 5.1 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4553
+#: spa/plugins/alsa/acp/alsa-mixer.c:4558
msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "Цифрлық көлемді 5.1 (IEC958/DTS)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4554
+#: spa/plugins/alsa/acp/alsa-mixer.c:4559
msgid "Digital Stereo (HDMI)"
msgstr "Цифрлық стерео (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4555
+#: spa/plugins/alsa/acp/alsa-mixer.c:4560
msgid "Digital Surround 5.1 (HDMI)"
msgstr "Цифрлық көлемді 5.1 (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4556
+#: spa/plugins/alsa/acp/alsa-mixer.c:4561
msgid "Chat"
-msgstr "Чат"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4557
+#: spa/plugins/alsa/acp/alsa-mixer.c:4562
msgid "Game"
-msgstr "Ойын"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4691
+#: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Analog Mono Duplex"
msgstr "Аналогтық моно дуплекс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4692
+#: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Analog Stereo Duplex"
msgstr "Аналогтық стерео дуплекс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4695
+#: spa/plugins/alsa/acp/alsa-mixer.c:4700
msgid "Digital Stereo Duplex (IEC958)"
msgstr "Цифрлық стерео дуплекс (IEC958)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4696
+#: spa/plugins/alsa/acp/alsa-mixer.c:4701
msgid "Multichannel Duplex"
msgstr "Көпарналы дуплекс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4697
+#: spa/plugins/alsa/acp/alsa-mixer.c:4702
msgid "Stereo Duplex"
msgstr "Стерео дуплекс"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4698
+#: spa/plugins/alsa/acp/alsa-mixer.c:4703
msgid "Mono Chat + 7.1 Surround"
-msgstr "Моно чат + 7.1 көлемді дыбыс"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4799
+#: spa/plugins/alsa/acp/alsa-mixer.c:4806
#, c-format
msgid "%s Output"
msgstr "%s шығысы"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4807
+#: spa/plugins/alsa/acp/alsa-mixer.c:4813
#, c-format
msgid "%s Input"
msgstr "%s кірісі"
-#: spa/plugins/alsa/acp/alsa-util.c:1233 spa/plugins/alsa/acp/alsa-util.c:1327
+#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269
#, c-format
msgid ""
-"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu ms).\n"
-"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
msgid_plural ""
-"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
-"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
+"ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
msgstr[0] ""
-"snd_pcm_avail() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
-"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
msgstr[1] ""
-"snd_pcm_avail() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
-"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
-#: spa/plugins/alsa/acp/alsa-util.c:1299
+#: spa/plugins/alsa/acp/alsa-util.c:1241
#, c-format
msgid ""
-"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s%lu ms).\n"
-"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
msgid_plural ""
-"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s%lu ms).\n"
-"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
msgstr[0] ""
-"snd_pcm_delay() өте үлкен мән қайтарды: %li байт (%s%lu мс).\n"
-"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
msgstr[1] ""
-"snd_pcm_delay() өте үлкен мән қайтарды: %li байт (%s%lu мс).\n"
-"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
-#: spa/plugins/alsa/acp/alsa-util.c:1346
+#: spa/plugins/alsa/acp/alsa-util.c:1288
#, c-format
msgid ""
-"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail %lu.\n"
-"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
+"%lu.\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
msgstr ""
-"snd_pcm_avail_delay() оғаш мәндер қайтарды: кідіріс %lu мәні қолжетімді %lu мәнінен аз.\n"
-"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
-#: spa/plugins/alsa/acp/alsa-util.c:1389
+#: spa/plugins/alsa/acp/alsa-util.c:1331
#, c-format
msgid ""
-"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte (%lu ms).\n"
-"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
msgid_plural ""
-"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
-"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
+"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
+"(%lu ms).\n"
+"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
+"to the ALSA developers."
msgstr[0] ""
-"snd_pcm_mmap_begin() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
-"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
msgstr[1] ""
-"snd_pcm_mmap_begin() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
-"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
-#: spa/plugins/alsa/acp/channelmap.h:460
-msgid "(invalid)"
-msgstr "(жарамсыз)"
-
-#: spa/plugins/alsa/acp/compat.c:194
-msgid "Built-in Audio"
-msgstr "Құрамындағы аудио"
-
-#: spa/plugins/alsa/acp/compat.c:199
-msgid "Modem"
-msgstr "Модем"
-
-#: spa/plugins/bluez5/bluez5-device.c:2032
+#: spa/plugins/bluez5/bluez5-device.c:1010
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
-msgstr "Аудио шлюзі (A2DP бастапқы көзі және HSP/HFP AG)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2061
-msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
-msgstr "Есту аппараттарына арналған аудио ағыны (ASHA қабылдағышы)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2104
+#: spa/plugins/bluez5/bluez5-device.c:1033
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
-msgstr "Жоғары сапалы ойнату (A2DP қабылдағышы, кодек %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2107
+#: spa/plugins/bluez5/bluez5-device.c:1035
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
-msgstr "Жоғары сапалы дуплекс (A2DP бастапқы көзі/қабылдағышы, кодек %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2115
+#: spa/plugins/bluez5/bluez5-device.c:1041
msgid "High Fidelity Playback (A2DP Sink)"
-msgstr "Жоғары сапалы ойнату (A2DP қабылдағышы)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2117
+#: spa/plugins/bluez5/bluez5-device.c:1043
msgid "High Fidelity Duplex (A2DP Source/Sink)"
-msgstr "Жоғары сапалы дуплекс (A2DP бастапқы көзі/қабылдағышы)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2194
-#, c-format
-msgid "High Fidelity Playback (BAP Sink, codec %s)"
-msgstr "Жоғары сапалы ойнату (BAP қабылдағышы, кодек %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2199
-#, c-format
-msgid "High Fidelity Input (BAP Source, codec %s)"
-msgstr "Жоғары сапалы кіріс (BAP бастапқы көзі, кодек %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2203
-#, c-format
-msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
-msgstr "Жоғары сапалы дуплекс (BAP бастапқы көзі/қабылдағышы, кодек %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2212
-msgid "High Fidelity Playback (BAP Sink)"
-msgstr "Жоғары сапалы ойнату (BAP қабылдағышы)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2216
-msgid "High Fidelity Input (BAP Source)"
-msgstr "Жоғары сапалы кіріс (BAP бастапқы көзі)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2219
-msgid "High Fidelity Duplex (BAP Source/Sink)"
-msgstr "Жоғары сапалы дуплекс (BAP бастапқы көзі/қабылдағышы)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2259
+#: spa/plugins/bluez5/bluez5-device.c:1070
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
-msgstr "Гарнитура (HSP/HFP, кодек %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2411 spa/plugins/bluez5/bluez5-device.c:2416
-#: spa/plugins/bluez5/bluez5-device.c:2423 spa/plugins/bluez5/bluez5-device.c:2429
-#: spa/plugins/bluez5/bluez5-device.c:2435 spa/plugins/bluez5/bluez5-device.c:2441
-#: spa/plugins/bluez5/bluez5-device.c:2447 spa/plugins/bluez5/bluez5-device.c:2453
-#: spa/plugins/bluez5/bluez5-device.c:2459
+#: spa/plugins/bluez5/bluez5-device.c:1074
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr ""
+
+#: spa/plugins/bluez5/bluez5-device.c:1140
msgid "Handsfree"
msgstr "Хендс-фри"
-#: spa/plugins/bluez5/bluez5-device.c:2417
-msgid "Handsfree (HFP)"
-msgstr "Гарнитура (HFP)"
+#: spa/plugins/bluez5/bluez5-device.c:1155
+msgid "Headphone"
+msgstr "Құлаққап"
-#: spa/plugins/bluez5/bluez5-device.c:2440
+#: spa/plugins/bluez5/bluez5-device.c:1160
msgid "Portable"
msgstr "Портативті динамик"
-#: spa/plugins/bluez5/bluez5-device.c:2446
+#: spa/plugins/bluez5/bluez5-device.c:1165
msgid "Car"
msgstr "Автомобильдік динамик"
-#: spa/plugins/bluez5/bluez5-device.c:2452
+#: spa/plugins/bluez5/bluez5-device.c:1170
msgid "HiFi"
msgstr "HiFi"
-#: spa/plugins/bluez5/bluez5-device.c:2458
+#: spa/plugins/bluez5/bluez5-device.c:1175
msgid "Phone"
msgstr "Телефон"
-#: spa/plugins/bluez5/bluez5-device.c:2465
+#: spa/plugins/bluez5/bluez5-device.c:1181
msgid "Bluetooth"
msgstr "Bluetooth"
-
-#: spa/plugins/bluez5/bluez5-device.c:2466
-msgid "Bluetooth Handsfree"
-msgstr "Bluetooth гарнитурасы"
-
-#~ msgid "Headphone"
-#~ msgstr "Құлаққап"
diff --git a/po/sr.po b/po/sr.po
index 9ae7491dc..247b58701 100644
--- a/po/sr.po
+++ b/po/sr.po
@@ -3,239 +3,111 @@
# This file is distributed under the same license as the pipewire package.
# Igor Miletic (Игор Милетић) , 2009.
# Miloš Komarčević , 2009, 2012.
-# Марко Костић , 2026
#
msgid ""
msgstr ""
"Project-Id-Version: pipewire\n"
-"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
-"issues\n"
-"POT-Creation-Date: 2026-04-09 14:32+0000\n"
-"PO-Revision-Date: 2026-04-11 15:05+0200\n"
-"Last-Translator: Марко Костић \n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
+"issues/new\n"
+"POT-Creation-Date: 2021-04-18 16:54+0800\n"
+"PO-Revision-Date: 2012-01-30 09:55+0000\n"
+"Last-Translator: Miloš Komarčević \n"
"Language-Team: Serbian (sr) \n"
-"Language: sr\n"
+"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
-"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
-"X-Generator: Poedit 3.9\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-#: src/daemon/pipewire.c:29
+#: src/daemon/pipewire.c:43
#, c-format
msgid ""
"%s [options]\n"
" -h, --help Show this help\n"
-" -v, --verbose Increase verbosity by one level\n"
" --version Show version\n"
" -c, --config Load config (Default %s)\n"
-" -P --properties Set context properties\n"
msgstr ""
-"%s [опције]\n"
-" -h, --help Прикажи ову помоћ\n"
-" -v, --verbose Повећај опширност за један ниво\n"
-" --version Прикажи издање\n"
-" -c, --config Учитај подешавања (подразумевано "
-"%s)\n"
-" -P --properties Постави својства контекста\n"
-
-#: src/daemon/pipewire.desktop.in:3
-msgid "PipeWire Media System"
-msgstr "Пајпвајер медијски систем"
#: src/daemon/pipewire.desktop.in:4
+msgid "PipeWire Media System"
+msgstr ""
+
+#: src/daemon/pipewire.desktop.in:5
msgid "Start the PipeWire Media System"
-msgstr "Покрени Пајпвајер медијски систем"
+msgstr ""
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
-#, c-format
-msgid "Tunnel to %s%s%s"
-msgstr "Тунел до %s%s%s"
+#: src/examples/media-session/alsa-monitor.c:526
+#: spa/plugins/alsa/acp/compat.c:187
+msgid "Built-in Audio"
+msgstr "Унутрашњи звук"
-#: src/modules/module-fallback-sink.c:40
-msgid "Dummy Output"
-msgstr "Лажан излаз"
+#: src/examples/media-session/alsa-monitor.c:530
+#: spa/plugins/alsa/acp/compat.c:192
+msgid "Modem"
+msgstr "Модем"
-#: src/modules/module-pulse-tunnel.c:761
-#, c-format
-msgid "Tunnel for %s@%s"
-msgstr "Тунел за %s@%s"
-
-#: src/modules/module-zeroconf-discover.c:290
+#: src/examples/media-session/alsa-monitor.c:539
msgid "Unknown device"
-msgstr "Непознати уређај"
+msgstr ""
-#: src/modules/module-zeroconf-discover.c:302
-#, c-format
-msgid "%s on %s@%s"
-msgstr "%s на %s@%s"
-
-#: src/modules/module-zeroconf-discover.c:306
-#, c-format
-msgid "%s on %s"
-msgstr "%s на %s"
-
-#: src/tools/pw-cat.c:269
-#, c-format
-msgid "Supported formats:\n"
-msgstr "Подржани формати:\n"
-
-#: src/tools/pw-cat.c:754
-#, c-format
-msgid "Supported channel layouts:\n"
-msgstr "Подржани распореди канала:\n"
-
-#: src/tools/pw-cat.c:764
-#, c-format
-msgid "Supported channel layout aliases:\n"
-msgstr "Подржани надимци распореда канала:\n"
-
-#: src/tools/pw-cat.c:766
-#, c-format
-msgid " %s -> %s\n"
-msgstr " %s -> %s\n"
-
-#: src/tools/pw-cat.c:771
-#, c-format
-msgid "Supported channel names:\n"
-msgstr "Подржани називи канала:\n"
-
-#: src/tools/pw-cat.c:1183
+#: src/tools/pw-cat.c:991
#, c-format
msgid ""
-"%s [options] [|-]\n"
+"%s [options] \n"
" -h, --help Show this help\n"
" --version Show version\n"
" -v, --verbose Enable verbose operations\n"
"\n"
msgstr ""
-"%s [опције] [<датотека>|-]\n"
-" -h, --help Прикажи ову помоћ\n"
-" --version Прикажи издање\n"
-" -v, --verbose Омогући опширан рад\n"
-"\n"
-#: src/tools/pw-cat.c:1190
+#: src/tools/pw-cat.c:998
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
" --media-type Set media type (default %s)\n"
" --media-category Set media category (default %s)\n"
" --media-role Set media role (default %s)\n"
-" --target Set node target serial or name "
-"(default %s)\n"
+" --target Set node target (default %s)\n"
" 0 means don't link\n"
-" -C --monitor Capture monitor ports (in recording "
-"mode)\n"
" --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n"
" the rate is the one of the source "
"file\n"
-" -P --properties Set node properties\n"
+" --list-targets List available targets for --target\n"
"\n"
msgstr ""
-" -R, --remote Назив удаљеног услужног програма\n"
-" --media-type Постави врсту медија (подразумевано "
-"%s)\n"
-" --media-category Постави категорију медија "
-"(подразумевано %s)\n"
-" --media-role Постави улогу медија (подразумевано "
-"%s)\n"
-" --target Постави циљни серијски број или "
-"назив чвора (подразумевано %s)\n"
-" 0 значи без повезивања\n"
-" -C --monitor Хватај прикључнике за надзор (у "
-"режиму снимања)\n"
-" --latency Постави латентност чвора "
-"(подразумевано %s)\n"
-" Xјединица (јединица = s, ms, us, "
-"ns)\n"
-" или директни узорци (256)\n"
-" проток је онај из изворне "
-"датотеке\n"
-" -P --properties Постави својства чвора\n"
-"\n"
-#: src/tools/pw-cat.c:1209
+#: src/tools/pw-cat.c:1016
#, c-format
msgid ""
-" --rate Sample rate (default %u)\n"
-" --channels Number of channels (default %u)\n"
+" --rate Sample rate (req. for rec) (default "
+"%u)\n"
+" --channels Number of channels (req. for rec) "
+"(default %u)\n"
" --channel-map Channel map\n"
-" a channel layout: \"Stereo\", "
-"\"5.1\",... or\n"
+" one of: \"stereo\", "
+"\"surround-51\",... or\n"
" comma separated list of channel "
"names: eg. \"FL,FR\"\n"
-" --list-layouts List supported channel layouts\n"
-" --list-channel-names List supported channel maps\n"
-" --format Sample format (default %s)\n"
-" --list-formats List supported sample formats\n"
-" --container Container format\n"
-" --list-containers List supported containers and "
-"extensions\n"
+" --format Sample format %s (req. for rec) "
+"(default %s)\n"
" --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default "
"%d)\n"
-" -a, --raw RAW mode\n"
-" -M, --force-midi Force midi format, one of \"midi\" "
-"or \"ump\", (default ump)\n"
-" -n, --sample-count COUNT Stop after COUNT samples\n"
"\n"
msgstr ""
-" --rate Учесталост узорковања (подразумевано "
-"%u)\n"
-" --channels Број канала (подразумевано %u)\n"
-" --channel-map Мапа канала\n"
-" распоред канала: „Stereo“, "
-"„5.1“,... или\n"
-" зарезом раздвојен списак назива "
-"канала: нпр. „FL,FR“\n"
-" --list-layouts Испиши подржане распореде канала\n"
-" --list-channel-names Испиши подржане мапе канала\n"
-" --format Формат узорка (подразумевано %s)\n"
-" --list-formats Испиши подржане формате узорака\n"
-" --container Формат контејнера\n"
-" --list-containers Испиши подржане контејнере и "
-"проширења\n"
-" --volume Јачина тока 0-1.0 (подразумевано "
-"%.3f)\n"
-" -q --quality Квалитет промене узорковања (0 - 15) "
-"(подразумевано %d)\n"
-" -a, --raw Сирови (RAW) режим\n"
-" -M, --force-midi Приморај МИДИ формат, један од "
-"„midi“ или „ump“, (подразумевано ump)\n"
-" -n, --sample-count БРОЈ Заустави након БРОЈ узорака\n"
-"\n"
-#: src/tools/pw-cat.c:1234
+#: src/tools/pw-cat.c:1033
msgid ""
" -p, --playback Playback mode\n"
" -r, --record Recording mode\n"
" -m, --midi Midi mode\n"
-" -d, --dsd DSD mode\n"
-" -o, --encoded Encoded mode\n"
-" -s, --sysex SysEx mode\n"
-" -c, --midi-clip MIDI clip mode\n"
"\n"
msgstr ""
-" -p, --playback Режим пуштања\n"
-" -r, --record Режим снимања\n"
-" -m, --midi МИДИ режим\n"
-" -d, --dsd DSD режим\n"
-" -o, --encoded Кодирани режим\n"
-" -s, --sysex SysEx режим\n"
-" -c, --midi-clip Режим МИДИ одсечка\n"
-"\n"
-#: src/tools/pw-cat.c:1839
-#, c-format
-msgid "Supported containers and extensions:\n"
-msgstr "Подржани контејнери и проширења:\n"
-
-#: src/tools/pw-cli.c:2386
+#: src/tools/pw-cli.c:2932
#, c-format
msgid ""
"%s [options] [command]\n"
@@ -243,365 +115,376 @@ msgid ""
" --version Show version\n"
" -d, --daemon Start as daemon (Default false)\n"
" -r, --remote Remote daemon name\n"
-" -m, --monitor Monitor activity\n"
"\n"
msgstr ""
-"%s [опције] [наредба]\n"
-" -h, --help Прикажи ову помоћ\n"
-" --version Прикажи издање\n"
-" -d, --daemon Покрени као услужни програм "
-"(подразумевано нетачно)\n"
-" -r, --remote Назив удаљеног услужног програма\n"
-" -m, --monitor Надгледај активност\n"
-"\n"
-#: spa/plugins/alsa/acp/acp.c:361
+#: spa/plugins/alsa/acp/acp.c:290
msgid "Pro Audio"
-msgstr "Професионални звук"
+msgstr ""
-#: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
-#: spa/plugins/bluez5/bluez5-device.c:2163
+#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704
+#: spa/plugins/bluez5/bluez5-device.c:1000
msgid "Off"
msgstr "Искључено"
-#: spa/plugins/alsa/acp/acp.c:618
-#, c-format
-msgid "%s [ALSA UCM error]"
-msgstr "%s [ALSA UCM грешка]"
+#: spa/plugins/alsa/acp/channelmap.h:466
+msgid "(invalid)"
+msgstr "(неисправно)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2721
+#: spa/plugins/alsa/acp/alsa-mixer.c:2709
msgid "Input"
msgstr "Улаз"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2722
+#: spa/plugins/alsa/acp/alsa-mixer.c:2710
msgid "Docking Station Input"
msgstr "Улаз прикључне станице"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2723
+#: spa/plugins/alsa/acp/alsa-mixer.c:2711
+#, fuzzy
msgid "Docking Station Microphone"
msgstr "Микрофон прикључне станице"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2724
+#: spa/plugins/alsa/acp/alsa-mixer.c:2712
+#, fuzzy
msgid "Docking Station Line In"
msgstr "Улаз прикључне станице"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2725
-#: spa/plugins/alsa/acp/alsa-mixer.c:2816
+#: spa/plugins/alsa/acp/alsa-mixer.c:2713
+#: spa/plugins/alsa/acp/alsa-mixer.c:2804
msgid "Line In"
msgstr "Линија у"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2726
-#: spa/plugins/alsa/acp/alsa-mixer.c:2810
-#: spa/plugins/bluez5/bluez5-device.c:2596
+#: spa/plugins/alsa/acp/alsa-mixer.c:2714
+#: spa/plugins/alsa/acp/alsa-mixer.c:2798
+#: spa/plugins/bluez5/bluez5-device.c:1145
msgid "Microphone"
msgstr "Микрофон"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2727
-#: spa/plugins/alsa/acp/alsa-mixer.c:2811
+#: spa/plugins/alsa/acp/alsa-mixer.c:2715
+#: spa/plugins/alsa/acp/alsa-mixer.c:2799
+#, fuzzy
msgid "Front Microphone"
-msgstr "Предњи микрофон"
+msgstr "Микрофон прикључне станице"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2728
-#: spa/plugins/alsa/acp/alsa-mixer.c:2812
+#: spa/plugins/alsa/acp/alsa-mixer.c:2716
+#: spa/plugins/alsa/acp/alsa-mixer.c:2800
+#, fuzzy
msgid "Rear Microphone"
-msgstr "Задњи микрофон"
+msgstr "Микрофон"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2729
+#: spa/plugins/alsa/acp/alsa-mixer.c:2717
msgid "External Microphone"
msgstr "Спољни микрофон"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2730
-#: spa/plugins/alsa/acp/alsa-mixer.c:2814
+#: spa/plugins/alsa/acp/alsa-mixer.c:2718
+#: spa/plugins/alsa/acp/alsa-mixer.c:2802
msgid "Internal Microphone"
msgstr "Унутрашњи микрофон"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2731
-#: spa/plugins/alsa/acp/alsa-mixer.c:2817
+#: spa/plugins/alsa/acp/alsa-mixer.c:2719
+#: spa/plugins/alsa/acp/alsa-mixer.c:2805
msgid "Radio"
msgstr "Радио"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2732
-#: spa/plugins/alsa/acp/alsa-mixer.c:2818
+#: spa/plugins/alsa/acp/alsa-mixer.c:2720
+#: spa/plugins/alsa/acp/alsa-mixer.c:2806
msgid "Video"
msgstr "Видео"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2733
+#: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Automatic Gain Control"
msgstr "Самостална контрола појачања"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2734
+#: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "No Automatic Gain Control"
msgstr "Без самосталне контроле појачања"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2735
+#: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Boost"
msgstr "Подизање"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2736
+#: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "No Boost"
msgstr "Без подизања"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2737
+#: spa/plugins/alsa/acp/alsa-mixer.c:2725
msgid "Amplifier"
msgstr "Појачало"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2738
+#: spa/plugins/alsa/acp/alsa-mixer.c:2726
msgid "No Amplifier"
msgstr "Без појачала"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2739
+#: spa/plugins/alsa/acp/alsa-mixer.c:2727
+#, fuzzy
msgid "Bass Boost"
-msgstr "Појачање баса"
+msgstr "Подизање"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2740
+#: spa/plugins/alsa/acp/alsa-mixer.c:2728
+#, fuzzy
msgid "No Bass Boost"
-msgstr "Без појачања баса"
+msgstr "Без подизања"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2741
-#: spa/plugins/bluez5/bluez5-device.c:2602
+#: spa/plugins/alsa/acp/alsa-mixer.c:2729
+#: spa/plugins/bluez5/bluez5-device.c:1150
msgid "Speaker"
-msgstr "Звучник"
+msgstr ""
-#. Don't call it "headset", the HF one has the mic
-#: spa/plugins/alsa/acp/alsa-mixer.c:2742
-#: spa/plugins/alsa/acp/alsa-mixer.c:2820
-#: spa/plugins/bluez5/bluez5-device.c:2608
-#: spa/plugins/bluez5/bluez5-device.c:2675
+#: spa/plugins/alsa/acp/alsa-mixer.c:2730
+#: spa/plugins/alsa/acp/alsa-mixer.c:2808
msgid "Headphones"
msgstr "Аналогне слушалице"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2809
+#: spa/plugins/alsa/acp/alsa-mixer.c:2797
msgid "Analog Input"
msgstr "Аналогни улаз"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2813
+#: spa/plugins/alsa/acp/alsa-mixer.c:2801
msgid "Dock Microphone"
msgstr "Микрофон прикључне станице"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2815
+#: spa/plugins/alsa/acp/alsa-mixer.c:2803
+#, fuzzy
msgid "Headset Microphone"
-msgstr "Микрофон са слушалицама"
+msgstr "Микрофон"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2819
+#: spa/plugins/alsa/acp/alsa-mixer.c:2807
msgid "Analog Output"
msgstr "Аналогни излаз"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2821
+#: spa/plugins/alsa/acp/alsa-mixer.c:2809
+#, fuzzy
msgid "Headphones 2"
-msgstr "Слушалице 2"
+msgstr "Аналогне слушалице"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2822
+#: spa/plugins/alsa/acp/alsa-mixer.c:2810
+#, fuzzy
msgid "Headphones Mono Output"
-msgstr "Моно излаз за слушалице"
+msgstr "Аналогни моно излаз"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2823
+#: spa/plugins/alsa/acp/alsa-mixer.c:2811
+#, fuzzy
msgid "Line Out"
-msgstr "Линијски излаз"
+msgstr "Линија у"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2824
+#: spa/plugins/alsa/acp/alsa-mixer.c:2812
msgid "Analog Mono Output"
msgstr "Аналогни моно излаз"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2825
+#: spa/plugins/alsa/acp/alsa-mixer.c:2813
+#, fuzzy
msgid "Speakers"
-msgstr "Говорници"
+msgstr "Аналогни стерео"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2826
+#: spa/plugins/alsa/acp/alsa-mixer.c:2814
msgid "HDMI / DisplayPort"
-msgstr "ХДМИ / Дисплеј-порт"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:2827
+#: spa/plugins/alsa/acp/alsa-mixer.c:2815
+#, fuzzy
msgid "Digital Output (S/PDIF)"
-msgstr "Дигитални излаз (С/ПДИФ)"
+msgstr "Дигитални стерео (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2828
+#: spa/plugins/alsa/acp/alsa-mixer.c:2816
+#, fuzzy
msgid "Digital Input (S/PDIF)"
-msgstr "Дигитални улаз (С/ПДИФ)"
+msgstr "Дигитални стерео (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2829
+#: spa/plugins/alsa/acp/alsa-mixer.c:2817
msgid "Multichannel Input"
-msgstr "Вишеканални улаз"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:2830
+#: spa/plugins/alsa/acp/alsa-mixer.c:2818
+#, fuzzy
msgid "Multichannel Output"
-msgstr "Вишеканални излаз"
+msgstr "Празан излаз"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2831
+#: spa/plugins/alsa/acp/alsa-mixer.c:2819
+#, fuzzy
msgid "Game Output"
-msgstr "Излаз за игре"
+msgstr "Празан излаз"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2832
-#: spa/plugins/alsa/acp/alsa-mixer.c:2833
+#: spa/plugins/alsa/acp/alsa-mixer.c:2820
+#: spa/plugins/alsa/acp/alsa-mixer.c:2821
+#, fuzzy
msgid "Chat Output"
-msgstr "Излаз за ћаскање"
+msgstr "Празан излаз"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2834
+#: spa/plugins/alsa/acp/alsa-mixer.c:2822
+#, fuzzy
msgid "Chat Input"
-msgstr "Улаз за ћаскање"
+msgstr "Улаз"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2835
+#: spa/plugins/alsa/acp/alsa-mixer.c:2823
+#, fuzzy
msgid "Virtual Surround 7.1"
-msgstr "Виртуелни окружујући звук 7.1"
+msgstr "Аналогни окружујући 7.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4522
+#: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Analog Mono"
msgstr "Аналогни моно"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4523
+#: spa/plugins/alsa/acp/alsa-mixer.c:4528
+#, fuzzy
msgid "Analog Mono (Left)"
-msgstr "Аналогни моно (леви)"
+msgstr "Аналогни моно"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4524
+#: spa/plugins/alsa/acp/alsa-mixer.c:4529
+#, fuzzy
msgid "Analog Mono (Right)"
-msgstr "Аналогни моно (десни)"
+msgstr "Аналогни моно"
#. Note: Not translated to "Analog Stereo Input", because the source
#. * name gets "Input" appended to it automatically, so adding "Input"
#. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output.
-#: spa/plugins/alsa/acp/alsa-mixer.c:4525
-#: spa/plugins/alsa/acp/alsa-mixer.c:4533
-#: spa/plugins/alsa/acp/alsa-mixer.c:4534
+#: spa/plugins/alsa/acp/alsa-mixer.c:4530
+#: spa/plugins/alsa/acp/alsa-mixer.c:4538
+#: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Stereo"
msgstr "Аналогни стерео"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4526
+#: spa/plugins/alsa/acp/alsa-mixer.c:4531
msgid "Mono"
msgstr "Моно"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4527
+#: spa/plugins/alsa/acp/alsa-mixer.c:4532
msgid "Stereo"
msgstr "Стерео"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4535
-#: spa/plugins/alsa/acp/alsa-mixer.c:4693
-#: spa/plugins/bluez5/bluez5-device.c:2584
+#: spa/plugins/alsa/acp/alsa-mixer.c:4540
+#: spa/plugins/alsa/acp/alsa-mixer.c:4698
+#: spa/plugins/bluez5/bluez5-device.c:1135
msgid "Headset"
-msgstr "Слушалице са микрофоном"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4536
-#: spa/plugins/alsa/acp/alsa-mixer.c:4694
+#: spa/plugins/alsa/acp/alsa-mixer.c:4541
+#: spa/plugins/alsa/acp/alsa-mixer.c:4699
+#, fuzzy
msgid "Speakerphone"
-msgstr "Спикерфон"
+msgstr "Аналогни стерео"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4537
-#: spa/plugins/alsa/acp/alsa-mixer.c:4538
+#: spa/plugins/alsa/acp/alsa-mixer.c:4542
+#: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Multichannel"
-msgstr "Вишеканални"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4539
+#: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 2.1"
msgstr "Аналогни окружујући 2.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4540
+#: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 3.0"
msgstr "Аналогни окружујући 3.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4541
+#: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 3.1"
msgstr "Аналогни окружујући 3.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4542
+#: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 4.0"
msgstr "Аналогни окружујући 4.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4543
+#: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 4.1"
msgstr "Аналогни окружујући 4.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4544
+#: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 5.0"
msgstr "Аналогни окружујући 5.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4545
+#: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Analog Surround 5.1"
msgstr "Аналогни окружујући 5.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4546
+#: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Analog Surround 6.0"
msgstr "Аналогни окружујући 6.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4547
+#: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Analog Surround 6.1"
msgstr "Аналогни окружујући 6.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4548
+#: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Analog Surround 7.0"
msgstr "Аналогни окружујући 7.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4549
+#: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Analog Surround 7.1"
msgstr "Аналогни окружујући 7.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4550
+#: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Stereo (IEC958)"
msgstr "Дигитални стерео (IEC958)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4551
+#: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Дигитални окружујући 4.0 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4552
+#: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Дигитални окружујући 5.1 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4553
+#: spa/plugins/alsa/acp/alsa-mixer.c:4558
+#, fuzzy
msgid "Digital Surround 5.1 (IEC958/DTS)"
-msgstr "Дигитални окружујући 5.1 (IEC958/DTS)"
+msgstr "Дигитални окружујући 5.1 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4554
+#: spa/plugins/alsa/acp/alsa-mixer.c:4559
msgid "Digital Stereo (HDMI)"
msgstr "Дигитални стерео (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4555
+#: spa/plugins/alsa/acp/alsa-mixer.c:4560
+#, fuzzy
msgid "Digital Surround 5.1 (HDMI)"
-msgstr "Дигитални окружујући 5.1 (HDMI)"
+msgstr "Дигитални окружујући 5.1 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4556
+#: spa/plugins/alsa/acp/alsa-mixer.c:4561
msgid "Chat"
-msgstr "Ћаскање"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4557
+#: spa/plugins/alsa/acp/alsa-mixer.c:4562
msgid "Game"
-msgstr "Игрица"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4691
+#: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Analog Mono Duplex"
msgstr "Двосмерни аналогни моно"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4692
+#: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Analog Stereo Duplex"
msgstr "Двосмерни аналогни стерео"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4695
+#: spa/plugins/alsa/acp/alsa-mixer.c:4700
msgid "Digital Stereo Duplex (IEC958)"
msgstr "Двосмерни дигитални стерео (IEC958)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4696
+#: spa/plugins/alsa/acp/alsa-mixer.c:4701
msgid "Multichannel Duplex"
-msgstr "Вишеканални дуплекс"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4697
+#: spa/plugins/alsa/acp/alsa-mixer.c:4702
+#, fuzzy
msgid "Stereo Duplex"
-msgstr "Стерео дуплекс"
+msgstr "Двосмерни аналогни стерео"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4698
+#: spa/plugins/alsa/acp/alsa-mixer.c:4703
msgid "Mono Chat + 7.1 Surround"
-msgstr "Моно ћаскање + 7.1 окружујући"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4799
-#, c-format
+#: spa/plugins/alsa/acp/alsa-mixer.c:4806
+#, fuzzy, c-format
msgid "%s Output"
-msgstr "%s излаз"
+msgstr "Празан излаз"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4807
-#, c-format
+#: spa/plugins/alsa/acp/alsa-mixer.c:4813
+#, fuzzy, c-format
msgid "%s Input"
-msgstr "%s улаз"
+msgstr "Улаз"
-#: spa/plugins/alsa/acp/alsa-util.c:1233 spa/plugins/alsa/acp/alsa-util.c:1327
-#, c-format
+#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269
+#, fuzzy, c-format
msgid ""
"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
"ms).\n"
@@ -613,9 +496,9 @@ msgid_plural ""
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] ""
-"snd_pcm_avail() је вратио вредност која је изузетно велика: %lu бајт (%lu "
+"snd_pcm_avail() је вратио вредност која је необично велика: %lu бајтова (%lu "
"ms).\n"
-"Највероватније је ово грешка у ALSA управљачком програму „%s“. Пријавите "
+"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
"овај проблем ALSA програмерима."
msgstr[1] ""
"snd_pcm_avail() је вратио вредност која је необично велика: %lu бајтова (%lu "
@@ -628,49 +511,49 @@ msgstr[2] ""
"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
"овај проблем ALSA програмерима."
-#: spa/plugins/alsa/acp/alsa-util.c:1299
-#, c-format
+#: spa/plugins/alsa/acp/alsa-util.c:1241
+#, fuzzy, c-format
msgid ""
-"snd_pcm_delay() returned a value that is exceptionally large: %li byte "
-"(%s%lu ms).\n"
+"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
+"%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgid_plural ""
-"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
-"(%s%lu ms).\n"
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] ""
-"snd_pcm_delay() је вратио вредност која је изузетно велика: %li бајт (%s%lu "
-"ms).\n"
-"Највероватније је ово грешка у ALSA управљачком програму „%s“. Пријавите "
+"snd_pcm_delay() је вратио вредност која је необично велика: %li бајтова (%s"
+"%lu ms).\n"
+"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
"овај проблем ALSA програмерима."
msgstr[1] ""
-"snd_pcm_delay() је вратио вредност која је необично велика: %li бајтова "
-"(%s%lu ms).\n"
+"snd_pcm_delay() је вратио вредност која је необично велика: %li бајтова (%s"
+"%lu ms).\n"
"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
"овај проблем ALSA програмерима."
msgstr[2] ""
-"snd_pcm_delay() је вратио вредност која је необично велика: %li бајтова "
-"(%s%lu ms).\n"
+"snd_pcm_delay() је вратио вредност која је необично велика: %li бајтова (%s"
+"%lu ms).\n"
"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
"овај проблем ALSA програмерима."
-#: spa/plugins/alsa/acp/alsa-util.c:1346
-#, c-format
+#: spa/plugins/alsa/acp/alsa-util.c:1288
+#, fuzzy, c-format
msgid ""
"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
"%lu.\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr ""
-"snd_pcm_avail_delay() је вратио чудне вредности: кашњење %lu је мање од "
-"доступног %lu.\n"
-"Највероватније је ово грешка у ALSA управљачком програму „%s“. Пријавите "
+"snd_pcm_avail() је вратио вредност која је необично велика: %lu бајтова (%lu "
+"ms).\n"
+"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
"овај проблем ALSA програмерима."
-#: spa/plugins/alsa/acp/alsa-util.c:1389
-#, c-format
+#: spa/plugins/alsa/acp/alsa-util.c:1331
+#, fuzzy, c-format
msgid ""
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
"(%lu ms).\n"
@@ -682,9 +565,9 @@ msgid_plural ""
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] ""
-"snd_pcm_mmap_begin() је вратио вредност која је изузетно велика: %lu бајт "
-"(%lu ms).\n"
-"Највероватније је ово грешка у ALSA управљачком програму „%s“. Пријавите "
+"snd_pcm_mmap_begin() је вратио вредност која је необично велика: %lu "
+"бајтова (%lu ms).\n"
+"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
"овај проблем ALSA програмерима."
msgstr[1] ""
"snd_pcm_mmap_begin() је вратио вредност која је необично велика: %lu "
@@ -697,120 +580,62 @@ msgstr[2] ""
"Ово је највероватније грешка у „%s“ ALSA управљачком програму. Пријавите "
"овај проблем ALSA програмерима."
-#: spa/plugins/alsa/acp/channelmap.h:460
-msgid "(invalid)"
-msgstr "(неисправно)"
-
-#: spa/plugins/alsa/acp/compat.c:194
-msgid "Built-in Audio"
-msgstr "Унутрашњи звук"
-
-#: spa/plugins/alsa/acp/compat.c:199
-msgid "Modem"
-msgstr "Модем"
-
-#: spa/plugins/bluez5/bluez5-device.c:2174
+#: spa/plugins/bluez5/bluez5-device.c:1010
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
-msgstr "Звучни мрежни пролаз (А2ДП извор и HSP/HFP AG)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2203
-msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
-msgstr "Струјање звука за слушне апарате (ASHA сливник)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2246
+#: spa/plugins/bluez5/bluez5-device.c:1033
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
-msgstr "Пуштање високе верности (А2ДП сливник, кодек %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2249
+#: spa/plugins/bluez5/bluez5-device.c:1035
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
-msgstr "Двосмерно високе верности (А2ДП извор/сливник, кодек %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2257
+#: spa/plugins/bluez5/bluez5-device.c:1041
msgid "High Fidelity Playback (A2DP Sink)"
-msgstr "Пуштање високе верности (А2ДП сливник)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2259
+#: spa/plugins/bluez5/bluez5-device.c:1043
msgid "High Fidelity Duplex (A2DP Source/Sink)"
-msgstr "Двосмерно високе верности (А2ДП извор/сливник)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2281
-msgid "Auto: Prefer Quality (A2DP)"
-msgstr "Ауто: предност квалитету (А2ДП)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2286
-msgid "Auto: Prefer Latency (A2DP)"
-msgstr "Ауто: предност кашњењу (А2ДП)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2366
-#, c-format
-msgid "High Fidelity Playback (BAP Sink, codec %s)"
-msgstr "Пуштање високе верности (БАП сливник, кодек %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2371
-#, c-format
-msgid "High Fidelity Input (BAP Source, codec %s)"
-msgstr "Улаз високе верности (БАП извор, кодек %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2375
-#, c-format
-msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
-msgstr "Двосмерно високе верности (БАП извор/сливник, кодек %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2384
-msgid "High Fidelity Playback (BAP Sink)"
-msgstr "Пуштање високе верности (БАП сливник)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2388
-msgid "High Fidelity Input (BAP Source)"
-msgstr "Улаз високе верности (БАП извор)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2391
-msgid "High Fidelity Duplex (BAP Source/Sink)"
-msgstr "Двосмерно високе верности (БАП извор/сливник)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2431
+#: spa/plugins/bluez5/bluez5-device.c:1070
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
-msgstr "Слушалице са микрофоном (ХСП/ХФП, кодек %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2585
-#: spa/plugins/bluez5/bluez5-device.c:2590
-#: spa/plugins/bluez5/bluez5-device.c:2597
-#: spa/plugins/bluez5/bluez5-device.c:2603
-#: spa/plugins/bluez5/bluez5-device.c:2609
-#: spa/plugins/bluez5/bluez5-device.c:2615
-#: spa/plugins/bluez5/bluez5-device.c:2621
-#: spa/plugins/bluez5/bluez5-device.c:2627
-#: spa/plugins/bluez5/bluez5-device.c:2633
+#: spa/plugins/bluez5/bluez5-device.c:1074
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr ""
+
+#: spa/plugins/bluez5/bluez5-device.c:1140
msgid "Handsfree"
-msgstr "Хендсфри"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2591
-msgid "Handsfree (HFP)"
-msgstr "Хендсфри (ХФП)"
+#: spa/plugins/bluez5/bluez5-device.c:1155
+#, fuzzy
+msgid "Headphone"
+msgstr "Аналогне слушалице"
-#: spa/plugins/bluez5/bluez5-device.c:2614
+#: spa/plugins/bluez5/bluez5-device.c:1160
msgid "Portable"
-msgstr "Преносно"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2620
+#: spa/plugins/bluez5/bluez5-device.c:1165
msgid "Car"
-msgstr "Кола"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2626
+#: spa/plugins/bluez5/bluez5-device.c:1170
msgid "HiFi"
-msgstr "Хи-фи"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2632
+#: spa/plugins/bluez5/bluez5-device.c:1175
msgid "Phone"
-msgstr "Телефон"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2639
+#: spa/plugins/bluez5/bluez5-device.c:1181
msgid "Bluetooth"
-msgstr "Блутут"
-
-#: spa/plugins/bluez5/bluez5-device.c:2640
-msgid "Bluetooth Handsfree"
-msgstr "Блутут хендсфри"
+msgstr ""
diff --git a/po/sr@latin.po b/po/sr@latin.po
index bc7572621..6f56d8608 100644
--- a/po/sr@latin.po
+++ b/po/sr@latin.po
@@ -1,241 +1,113 @@
-# Serbian translations for pipewire
+# Serbian(Latin) translations for pipewire
# Copyright (C) 2006 Lennart Poettering
# This file is distributed under the same license as the pipewire package.
# Igor Miletic (Igor Miletić) , 2009.
# Miloš Komarčević , 2009, 2012.
-# Marko Kostić , 2026
#
msgid ""
msgstr ""
"Project-Id-Version: pipewire\n"
-"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
-"issues\n"
-"POT-Creation-Date: 2026-04-09 14:32+0000\n"
-"PO-Revision-Date: 2026-04-11 15:05+0200\n"
-"Last-Translator: Marko Kostić \n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
+"issues/new\n"
+"POT-Creation-Date: 2021-04-18 16:54+0800\n"
+"PO-Revision-Date: 2012-01-30 09:55+0000\n"
+"Last-Translator: Miloš Komarčević \n"
"Language-Team: Serbian (sr) \n"
-"Language: sr\n"
+"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
-"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
-"X-Generator: Poedit 3.9\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-#: src/daemon/pipewire.c:29
+#: src/daemon/pipewire.c:43
#, c-format
msgid ""
"%s [options]\n"
" -h, --help Show this help\n"
-" -v, --verbose Increase verbosity by one level\n"
" --version Show version\n"
" -c, --config Load config (Default %s)\n"
-" -P --properties Set context properties\n"
msgstr ""
-"%s [opcije]\n"
-" -h, --help Prikaži ovu pomoć\n"
-" -v, --verbose Povećaj opširnost za jedan nivo\n"
-" --version Prikaži izdanje\n"
-" -c, --config Učitaj podešavanja (podrazumevano "
-"%s)\n"
-" -P --properties Postavi svojstva konteksta\n"
-
-#: src/daemon/pipewire.desktop.in:3
-msgid "PipeWire Media System"
-msgstr "Pajpvajer medijski sistem"
#: src/daemon/pipewire.desktop.in:4
+msgid "PipeWire Media System"
+msgstr ""
+
+#: src/daemon/pipewire.desktop.in:5
msgid "Start the PipeWire Media System"
-msgstr "Pokreni Pajpvajer medijski sistem"
+msgstr ""
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
-#, c-format
-msgid "Tunnel to %s%s%s"
-msgstr "Tunel do %s%s%s"
+#: src/examples/media-session/alsa-monitor.c:526
+#: spa/plugins/alsa/acp/compat.c:187
+msgid "Built-in Audio"
+msgstr "Unutrašnji zvuk"
-#: src/modules/module-fallback-sink.c:40
-msgid "Dummy Output"
-msgstr "Lažan izlaz"
+#: src/examples/media-session/alsa-monitor.c:530
+#: spa/plugins/alsa/acp/compat.c:192
+msgid "Modem"
+msgstr "Modem"
-#: src/modules/module-pulse-tunnel.c:761
-#, c-format
-msgid "Tunnel for %s@%s"
-msgstr "Tunel za %s@%s"
-
-#: src/modules/module-zeroconf-discover.c:290
+#: src/examples/media-session/alsa-monitor.c:539
msgid "Unknown device"
-msgstr "Nepoznati uređaj"
+msgstr ""
-#: src/modules/module-zeroconf-discover.c:302
-#, c-format
-msgid "%s on %s@%s"
-msgstr "%s na %s@%s"
-
-#: src/modules/module-zeroconf-discover.c:306
-#, c-format
-msgid "%s on %s"
-msgstr "%s na %s"
-
-#: src/tools/pw-cat.c:269
-#, c-format
-msgid "Supported formats:\n"
-msgstr "Podržani formati:\n"
-
-#: src/tools/pw-cat.c:754
-#, c-format
-msgid "Supported channel layouts:\n"
-msgstr "Podržani rasporedi kanala:\n"
-
-#: src/tools/pw-cat.c:764
-#, c-format
-msgid "Supported channel layout aliases:\n"
-msgstr "Podržani nadimci rasporeda kanala:\n"
-
-#: src/tools/pw-cat.c:766
-#, c-format
-msgid " %s -> %s\n"
-msgstr " %s -> %s\n"
-
-#: src/tools/pw-cat.c:771
-#, c-format
-msgid "Supported channel names:\n"
-msgstr "Podržani nazivi kanala:\n"
-
-#: src/tools/pw-cat.c:1183
+#: src/tools/pw-cat.c:991
#, c-format
msgid ""
-"%s [options] [|-]\n"
+"%s [options] \n"
" -h, --help Show this help\n"
" --version Show version\n"
" -v, --verbose Enable verbose operations\n"
"\n"
msgstr ""
-"%s [opcije] [|-]\n"
-" -h, --help Prikaži ovu pomoć\n"
-" --version Prikaži izdanje\n"
-" -v, --verbose Omogući opširan rad\n"
-"\n"
-#: src/tools/pw-cat.c:1190
+#: src/tools/pw-cat.c:998
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
" --media-type Set media type (default %s)\n"
" --media-category Set media category (default %s)\n"
" --media-role Set media role (default %s)\n"
-" --target Set node target serial or name "
-"(default %s)\n"
+" --target Set node target (default %s)\n"
" 0 means don't link\n"
-" -C --monitor Capture monitor ports (in recording "
-"mode)\n"
" --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n"
" the rate is the one of the source "
"file\n"
-" -P --properties Set node properties\n"
+" --list-targets List available targets for --target\n"
"\n"
msgstr ""
-" -R, --remote Naziv udaljenog uslužnog programa\n"
-" --media-type Postavi vrstu medija (podrazumevano "
-"%s)\n"
-" --media-category Postavi kategoriju medija "
-"(podrazumevano %s)\n"
-" --media-role Postavi ulogu medija (podrazumevano "
-"%s)\n"
-" --target Postavi ciljni serijski broj ili "
-"naziv čvora (podrazumevano %s)\n"
-" 0 znači bez povezivanja\n"
-" -C --monitor Hvataj priključnike za nadzor (u "
-"režimu snimanja)\n"
-" --latency Postavi latentnost čvora "
-"(podrazumevano %s)\n"
-" Xjedinica (jedinica = s, ms, us, "
-"ns)\n"
-" ili direktni uzorci (256)\n"
-" protok je onaj iz izvorne "
-"datoteke\n"
-" -P --properties Postavi svojstva čvora\n"
-"\n"
-#: src/tools/pw-cat.c:1209
+#: src/tools/pw-cat.c:1016
#, c-format
msgid ""
-" --rate Sample rate (default %u)\n"
-" --channels Number of channels (default %u)\n"
+" --rate Sample rate (req. for rec) (default "
+"%u)\n"
+" --channels Number of channels (req. for rec) "
+"(default %u)\n"
" --channel-map Channel map\n"
-" a channel layout: \"Stereo\", "
-"\"5.1\",... or\n"
+" one of: \"stereo\", "
+"\"surround-51\",... or\n"
" comma separated list of channel "
"names: eg. \"FL,FR\"\n"
-" --list-layouts List supported channel layouts\n"
-" --list-channel-names List supported channel maps\n"
-" --format Sample format (default %s)\n"
-" --list-formats List supported sample formats\n"
-" --container Container format\n"
-" --list-containers List supported containers and "
-"extensions\n"
+" --format Sample format %s (req. for rec) "
+"(default %s)\n"
" --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default "
"%d)\n"
-" -a, --raw RAW mode\n"
-" -M, --force-midi Force midi format, one of \"midi\" "
-"or \"ump\", (default ump)\n"
-" -n, --sample-count COUNT Stop after COUNT samples\n"
"\n"
msgstr ""
-" --rate Učestalost uzorkovanja (podrazumevano "
-"%u)\n"
-" --channels Broj kanala (podrazumevano %u)\n"
-" --channel-map Mapa kanala\n"
-" raspored kanala: „Stereo“, "
-"„5.1“,... ili\n"
-" zarezom razdvojen spisak naziva "
-"kanala: npr. „FL,FR“\n"
-" --list-layouts Ispiši podržane rasporede kanala\n"
-" --list-channel-names Ispiši podržane mape kanala\n"
-" --format Format uzorka (podrazumevano %s)\n"
-" --list-formats Ispiši podržane formate uzoraka\n"
-" --container Format kontejnera\n"
-" --list-containers Ispiši podržane kontejnere i "
-"proširenja\n"
-" --volume Jačina toka 0-1.0 (podrazumevano "
-"%.3f)\n"
-" -q --quality Kvalitet promene uzorkovanja (0 - 15) "
-"(podrazumevano %d)\n"
-" -a, --raw Sirovi (RAW) režim\n"
-" -M, --force-midi Primoraj MIDI format, jedan od "
-"„midi“ ili „ump“, (podrazumevano ump)\n"
-" -n, --sample-count BROJ Zaustavi nakon BROJ uzoraka\n"
-"\n"
-#: src/tools/pw-cat.c:1234
+#: src/tools/pw-cat.c:1033
msgid ""
" -p, --playback Playback mode\n"
" -r, --record Recording mode\n"
" -m, --midi Midi mode\n"
-" -d, --dsd DSD mode\n"
-" -o, --encoded Encoded mode\n"
-" -s, --sysex SysEx mode\n"
-" -c, --midi-clip MIDI clip mode\n"
"\n"
msgstr ""
-" -p, --playback Režim puštanja\n"
-" -r, --record Režim snimanja\n"
-" -m, --midi MIDI režim\n"
-" -d, --dsd DSD režim\n"
-" -o, --encoded Kodirani režim\n"
-" -s, --sysex SysEx režim\n"
-" -c, --midi-clip Režim MIDI odsečka\n"
-"\n"
-#: src/tools/pw-cat.c:1839
-#, c-format
-msgid "Supported containers and extensions:\n"
-msgstr "Podržani kontejneri i proširenja:\n"
-
-#: src/tools/pw-cli.c:2386
+#: src/tools/pw-cli.c:2932
#, c-format
msgid ""
"%s [options] [command]\n"
@@ -243,365 +115,376 @@ msgid ""
" --version Show version\n"
" -d, --daemon Start as daemon (Default false)\n"
" -r, --remote Remote daemon name\n"
-" -m, --monitor Monitor activity\n"
"\n"
msgstr ""
-"%s [opcije] [naredba]\n"
-" -h, --help Prikaži ovu pomoć\n"
-" --version Prikaži izdanje\n"
-" -d, --daemon Pokreni kao uslužni program "
-"(podrazumevano netačno)\n"
-" -r, --remote Naziv udaljenog uslužnog programa\n"
-" -m, --monitor Nadgledaj aktivnost\n"
-"\n"
-#: spa/plugins/alsa/acp/acp.c:361
+#: spa/plugins/alsa/acp/acp.c:290
msgid "Pro Audio"
-msgstr "Profesionalni zvuk"
+msgstr ""
-#: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
-#: spa/plugins/bluez5/bluez5-device.c:2163
+#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704
+#: spa/plugins/bluez5/bluez5-device.c:1000
msgid "Off"
msgstr "Isključeno"
-#: spa/plugins/alsa/acp/acp.c:618
-#, c-format
-msgid "%s [ALSA UCM error]"
-msgstr "%s [ALSA UCM greška]"
+#: spa/plugins/alsa/acp/channelmap.h:466
+msgid "(invalid)"
+msgstr "(neispravno)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2721
+#: spa/plugins/alsa/acp/alsa-mixer.c:2709
msgid "Input"
msgstr "Ulaz"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2722
+#: spa/plugins/alsa/acp/alsa-mixer.c:2710
msgid "Docking Station Input"
msgstr "Ulaz priključne stanice"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2723
+#: spa/plugins/alsa/acp/alsa-mixer.c:2711
+#, fuzzy
msgid "Docking Station Microphone"
msgstr "Mikrofon priključne stanice"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2724
+#: spa/plugins/alsa/acp/alsa-mixer.c:2712
+#, fuzzy
msgid "Docking Station Line In"
msgstr "Ulaz priključne stanice"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2725
-#: spa/plugins/alsa/acp/alsa-mixer.c:2816
+#: spa/plugins/alsa/acp/alsa-mixer.c:2713
+#: spa/plugins/alsa/acp/alsa-mixer.c:2804
msgid "Line In"
msgstr "Linija u"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2726
-#: spa/plugins/alsa/acp/alsa-mixer.c:2810
-#: spa/plugins/bluez5/bluez5-device.c:2596
+#: spa/plugins/alsa/acp/alsa-mixer.c:2714
+#: spa/plugins/alsa/acp/alsa-mixer.c:2798
+#: spa/plugins/bluez5/bluez5-device.c:1145
msgid "Microphone"
msgstr "Mikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2727
-#: spa/plugins/alsa/acp/alsa-mixer.c:2811
+#: spa/plugins/alsa/acp/alsa-mixer.c:2715
+#: spa/plugins/alsa/acp/alsa-mixer.c:2799
+#, fuzzy
msgid "Front Microphone"
-msgstr "Prednji mikrofon"
+msgstr "Mikrofon priključne stanice"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2728
-#: spa/plugins/alsa/acp/alsa-mixer.c:2812
+#: spa/plugins/alsa/acp/alsa-mixer.c:2716
+#: spa/plugins/alsa/acp/alsa-mixer.c:2800
+#, fuzzy
msgid "Rear Microphone"
-msgstr "Zadnji mikrofon"
+msgstr "Mikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2729
+#: spa/plugins/alsa/acp/alsa-mixer.c:2717
msgid "External Microphone"
msgstr "Spoljni mikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2730
-#: spa/plugins/alsa/acp/alsa-mixer.c:2814
+#: spa/plugins/alsa/acp/alsa-mixer.c:2718
+#: spa/plugins/alsa/acp/alsa-mixer.c:2802
msgid "Internal Microphone"
msgstr "Unutrašnji mikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2731
-#: spa/plugins/alsa/acp/alsa-mixer.c:2817
+#: spa/plugins/alsa/acp/alsa-mixer.c:2719
+#: spa/plugins/alsa/acp/alsa-mixer.c:2805
msgid "Radio"
msgstr "Radio"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2732
-#: spa/plugins/alsa/acp/alsa-mixer.c:2818
+#: spa/plugins/alsa/acp/alsa-mixer.c:2720
+#: spa/plugins/alsa/acp/alsa-mixer.c:2806
msgid "Video"
msgstr "Video"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2733
+#: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Automatic Gain Control"
msgstr "Samostalna kontrola pojačanja"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2734
+#: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "No Automatic Gain Control"
msgstr "Bez samostalne kontrole pojačanja"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2735
+#: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Boost"
msgstr "Podizanje"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2736
+#: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "No Boost"
msgstr "Bez podizanja"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2737
+#: spa/plugins/alsa/acp/alsa-mixer.c:2725
msgid "Amplifier"
msgstr "Pojačalo"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2738
+#: spa/plugins/alsa/acp/alsa-mixer.c:2726
msgid "No Amplifier"
msgstr "Bez pojačala"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2739
+#: spa/plugins/alsa/acp/alsa-mixer.c:2727
+#, fuzzy
msgid "Bass Boost"
-msgstr "Pojačanje basa"
+msgstr "Podizanje"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2740
+#: spa/plugins/alsa/acp/alsa-mixer.c:2728
+#, fuzzy
msgid "No Bass Boost"
-msgstr "Bez pojačanja basa"
+msgstr "Bez podizanja"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2741
-#: spa/plugins/bluez5/bluez5-device.c:2602
+#: spa/plugins/alsa/acp/alsa-mixer.c:2729
+#: spa/plugins/bluez5/bluez5-device.c:1150
msgid "Speaker"
-msgstr "Zvučnik"
+msgstr ""
-#. Don't call it "headset", the HF one has the mic
-#: spa/plugins/alsa/acp/alsa-mixer.c:2742
-#: spa/plugins/alsa/acp/alsa-mixer.c:2820
-#: spa/plugins/bluez5/bluez5-device.c:2608
-#: spa/plugins/bluez5/bluez5-device.c:2675
+#: spa/plugins/alsa/acp/alsa-mixer.c:2730
+#: spa/plugins/alsa/acp/alsa-mixer.c:2808
msgid "Headphones"
msgstr "Analogne slušalice"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2809
+#: spa/plugins/alsa/acp/alsa-mixer.c:2797
msgid "Analog Input"
msgstr "Analogni ulaz"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2813
+#: spa/plugins/alsa/acp/alsa-mixer.c:2801
msgid "Dock Microphone"
msgstr "Mikrofon priključne stanice"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2815
+#: spa/plugins/alsa/acp/alsa-mixer.c:2803
+#, fuzzy
msgid "Headset Microphone"
-msgstr "Mikrofon sa slušalicama"
+msgstr "Mikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2819
+#: spa/plugins/alsa/acp/alsa-mixer.c:2807
msgid "Analog Output"
msgstr "Analogni izlaz"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2821
+#: spa/plugins/alsa/acp/alsa-mixer.c:2809
+#, fuzzy
msgid "Headphones 2"
-msgstr "Slušalice 2"
+msgstr "Analogne slušalice"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2822
+#: spa/plugins/alsa/acp/alsa-mixer.c:2810
+#, fuzzy
msgid "Headphones Mono Output"
-msgstr "Mono izlaz za slušalice"
+msgstr "Analogni mono izlaz"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2823
+#: spa/plugins/alsa/acp/alsa-mixer.c:2811
+#, fuzzy
msgid "Line Out"
-msgstr "Linijski izlaz"
+msgstr "Linija u"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2824
+#: spa/plugins/alsa/acp/alsa-mixer.c:2812
msgid "Analog Mono Output"
msgstr "Analogni mono izlaz"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2825
+#: spa/plugins/alsa/acp/alsa-mixer.c:2813
+#, fuzzy
msgid "Speakers"
-msgstr "Govornici"
+msgstr "Analogni stereo"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2826
+#: spa/plugins/alsa/acp/alsa-mixer.c:2814
msgid "HDMI / DisplayPort"
-msgstr "HDMI / Displej-port"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:2827
+#: spa/plugins/alsa/acp/alsa-mixer.c:2815
+#, fuzzy
msgid "Digital Output (S/PDIF)"
-msgstr "Digitalni izlaz (S/PDIF)"
+msgstr "Digitalni stereo (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2828
+#: spa/plugins/alsa/acp/alsa-mixer.c:2816
+#, fuzzy
msgid "Digital Input (S/PDIF)"
-msgstr "Digitalni ulaz (S/PDIF)"
+msgstr "Digitalni stereo (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2829
+#: spa/plugins/alsa/acp/alsa-mixer.c:2817
msgid "Multichannel Input"
-msgstr "Višekanalni ulaz"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:2830
+#: spa/plugins/alsa/acp/alsa-mixer.c:2818
+#, fuzzy
msgid "Multichannel Output"
-msgstr "Višekanalni izlaz"
+msgstr "Prazan izlaz"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2831
+#: spa/plugins/alsa/acp/alsa-mixer.c:2819
+#, fuzzy
msgid "Game Output"
-msgstr "Izlaz za igre"
+msgstr "Prazan izlaz"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2832
-#: spa/plugins/alsa/acp/alsa-mixer.c:2833
+#: spa/plugins/alsa/acp/alsa-mixer.c:2820
+#: spa/plugins/alsa/acp/alsa-mixer.c:2821
+#, fuzzy
msgid "Chat Output"
-msgstr "Izlaz za ćaskanje"
+msgstr "Prazan izlaz"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2834
+#: spa/plugins/alsa/acp/alsa-mixer.c:2822
+#, fuzzy
msgid "Chat Input"
-msgstr "Ulaz za ćaskanje"
+msgstr "Ulaz"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2835
+#: spa/plugins/alsa/acp/alsa-mixer.c:2823
+#, fuzzy
msgid "Virtual Surround 7.1"
-msgstr "Virtuelni okružujući zvuk 7.1"
+msgstr "Analogni okružujući 7.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4522
+#: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Analog Mono"
msgstr "Analogni mono"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4523
+#: spa/plugins/alsa/acp/alsa-mixer.c:4528
+#, fuzzy
msgid "Analog Mono (Left)"
-msgstr "Analogni mono (levi)"
+msgstr "Analogni mono"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4524
+#: spa/plugins/alsa/acp/alsa-mixer.c:4529
+#, fuzzy
msgid "Analog Mono (Right)"
-msgstr "Analogni mono (desni)"
+msgstr "Analogni mono"
#. Note: Not translated to "Analog Stereo Input", because the source
#. * name gets "Input" appended to it automatically, so adding "Input"
#. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output.
-#: spa/plugins/alsa/acp/alsa-mixer.c:4525
-#: spa/plugins/alsa/acp/alsa-mixer.c:4533
-#: spa/plugins/alsa/acp/alsa-mixer.c:4534
+#: spa/plugins/alsa/acp/alsa-mixer.c:4530
+#: spa/plugins/alsa/acp/alsa-mixer.c:4538
+#: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Stereo"
msgstr "Analogni stereo"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4526
+#: spa/plugins/alsa/acp/alsa-mixer.c:4531
msgid "Mono"
msgstr "Mono"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4527
+#: spa/plugins/alsa/acp/alsa-mixer.c:4532
msgid "Stereo"
msgstr "Stereo"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4535
-#: spa/plugins/alsa/acp/alsa-mixer.c:4693
-#: spa/plugins/bluez5/bluez5-device.c:2584
+#: spa/plugins/alsa/acp/alsa-mixer.c:4540
+#: spa/plugins/alsa/acp/alsa-mixer.c:4698
+#: spa/plugins/bluez5/bluez5-device.c:1135
msgid "Headset"
-msgstr "Slušalice sa mikrofonom"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4536
-#: spa/plugins/alsa/acp/alsa-mixer.c:4694
+#: spa/plugins/alsa/acp/alsa-mixer.c:4541
+#: spa/plugins/alsa/acp/alsa-mixer.c:4699
+#, fuzzy
msgid "Speakerphone"
-msgstr "Spikerfon"
+msgstr "Analogni stereo"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4537
-#: spa/plugins/alsa/acp/alsa-mixer.c:4538
+#: spa/plugins/alsa/acp/alsa-mixer.c:4542
+#: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Multichannel"
-msgstr "Višekanalni"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4539
+#: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 2.1"
msgstr "Analogni okružujući 2.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4540
+#: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 3.0"
msgstr "Analogni okružujući 3.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4541
+#: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 3.1"
msgstr "Analogni okružujući 3.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4542
+#: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 4.0"
msgstr "Analogni okružujući 4.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4543
+#: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 4.1"
msgstr "Analogni okružujući 4.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4544
+#: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 5.0"
msgstr "Analogni okružujući 5.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4545
+#: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Analog Surround 5.1"
msgstr "Analogni okružujući 5.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4546
+#: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Analog Surround 6.0"
msgstr "Analogni okružujući 6.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4547
+#: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Analog Surround 6.1"
msgstr "Analogni okružujući 6.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4548
+#: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Analog Surround 7.0"
msgstr "Analogni okružujući 7.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4549
+#: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Analog Surround 7.1"
msgstr "Analogni okružujući 7.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4550
+#: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Stereo (IEC958)"
msgstr "Digitalni stereo (IEC958)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4551
+#: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Digitalni okružujući 4.0 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4552
+#: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Digitalni okružujući 5.1 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4553
+#: spa/plugins/alsa/acp/alsa-mixer.c:4558
+#, fuzzy
msgid "Digital Surround 5.1 (IEC958/DTS)"
-msgstr "Digitalni okružujući 5.1 (IEC958/DTS)"
+msgstr "Digitalni okružujući 5.1 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4554
+#: spa/plugins/alsa/acp/alsa-mixer.c:4559
msgid "Digital Stereo (HDMI)"
msgstr "Digitalni stereo (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4555
+#: spa/plugins/alsa/acp/alsa-mixer.c:4560
+#, fuzzy
msgid "Digital Surround 5.1 (HDMI)"
-msgstr "Digitalni okružujući 5.1 (HDMI)"
+msgstr "Digitalni okružujući 5.1 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4556
+#: spa/plugins/alsa/acp/alsa-mixer.c:4561
msgid "Chat"
-msgstr "Ćaskanje"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4557
+#: spa/plugins/alsa/acp/alsa-mixer.c:4562
msgid "Game"
-msgstr "Igrica"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4691
+#: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Analog Mono Duplex"
msgstr "Dvosmerni analogni mono"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4692
+#: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Analog Stereo Duplex"
msgstr "Dvosmerni analogni stereo"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4695
+#: spa/plugins/alsa/acp/alsa-mixer.c:4700
msgid "Digital Stereo Duplex (IEC958)"
msgstr "Dvosmerni digitalni stereo (IEC958)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4696
+#: spa/plugins/alsa/acp/alsa-mixer.c:4701
msgid "Multichannel Duplex"
-msgstr "Višekanalni dupleks"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4697
+#: spa/plugins/alsa/acp/alsa-mixer.c:4702
+#, fuzzy
msgid "Stereo Duplex"
-msgstr "Stereo dupleks"
+msgstr "Dvosmerni analogni stereo"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4698
+#: spa/plugins/alsa/acp/alsa-mixer.c:4703
msgid "Mono Chat + 7.1 Surround"
-msgstr "Mono ćaskanje + 7.1 okružujući"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4799
-#, c-format
+#: spa/plugins/alsa/acp/alsa-mixer.c:4806
+#, fuzzy, c-format
msgid "%s Output"
-msgstr "%s izlaz"
+msgstr "Prazan izlaz"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4807
-#, c-format
+#: spa/plugins/alsa/acp/alsa-mixer.c:4813
+#, fuzzy, c-format
msgid "%s Input"
-msgstr "%s ulaz"
+msgstr "Ulaz"
-#: spa/plugins/alsa/acp/alsa-util.c:1233 spa/plugins/alsa/acp/alsa-util.c:1327
-#, c-format
+#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269
+#, fuzzy, c-format
msgid ""
"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
"ms).\n"
@@ -613,9 +496,9 @@ msgid_plural ""
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] ""
-"snd_pcm_avail() je vratio vrednost koja je izuzetno velika: %lu bajt (%lu "
+"snd_pcm_avail() je vratio vrednost koja je neobično velika: %lu bajtova (%lu "
"ms).\n"
-"Najverovatnije je ovo greška u ALSA upravljačkom programu „%s“. Prijavite "
+"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
"ovaj problem ALSA programerima."
msgstr[1] ""
"snd_pcm_avail() je vratio vrednost koja je neobično velika: %lu bajtova (%lu "
@@ -628,49 +511,49 @@ msgstr[2] ""
"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
"ovaj problem ALSA programerima."
-#: spa/plugins/alsa/acp/alsa-util.c:1299
-#, c-format
+#: spa/plugins/alsa/acp/alsa-util.c:1241
+#, fuzzy, c-format
msgid ""
-"snd_pcm_delay() returned a value that is exceptionally large: %li byte "
-"(%s%lu ms).\n"
+"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
+"%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgid_plural ""
-"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
-"(%s%lu ms).\n"
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] ""
-"snd_pcm_delay() je vratio vrednost koja je izuzetno velika: %li bajt (%s%lu "
-"ms).\n"
-"Najverovatnije je ovo greška u ALSA upravljačkom programu „%s“. Prijavite "
+"snd_pcm_delay() je vratio vrednost koja je neobično velika: %li bajtova (%s"
+"%lu ms).\n"
+"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
"ovaj problem ALSA programerima."
msgstr[1] ""
-"snd_pcm_delay() je vratio vrednost koja je neobično velika: %li bajtova "
-"(%s%lu ms).\n"
+"snd_pcm_delay() je vratio vrednost koja je neobično velika: %li bajtova (%s"
+"%lu ms).\n"
"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
"ovaj problem ALSA programerima."
msgstr[2] ""
-"snd_pcm_delay() je vratio vrednost koja je neobično velika: %li bajtova "
-"(%s%lu ms).\n"
+"snd_pcm_delay() je vratio vrednost koja je neobično velika: %li bajtova (%s"
+"%lu ms).\n"
"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
"ovaj problem ALSA programerima."
-#: spa/plugins/alsa/acp/alsa-util.c:1346
-#, c-format
+#: spa/plugins/alsa/acp/alsa-util.c:1288
+#, fuzzy, c-format
msgid ""
"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
"%lu.\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr ""
-"snd_pcm_avail_delay() je vratio čudne vrednosti: kašnjenje %lu je manje od "
-"dostupnog %lu.\n"
-"Najverovatnije je ovo greška u ALSA upravljačkom programu „%s“. Prijavite "
+"snd_pcm_avail() je vratio vrednost koja je neobično velika: %lu bajtova (%lu "
+"ms).\n"
+"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
"ovaj problem ALSA programerima."
-#: spa/plugins/alsa/acp/alsa-util.c:1389
-#, c-format
+#: spa/plugins/alsa/acp/alsa-util.c:1331
+#, fuzzy, c-format
msgid ""
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
"(%lu ms).\n"
@@ -682,9 +565,9 @@ msgid_plural ""
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] ""
-"snd_pcm_mmap_begin() je vratio vrednost koja je izuzetno velika: %lu bajt "
-"(%lu ms).\n"
-"Najverovatnije je ovo greška u ALSA upravljačkom programu „%s“. Prijavite "
+"snd_pcm_mmap_begin() je vratio vrednost koja je neobično velika: %lu "
+"bajtova (%lu ms).\n"
+"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
"ovaj problem ALSA programerima."
msgstr[1] ""
"snd_pcm_mmap_begin() je vratio vrednost koja je neobično velika: %lu "
@@ -697,120 +580,62 @@ msgstr[2] ""
"Ovo je najverovatnije greška u „%s“ ALSA upravljačkom programu. Prijavite "
"ovaj problem ALSA programerima."
-#: spa/plugins/alsa/acp/channelmap.h:460
-msgid "(invalid)"
-msgstr "(neispravno)"
-
-#: spa/plugins/alsa/acp/compat.c:194
-msgid "Built-in Audio"
-msgstr "Unutrašnji zvuk"
-
-#: spa/plugins/alsa/acp/compat.c:199
-msgid "Modem"
-msgstr "Modem"
-
-#: spa/plugins/bluez5/bluez5-device.c:2174
+#: spa/plugins/bluez5/bluez5-device.c:1010
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
-msgstr "Zvučni mrežni prolaz (A2DP izvor i HSP/HFP AG)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2203
-msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
-msgstr "Strujanje zvuka za slušne aparate (ASHA slivnik)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2246
+#: spa/plugins/bluez5/bluez5-device.c:1033
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
-msgstr "Puštanje visoke vernosti (A2DP slivnik, kodek %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2249
+#: spa/plugins/bluez5/bluez5-device.c:1035
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
-msgstr "Dvosmerno visoke vernosti (A2DP izvor/slivnik, kodek %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2257
+#: spa/plugins/bluez5/bluez5-device.c:1041
msgid "High Fidelity Playback (A2DP Sink)"
-msgstr "Puštanje visoke vernosti (A2DP slivnik)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2259
+#: spa/plugins/bluez5/bluez5-device.c:1043
msgid "High Fidelity Duplex (A2DP Source/Sink)"
-msgstr "Dvosmerno visoke vernosti (A2DP izvor/slivnik)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2281
-msgid "Auto: Prefer Quality (A2DP)"
-msgstr "Auto: prednost kvalitetu (A2DP)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2286
-msgid "Auto: Prefer Latency (A2DP)"
-msgstr "Auto: prednost kašnjenju (A2DP)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2366
-#, c-format
-msgid "High Fidelity Playback (BAP Sink, codec %s)"
-msgstr "Puštanje visoke vernosti (BAP slivnik, kodek %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2371
-#, c-format
-msgid "High Fidelity Input (BAP Source, codec %s)"
-msgstr "Ulaz visoke vernosti (BAP izvor, kodek %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2375
-#, c-format
-msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
-msgstr "Dvosmerno visoke vernosti (BAP izvor/slivnik, kodek %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2384
-msgid "High Fidelity Playback (BAP Sink)"
-msgstr "Puštanje visoke vernosti (BAP slivnik)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2388
-msgid "High Fidelity Input (BAP Source)"
-msgstr "Ulaz visoke vernosti (BAP izvor)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2391
-msgid "High Fidelity Duplex (BAP Source/Sink)"
-msgstr "Dvosmerno visoke vernosti (BAP izvor/slivnik)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2431
+#: spa/plugins/bluez5/bluez5-device.c:1070
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
-msgstr "Slušalice sa mikrofonom (HSP/HFP, kodek %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2585
-#: spa/plugins/bluez5/bluez5-device.c:2590
-#: spa/plugins/bluez5/bluez5-device.c:2597
-#: spa/plugins/bluez5/bluez5-device.c:2603
-#: spa/plugins/bluez5/bluez5-device.c:2609
-#: spa/plugins/bluez5/bluez5-device.c:2615
-#: spa/plugins/bluez5/bluez5-device.c:2621
-#: spa/plugins/bluez5/bluez5-device.c:2627
-#: spa/plugins/bluez5/bluez5-device.c:2633
+#: spa/plugins/bluez5/bluez5-device.c:1074
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr ""
+
+#: spa/plugins/bluez5/bluez5-device.c:1140
msgid "Handsfree"
-msgstr "Hendsfri"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2591
-msgid "Handsfree (HFP)"
-msgstr "Hendsfri (HFP)"
+#: spa/plugins/bluez5/bluez5-device.c:1155
+#, fuzzy
+msgid "Headphone"
+msgstr "Analogne slušalice"
-#: spa/plugins/bluez5/bluez5-device.c:2614
+#: spa/plugins/bluez5/bluez5-device.c:1160
msgid "Portable"
-msgstr "Prenosno"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2620
+#: spa/plugins/bluez5/bluez5-device.c:1165
msgid "Car"
-msgstr "Kola"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2626
+#: spa/plugins/bluez5/bluez5-device.c:1170
msgid "HiFi"
-msgstr "Hi-fi"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2632
+#: spa/plugins/bluez5/bluez5-device.c:1175
msgid "Phone"
-msgstr "Telefon"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2639
+#: spa/plugins/bluez5/bluez5-device.c:1181
msgid "Bluetooth"
-msgstr "Blutut"
-
-#: spa/plugins/bluez5/bluez5-device.c:2640
-msgid "Bluetooth Handsfree"
-msgstr "Blutut hendsfri"
+msgstr ""
diff --git a/po/sv.po b/po/sv.po
index 1474f5084..0bc2796a4 100644
--- a/po/sv.po
+++ b/po/sv.po
@@ -1,9 +1,9 @@
# Swedish translation for pipewire.
-# Copyright © 2008-2026 Free Software Foundation, Inc.
+# Copyright © 2008-2025 Free Software Foundation, Inc.
# This file is distributed under the same license as the pipewire package.
# Daniel Nylander , 2008, 2012.
# Josef Andersson , 2014, 2017.
-# Anders Jonsson , 2021, 2022, 2023, 2024, 2025, 2026.
+# Anders Jonsson , 2021, 2022, 2023, 2024, 2025.
#
# Termer:
# input/output: ingång/utgång (det handlar om ljud)
@@ -19,8 +19,8 @@ msgstr ""
"Project-Id-Version: pipewire\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
"issues\n"
-"POT-Creation-Date: 2026-02-09 12:55+0000\n"
-"PO-Revision-Date: 2026-02-22 21:48+0100\n"
+"POT-Creation-Date: 2025-04-16 15:31+0000\n"
+"PO-Revision-Date: 2025-04-22 10:43+0200\n"
"Last-Translator: Anders Jonsson \n"
"Language-Team: Swedish \n"
"Language: sv\n"
@@ -28,7 +28,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Poedit 3.8\n"
+"X-Generator: Poedit 3.5\n"
#: src/daemon/pipewire.c:29
#, c-format
@@ -47,11 +47,11 @@ msgstr ""
" -c, --config Läs in konfig (standard %s)\n"
" -P --properties Ställ in kontextegenskaper\n"
-#: src/daemon/pipewire.desktop.in:3
+#: src/daemon/pipewire.desktop.in:4
msgid "PipeWire Media System"
msgstr "PipeWire mediasystem"
-#: src/daemon/pipewire.desktop.in:4
+#: src/daemon/pipewire.desktop.in:5
msgid "Start the PipeWire Media System"
msgstr "Starta mediasystemet PipeWire"
@@ -65,51 +65,26 @@ msgstr "Tunnel till %s%s%s"
msgid "Dummy Output"
msgstr "Attrapputgång"
-#: src/modules/module-pulse-tunnel.c:761
+#: src/modules/module-pulse-tunnel.c:760
#, c-format
msgid "Tunnel for %s@%s"
msgstr "Tunnel för %s@%s"
-#: src/modules/module-zeroconf-discover.c:326
+#: src/modules/module-zeroconf-discover.c:320
msgid "Unknown device"
msgstr "Okänd enhet"
-#: src/modules/module-zeroconf-discover.c:338
+#: src/modules/module-zeroconf-discover.c:332
#, c-format
msgid "%s on %s@%s"
msgstr "%s på %s@%s"
-#: src/modules/module-zeroconf-discover.c:342
+#: src/modules/module-zeroconf-discover.c:336
#, c-format
msgid "%s on %s"
msgstr "%s på %s"
-#: src/tools/pw-cat.c:264
-#, c-format
-msgid "Supported formats:\n"
-msgstr "Format som stöds:\n"
-
-#: src/tools/pw-cat.c:749
-#, c-format
-msgid "Supported channel layouts:\n"
-msgstr "Kanallayouter som stöds:\n"
-
-#: src/tools/pw-cat.c:759
-#, c-format
-msgid "Supported channel layout aliases:\n"
-msgstr "Kanallayoutalias som stöds:\n"
-
-#: src/tools/pw-cat.c:761
-#, c-format
-msgid " %s -> %s\n"
-msgstr " %s -> %s\n"
-
-#: src/tools/pw-cat.c:766
-#, c-format
-msgid "Supported channel names:\n"
-msgstr "Kanalnamn som stöds:\n"
-
-#: src/tools/pw-cat.c:1177
+#: src/tools/pw-cat.c:973
#, c-format
msgid ""
"%s [options] [|-]\n"
@@ -124,7 +99,7 @@ msgstr ""
" -v, --verbose Aktivera utförliga operationer\n"
"\n"
-#: src/tools/pw-cat.c:1184
+#: src/tools/pw-cat.c:980
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
@@ -156,64 +131,50 @@ msgstr ""
" -P --properties Sätt nodegenskaper\n"
"\n"
-#: src/tools/pw-cat.c:1202
+#: src/tools/pw-cat.c:998
#, c-format
msgid ""
-" --rate Sample rate (default %u)\n"
-" --channels Number of channels (default %u)\n"
+" --rate Sample rate (req. for rec) (default "
+"%u)\n"
+" --channels Number of channels (req. for rec) "
+"(default %u)\n"
" --channel-map Channel map\n"
-" a channel layout: \"Stereo\", "
-"\"5.1\",... or\n"
+" one of: \"stereo\", "
+"\"surround-51\",... or\n"
" comma separated list of channel "
"names: eg. \"FL,FR\"\n"
-" --list-layouts List supported channel layouts\n"
-" --list-channel-names List supported channel maps\n"
-" --format Sample format (default %s)\n"
-" --list-formats List supported sample formats\n"
-" --container Container format\n"
-" --list-containers List supported containers and "
-"extensions\n"
+" --format Sample format %s (req. for rec) "
+"(default %s)\n"
" --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default "
"%d)\n"
" -a, --raw RAW mode\n"
-" -M, --force-midi Force midi format, one of \"midi\" "
-"or \"ump\", (default ump)\n"
-" -n, --sample-count COUNT Stop after COUNT samples\n"
"\n"
msgstr ""
-" --rate Samplingsfrekvens (standard %u)\n"
-" --channels Antal kanaler (standard %u)\n"
+" --rate Samplingsfrekvens (krävs för insp.) "
+"(standard %u)\n"
+" --channels Antal kanaler (krävs för insp.) "
+"(standard %u)\n"
" --channel-map Kanalmappning\n"
-" en kanallayout: \"Stereo\", "
-"\"5.1\",... eller\n"
+" en av: \"stereo\", "
+"\"surround-51\",... eller\n"
" kommaseparerad lista av "
"kanalnamn: t.ex. \"FL,FR\"\n"
-" --list-layouts Lista kanallayouter som stöds\n"
-" --list-channel-names Lista kanalmappningar som stöds\n"
-" --format Samplingsformat (standard %s)\n"
-" --list-formats Lista samplingsformat som stöds\n"
-" --container Behållarformat\n"
-" --list-containers Lista behållare och tillägg som "
-"stöds\n"
+" --format Samplingsformat %s (krävs för insp.) "
+"(standard %s)\n"
" --volume Strömvolym 0-1.0 (standard %.3f)\n"
" -q --quality Omsamplarkvalitet (0 - 15) (standard "
"%d)\n"
" -a, --raw RAW-läge\n"
-" -M, --force-midi Tvinga midi-format, en av \"midi\" "
-"eller \"ump\", (standard ump)\n"
-" -n, --sample-count ANTAL Stoppa efter ANTAL samplar\n"
"\n"
-#: src/tools/pw-cat.c:1227
+#: src/tools/pw-cat.c:1016
msgid ""
" -p, --playback Playback mode\n"
" -r, --record Recording mode\n"
" -m, --midi Midi mode\n"
" -d, --dsd DSD mode\n"
" -o, --encoded Encoded mode\n"
-" -s, --sysex SysEx mode\n"
-" -c, --midi-clip MIDI clip mode\n"
"\n"
msgstr ""
" -p, --playback Uppspelningsläge\n"
@@ -221,16 +182,9 @@ msgstr ""
" -m, --midi Midiläge\n"
" -d, --dsd DSD-läge\n"
" -o, --encoded Kodat läge\n"
-" -s, --sysex SysEx-läge\n"
-" -c, --midi-clip MIDI-klippläge\n"
"\n"
-#: src/tools/pw-cat.c:1827
-#, c-format
-msgid "Supported containers and extensions:\n"
-msgstr "Behållare och tillägg som stöds:\n"
-
-#: src/tools/pw-cli.c:2386
+#: src/tools/pw-cli.c:2306
#, c-format
msgid ""
"%s [options] [command]\n"
@@ -249,203 +203,200 @@ msgstr ""
" -m, --monitor Övervaka aktivitet\n"
"\n"
-#: spa/plugins/alsa/acp/acp.c:361
+#: spa/plugins/alsa/acp/acp.c:350
msgid "Pro Audio"
msgstr "Professionellt ljud"
-#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
-#: spa/plugins/bluez5/bluez5-device.c:2021
+#: spa/plugins/alsa/acp/acp.c:511 spa/plugins/alsa/acp/alsa-mixer.c:4635
+#: spa/plugins/bluez5/bluez5-device.c:1802
msgid "Off"
msgstr "Av"
-#: spa/plugins/alsa/acp/acp.c:620
+#: spa/plugins/alsa/acp/acp.c:593
#, c-format
msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM-fel]"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2721
+#: spa/plugins/alsa/acp/alsa-mixer.c:2652
msgid "Input"
msgstr "Ingång"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2722
+#: spa/plugins/alsa/acp/alsa-mixer.c:2653
msgid "Docking Station Input"
msgstr "Ingång för dockningsstation"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2723
+#: spa/plugins/alsa/acp/alsa-mixer.c:2654
msgid "Docking Station Microphone"
msgstr "Mikrofon för dockningsstation"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2724
+#: spa/plugins/alsa/acp/alsa-mixer.c:2655
msgid "Docking Station Line In"
msgstr "Linje in för dockningsstation"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2725
-#: spa/plugins/alsa/acp/alsa-mixer.c:2816
+#: spa/plugins/alsa/acp/alsa-mixer.c:2656
+#: spa/plugins/alsa/acp/alsa-mixer.c:2747
msgid "Line In"
msgstr "Linje in"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2726
-#: spa/plugins/alsa/acp/alsa-mixer.c:2810
-#: spa/plugins/bluez5/bluez5-device.c:2422
+#: spa/plugins/alsa/acp/alsa-mixer.c:2657
+#: spa/plugins/alsa/acp/alsa-mixer.c:2741
+#: spa/plugins/bluez5/bluez5-device.c:2146
msgid "Microphone"
msgstr "Mikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2727
-#: spa/plugins/alsa/acp/alsa-mixer.c:2811
+#: spa/plugins/alsa/acp/alsa-mixer.c:2658
+#: spa/plugins/alsa/acp/alsa-mixer.c:2742
msgid "Front Microphone"
msgstr "Frontmikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2728
-#: spa/plugins/alsa/acp/alsa-mixer.c:2812
+#: spa/plugins/alsa/acp/alsa-mixer.c:2659
+#: spa/plugins/alsa/acp/alsa-mixer.c:2743
msgid "Rear Microphone"
msgstr "Bakre mikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2729
+#: spa/plugins/alsa/acp/alsa-mixer.c:2660
msgid "External Microphone"
msgstr "Extern mikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2730
-#: spa/plugins/alsa/acp/alsa-mixer.c:2814
+#: spa/plugins/alsa/acp/alsa-mixer.c:2661
+#: spa/plugins/alsa/acp/alsa-mixer.c:2745
msgid "Internal Microphone"
msgstr "Intern mikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2731
-#: spa/plugins/alsa/acp/alsa-mixer.c:2817
+#: spa/plugins/alsa/acp/alsa-mixer.c:2662
+#: spa/plugins/alsa/acp/alsa-mixer.c:2748
msgid "Radio"
msgstr "Radio"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2732
-#: spa/plugins/alsa/acp/alsa-mixer.c:2818
+#: spa/plugins/alsa/acp/alsa-mixer.c:2663
+#: spa/plugins/alsa/acp/alsa-mixer.c:2749
msgid "Video"
msgstr "Video"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2733
+#: spa/plugins/alsa/acp/alsa-mixer.c:2664
msgid "Automatic Gain Control"
msgstr "Automatisk förstärkningskontroll"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2734
+#: spa/plugins/alsa/acp/alsa-mixer.c:2665
msgid "No Automatic Gain Control"
msgstr "Ingen automatisk förstärkningskontroll"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2735
+#: spa/plugins/alsa/acp/alsa-mixer.c:2666
msgid "Boost"
msgstr "Ökning"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2736
+#: spa/plugins/alsa/acp/alsa-mixer.c:2667
msgid "No Boost"
msgstr "Ingen ökning"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2737
+#: spa/plugins/alsa/acp/alsa-mixer.c:2668
msgid "Amplifier"
msgstr "Förstärkare"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2738
+#: spa/plugins/alsa/acp/alsa-mixer.c:2669
msgid "No Amplifier"
msgstr "Ingen förstärkare"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2739
+#: spa/plugins/alsa/acp/alsa-mixer.c:2670
msgid "Bass Boost"
msgstr "Basökning"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2740
+#: spa/plugins/alsa/acp/alsa-mixer.c:2671
msgid "No Bass Boost"
msgstr "Ingen basökning"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2741
-#: spa/plugins/bluez5/bluez5-device.c:2428
+#: spa/plugins/alsa/acp/alsa-mixer.c:2672
+#: spa/plugins/bluez5/bluez5-device.c:2152
msgid "Speaker"
msgstr "Högtalare"
-#. Don't call it "headset", the HF one has the mic
-#: spa/plugins/alsa/acp/alsa-mixer.c:2742
-#: spa/plugins/alsa/acp/alsa-mixer.c:2820
-#: spa/plugins/bluez5/bluez5-device.c:2434
-#: spa/plugins/bluez5/bluez5-device.c:2501
+#: spa/plugins/alsa/acp/alsa-mixer.c:2673
+#: spa/plugins/alsa/acp/alsa-mixer.c:2751
msgid "Headphones"
msgstr "Hörlurar"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2809
+#: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "Analog Input"
msgstr "Analog ingång"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2813
+#: spa/plugins/alsa/acp/alsa-mixer.c:2744
msgid "Dock Microphone"
msgstr "Dockmikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2815
+#: spa/plugins/alsa/acp/alsa-mixer.c:2746
msgid "Headset Microphone"
msgstr "Headset-mikrofon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2819
+#: spa/plugins/alsa/acp/alsa-mixer.c:2750
msgid "Analog Output"
msgstr "Analog utgång"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2821
+#: spa/plugins/alsa/acp/alsa-mixer.c:2752
msgid "Headphones 2"
msgstr "Hörlurar 2"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2822
+#: spa/plugins/alsa/acp/alsa-mixer.c:2753
msgid "Headphones Mono Output"
msgstr "Monoutgång för hörlurar"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2823
+#: spa/plugins/alsa/acp/alsa-mixer.c:2754
msgid "Line Out"
msgstr "Linje ut"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2824
+#: spa/plugins/alsa/acp/alsa-mixer.c:2755
msgid "Analog Mono Output"
msgstr "Analog monoutgång"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2825
+#: spa/plugins/alsa/acp/alsa-mixer.c:2756
msgid "Speakers"
msgstr "Högtalare"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2826
+#: spa/plugins/alsa/acp/alsa-mixer.c:2757
msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2827
+#: spa/plugins/alsa/acp/alsa-mixer.c:2758
msgid "Digital Output (S/PDIF)"
msgstr "Digital utgång (S/PDIF)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2828
+#: spa/plugins/alsa/acp/alsa-mixer.c:2759
msgid "Digital Input (S/PDIF)"
msgstr "Digital ingång (S/PDIF)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2829
+#: spa/plugins/alsa/acp/alsa-mixer.c:2760
msgid "Multichannel Input"
msgstr "Multikanalingång"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2830
+#: spa/plugins/alsa/acp/alsa-mixer.c:2761
msgid "Multichannel Output"
msgstr "Multikanalutgång"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2831
+#: spa/plugins/alsa/acp/alsa-mixer.c:2762
msgid "Game Output"
msgstr "Spelutgång"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2832
-#: spa/plugins/alsa/acp/alsa-mixer.c:2833
+#: spa/plugins/alsa/acp/alsa-mixer.c:2763
+#: spa/plugins/alsa/acp/alsa-mixer.c:2764
msgid "Chat Output"
msgstr "Chatt-utgång"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2834
+#: spa/plugins/alsa/acp/alsa-mixer.c:2765
msgid "Chat Input"
msgstr "Chatt-ingång"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2835
+#: spa/plugins/alsa/acp/alsa-mixer.c:2766
msgid "Virtual Surround 7.1"
msgstr "Virtual surround 7.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4522
+#: spa/plugins/alsa/acp/alsa-mixer.c:4458
msgid "Analog Mono"
msgstr "Analog mono"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4523
+#: spa/plugins/alsa/acp/alsa-mixer.c:4459
msgid "Analog Mono (Left)"
msgstr "Analog mono (vänster)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4524
+#: spa/plugins/alsa/acp/alsa-mixer.c:4460
msgid "Analog Mono (Right)"
msgstr "Analog mono (höger)"
@@ -454,142 +405,142 @@ msgstr "Analog mono (höger)"
#. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output.
-#: spa/plugins/alsa/acp/alsa-mixer.c:4525
-#: spa/plugins/alsa/acp/alsa-mixer.c:4533
-#: spa/plugins/alsa/acp/alsa-mixer.c:4534
+#: spa/plugins/alsa/acp/alsa-mixer.c:4461
+#: spa/plugins/alsa/acp/alsa-mixer.c:4469
+#: spa/plugins/alsa/acp/alsa-mixer.c:4470
msgid "Analog Stereo"
msgstr "Analog stereo"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4526
+#: spa/plugins/alsa/acp/alsa-mixer.c:4462
msgid "Mono"
msgstr "Mono"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4527
+#: spa/plugins/alsa/acp/alsa-mixer.c:4463
msgid "Stereo"
msgstr "Stereo"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4535
-#: spa/plugins/alsa/acp/alsa-mixer.c:4693
-#: spa/plugins/bluez5/bluez5-device.c:2410
+#: spa/plugins/alsa/acp/alsa-mixer.c:4471
+#: spa/plugins/alsa/acp/alsa-mixer.c:4629
+#: spa/plugins/bluez5/bluez5-device.c:2134
msgid "Headset"
msgstr "Headset"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4536
-#: spa/plugins/alsa/acp/alsa-mixer.c:4694
+#: spa/plugins/alsa/acp/alsa-mixer.c:4472
+#: spa/plugins/alsa/acp/alsa-mixer.c:4630
msgid "Speakerphone"
msgstr "Högtalartelefon"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4537
-#: spa/plugins/alsa/acp/alsa-mixer.c:4538
+#: spa/plugins/alsa/acp/alsa-mixer.c:4473
+#: spa/plugins/alsa/acp/alsa-mixer.c:4474
msgid "Multichannel"
msgstr "Multikanal"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4539
+#: spa/plugins/alsa/acp/alsa-mixer.c:4475
msgid "Analog Surround 2.1"
msgstr "Analog surround 2.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4540
+#: spa/plugins/alsa/acp/alsa-mixer.c:4476
msgid "Analog Surround 3.0"
msgstr "Analog surround 3.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4541
+#: spa/plugins/alsa/acp/alsa-mixer.c:4477
msgid "Analog Surround 3.1"
msgstr "Analog surround 3.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4542
+#: spa/plugins/alsa/acp/alsa-mixer.c:4478
msgid "Analog Surround 4.0"
msgstr "Analog surround 4.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4543
+#: spa/plugins/alsa/acp/alsa-mixer.c:4479
msgid "Analog Surround 4.1"
msgstr "Analog surround 4.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4544
+#: spa/plugins/alsa/acp/alsa-mixer.c:4480
msgid "Analog Surround 5.0"
msgstr "Analog surround 5.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4545
+#: spa/plugins/alsa/acp/alsa-mixer.c:4481
msgid "Analog Surround 5.1"
msgstr "Analog surround 5.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4546
+#: spa/plugins/alsa/acp/alsa-mixer.c:4482
msgid "Analog Surround 6.0"
msgstr "Analog surround 6.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4547
+#: spa/plugins/alsa/acp/alsa-mixer.c:4483
msgid "Analog Surround 6.1"
msgstr "Analog surround 6.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4548
+#: spa/plugins/alsa/acp/alsa-mixer.c:4484
msgid "Analog Surround 7.0"
msgstr "Analog surround 7.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4549
+#: spa/plugins/alsa/acp/alsa-mixer.c:4485
msgid "Analog Surround 7.1"
msgstr "Analog surround 7.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4550
+#: spa/plugins/alsa/acp/alsa-mixer.c:4486
msgid "Digital Stereo (IEC958)"
msgstr "Digital stereo (IEC958)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4551
+#: spa/plugins/alsa/acp/alsa-mixer.c:4487
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Digital surround 4.0 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4552
+#: spa/plugins/alsa/acp/alsa-mixer.c:4488
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Digital surround 5.1 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4553
+#: spa/plugins/alsa/acp/alsa-mixer.c:4489
msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "Digital surround 5.1 (IEC958/DTS)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4554
+#: spa/plugins/alsa/acp/alsa-mixer.c:4490
msgid "Digital Stereo (HDMI)"
msgstr "Digital stereo (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4555
+#: spa/plugins/alsa/acp/alsa-mixer.c:4491
msgid "Digital Surround 5.1 (HDMI)"
msgstr "Digital surround 5.1 (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4556
+#: spa/plugins/alsa/acp/alsa-mixer.c:4492
msgid "Chat"
msgstr "Chatt"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4557
+#: spa/plugins/alsa/acp/alsa-mixer.c:4493
msgid "Game"
msgstr "Spel"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4691
+#: spa/plugins/alsa/acp/alsa-mixer.c:4627
msgid "Analog Mono Duplex"
msgstr "Analog mono duplex"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4692
+#: spa/plugins/alsa/acp/alsa-mixer.c:4628
msgid "Analog Stereo Duplex"
msgstr "Analog stereo duplex"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4695
+#: spa/plugins/alsa/acp/alsa-mixer.c:4631
msgid "Digital Stereo Duplex (IEC958)"
msgstr "Digital stereo duplex (IEC958)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4696
+#: spa/plugins/alsa/acp/alsa-mixer.c:4632
msgid "Multichannel Duplex"
msgstr "Multikanalduplex"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4697
+#: spa/plugins/alsa/acp/alsa-mixer.c:4633
msgid "Stereo Duplex"
msgstr "Stereo duplex"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4698
+#: spa/plugins/alsa/acp/alsa-mixer.c:4634
msgid "Mono Chat + 7.1 Surround"
msgstr "Mono Chatt + 7.1 Surround"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4799
+#: spa/plugins/alsa/acp/alsa-mixer.c:4735
#, c-format
msgid "%s Output"
msgstr "%s-utgång"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4807
+#: spa/plugins/alsa/acp/alsa-mixer.c:4743
#, c-format
msgid "%s Input"
msgstr "%s-ingång"
@@ -676,115 +627,116 @@ msgstr[1] ""
"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
"problemet till ALSA-utvecklarna."
-#: spa/plugins/alsa/acp/channelmap.h:460
+#: spa/plugins/alsa/acp/channelmap.h:457
msgid "(invalid)"
msgstr "(ogiltig)"
-#: spa/plugins/alsa/acp/compat.c:194
+#: spa/plugins/alsa/acp/compat.c:193
msgid "Built-in Audio"
msgstr "Inbyggt ljud"
-#: spa/plugins/alsa/acp/compat.c:199
+#: spa/plugins/alsa/acp/compat.c:198
msgid "Modem"
msgstr "Modem"
-#: spa/plugins/bluez5/bluez5-device.c:2032
+#: spa/plugins/bluez5/bluez5-device.c:1813
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "Audio gateway (A2DP-källa & HSP/HFP AG)"
-#: spa/plugins/bluez5/bluez5-device.c:2061
+#: spa/plugins/bluez5/bluez5-device.c:1841
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "Ljudströmning för hörhjälpmedel (ASHA-utgång)"
-#: spa/plugins/bluez5/bluez5-device.c:2104
+#: spa/plugins/bluez5/bluez5-device.c:1881
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr "High fidelity-uppspelning (A2DP-utgång, kodek %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2107
+#: spa/plugins/bluez5/bluez5-device.c:1884
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr "High fidelity duplex (A2DP-källa/utgång, kodek %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2115
+#: spa/plugins/bluez5/bluez5-device.c:1892
msgid "High Fidelity Playback (A2DP Sink)"
msgstr "High fidelity-uppspelning (A2DP-utgång)"
-#: spa/plugins/bluez5/bluez5-device.c:2117
+#: spa/plugins/bluez5/bluez5-device.c:1894
msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "High fidelity duplex (A2DP-källa/utgång)"
-#: spa/plugins/bluez5/bluez5-device.c:2194
+#: spa/plugins/bluez5/bluez5-device.c:1944
#, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "High fidelity-uppspelning (BAP-utgång, kodek %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2199
+#: spa/plugins/bluez5/bluez5-device.c:1949
#, c-format
msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "High fidelity-ingång (BAP-källa, kodek %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2203
+#: spa/plugins/bluez5/bluez5-device.c:1953
#, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "High fidelity duplex (BAP-källa/utgång, kodek %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2212
+#: spa/plugins/bluez5/bluez5-device.c:1962
msgid "High Fidelity Playback (BAP Sink)"
msgstr "High fidelity-uppspelning (BAP-utgång)"
-#: spa/plugins/bluez5/bluez5-device.c:2216
+#: spa/plugins/bluez5/bluez5-device.c:1966
msgid "High Fidelity Input (BAP Source)"
msgstr "High fidelity-ingång (BAP-källa)"
-#: spa/plugins/bluez5/bluez5-device.c:2219
+#: spa/plugins/bluez5/bluez5-device.c:1969
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "High fidelity duplex (BAP-källa/utgång)"
-#: spa/plugins/bluez5/bluez5-device.c:2259
+#: spa/plugins/bluez5/bluez5-device.c:2015
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr "Headset-huvudenhet (HSP/HFP, kodek %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2411
-#: spa/plugins/bluez5/bluez5-device.c:2416
-#: spa/plugins/bluez5/bluez5-device.c:2423
-#: spa/plugins/bluez5/bluez5-device.c:2429
-#: spa/plugins/bluez5/bluez5-device.c:2435
-#: spa/plugins/bluez5/bluez5-device.c:2441
-#: spa/plugins/bluez5/bluez5-device.c:2447
-#: spa/plugins/bluez5/bluez5-device.c:2453
-#: spa/plugins/bluez5/bluez5-device.c:2459
+#: spa/plugins/bluez5/bluez5-device.c:2135
+#: spa/plugins/bluez5/bluez5-device.c:2140
+#: spa/plugins/bluez5/bluez5-device.c:2147
+#: spa/plugins/bluez5/bluez5-device.c:2153
+#: spa/plugins/bluez5/bluez5-device.c:2159
+#: spa/plugins/bluez5/bluez5-device.c:2165
+#: spa/plugins/bluez5/bluez5-device.c:2171
+#: spa/plugins/bluez5/bluez5-device.c:2177
+#: spa/plugins/bluez5/bluez5-device.c:2183
msgid "Handsfree"
msgstr "Handsfree"
-#: spa/plugins/bluez5/bluez5-device.c:2417
+#: spa/plugins/bluez5/bluez5-device.c:2141
msgid "Handsfree (HFP)"
msgstr "Handsfree (HFP)"
-#: spa/plugins/bluez5/bluez5-device.c:2440
+#: spa/plugins/bluez5/bluez5-device.c:2158
+msgid "Headphone"
+msgstr "Hörlurar"
+
+#: spa/plugins/bluez5/bluez5-device.c:2164
msgid "Portable"
msgstr "Bärbar"
-#: spa/plugins/bluez5/bluez5-device.c:2446
+#: spa/plugins/bluez5/bluez5-device.c:2170
msgid "Car"
msgstr "Bil"
-#: spa/plugins/bluez5/bluez5-device.c:2452
+#: spa/plugins/bluez5/bluez5-device.c:2176
msgid "HiFi"
msgstr "HiFi"
-#: spa/plugins/bluez5/bluez5-device.c:2458
+#: spa/plugins/bluez5/bluez5-device.c:2182
msgid "Phone"
msgstr "Telefon"
-#: spa/plugins/bluez5/bluez5-device.c:2465
+#: spa/plugins/bluez5/bluez5-device.c:2189
msgid "Bluetooth"
msgstr "Bluetooth"
-#: spa/plugins/bluez5/bluez5-device.c:2466
-msgid "Bluetooth Handsfree"
-msgstr "Bluetooth-handsfree"
-
-#~ msgid "Headphone"
-#~ msgstr "Hörlurar"
+#: spa/plugins/bluez5/bluez5-device.c:2190
+msgid "Bluetooth (HFP)"
+msgstr "Bluetooth (HFP)"
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 3236996bb..8be588b03 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -13,8 +13,8 @@ msgstr ""
"Project-Id-Version: pipewire.master-tx\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
"issues\n"
-"POT-Creation-Date: 2026-04-08 13:01+0000\n"
-"PO-Revision-Date: 2026-04-09 08:06+0800\n"
+"POT-Creation-Date: 2026-02-11 16:53+0000\n"
+"PO-Revision-Date: 2026-02-13 09:36+0800\n"
"Last-Translator: lumingzh \n"
"Language-Team: Chinese (China) \n"
"Language: zh_CN\n"
@@ -22,7 +22,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2016-03-22 13:23+0000\n"
-"X-Generator: Gtranslator 50.0\n"
+"X-Generator: Gtranslator 49.0\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: src/daemon/pipewire.c:29
@@ -65,46 +65,46 @@ msgstr "虚拟输出"
msgid "Tunnel for %s@%s"
msgstr "用于 %s@%s 的隧道"
-#: src/modules/module-zeroconf-discover.c:290
+#: src/modules/module-zeroconf-discover.c:326
msgid "Unknown device"
msgstr "未知设备"
-#: src/modules/module-zeroconf-discover.c:302
+#: src/modules/module-zeroconf-discover.c:338
#, c-format
msgid "%s on %s@%s"
msgstr "%2$s@%3$s 上的 %1$s"
-#: src/modules/module-zeroconf-discover.c:306
+#: src/modules/module-zeroconf-discover.c:342
#, c-format
msgid "%s on %s"
msgstr "%2$s 上的 %1$s"
-#: src/tools/pw-cat.c:269
+#: src/tools/pw-cat.c:264
#, c-format
msgid "Supported formats:\n"
msgstr "支持的格式:\n"
-#: src/tools/pw-cat.c:754
+#: src/tools/pw-cat.c:749
#, c-format
msgid "Supported channel layouts:\n"
msgstr "支持的声道布局:\n"
-#: src/tools/pw-cat.c:764
+#: src/tools/pw-cat.c:759
#, c-format
msgid "Supported channel layout aliases:\n"
msgstr "支持的声道布局别名:\n"
-#: src/tools/pw-cat.c:766
+#: src/tools/pw-cat.c:761
#, c-format
msgid " %s -> %s\n"
msgstr " %s -> %s\n"
-#: src/tools/pw-cat.c:771
+#: src/tools/pw-cat.c:766
#, c-format
msgid "Supported channel names:\n"
msgstr "支持的声道名称:\n"
-#: src/tools/pw-cat.c:1183
+#: src/tools/pw-cat.c:1177
#, c-format
msgid ""
"%s [options] [|-]\n"
@@ -119,7 +119,7 @@ msgstr ""
" -v, --verbose 输出详细操作\n"
"\n"
-#: src/tools/pw-cat.c:1190
+#: src/tools/pw-cat.c:1184
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
@@ -129,8 +129,6 @@ msgid ""
" --target Set node target serial or name "
"(default %s)\n"
" 0 means don't link\n"
-" -C --monitor Capture monitor ports (in recording "
-"mode)\n"
" --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n"
@@ -145,7 +143,6 @@ msgstr ""
" --media-role 设置媒体角色 (默认 %s)\n"
" --target 设置节点目标序列或名称 (默认 %s)\n"
" 设为 0 则不链接节点\n"
-" -C --monitor 捕获监视器端口 (录制模式)\n"
" --latency 设置节点延迟 (默认 %s)\n"
" 时间 (单位可为 s, ms, us, ns)\n"
" 或样本数 (如256)\n"
@@ -154,7 +151,7 @@ msgstr ""
" -P --properties 设置节点属性\n"
"\n"
-#: src/tools/pw-cat.c:1209
+#: src/tools/pw-cat.c:1202
#, c-format
msgid ""
" --rate Sample rate (default %u)\n"
@@ -201,7 +198,7 @@ msgstr ""
" -n, --sample-count COUNT 计数采样后停止\n"
"\n"
-#: src/tools/pw-cat.c:1234
+#: src/tools/pw-cat.c:1227
msgid ""
" -p, --playback Playback mode\n"
" -r, --record Recording mode\n"
@@ -221,7 +218,7 @@ msgstr ""
" -c, --midi-clip MIDI 剪辑模式\n"
"\n"
-#: src/tools/pw-cat.c:1839
+#: src/tools/pw-cat.c:1827
#, c-format
msgid "Supported containers and extensions:\n"
msgstr "支持的容器和扩展:\n"
@@ -248,12 +245,12 @@ msgstr ""
msgid "Pro Audio"
msgstr "专业音频"
-#: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
-#: spa/plugins/bluez5/bluez5-device.c:2163
+#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
+#: spa/plugins/bluez5/bluez5-device.c:2021
msgid "Off"
msgstr "关"
-#: spa/plugins/alsa/acp/acp.c:618
+#: spa/plugins/alsa/acp/acp.c:620
#, c-format
msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM 错误]"
@@ -281,7 +278,7 @@ msgstr "输入插孔"
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
-#: spa/plugins/bluez5/bluez5-device.c:2596
+#: spa/plugins/bluez5/bluez5-device.c:2422
msgid "Microphone"
msgstr "话筒"
@@ -347,15 +344,15 @@ msgid "No Bass Boost"
msgstr "无重低音增强"
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
-#: spa/plugins/bluez5/bluez5-device.c:2602
+#: spa/plugins/bluez5/bluez5-device.c:2428
msgid "Speaker"
msgstr "扬声器"
#. Don't call it "headset", the HF one has the mic
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
-#: spa/plugins/bluez5/bluez5-device.c:2608
-#: spa/plugins/bluez5/bluez5-device.c:2675
+#: spa/plugins/bluez5/bluez5-device.c:2434
+#: spa/plugins/bluez5/bluez5-device.c:2501
msgid "Headphones"
msgstr "模拟耳机"
@@ -465,7 +462,7 @@ msgstr "立体声"
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
-#: spa/plugins/bluez5/bluez5-device.c:2584
+#: spa/plugins/bluez5/bluez5-device.c:2410
msgid "Headset"
msgstr "耳机"
@@ -660,109 +657,101 @@ msgstr "内置音频"
msgid "Modem"
msgstr "调制解调器"
-#: spa/plugins/bluez5/bluez5-device.c:2174
+#: spa/plugins/bluez5/bluez5-device.c:2032
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "音频网关 (A2DP 信源 或 HSP/HFP 网关)"
-#: spa/plugins/bluez5/bluez5-device.c:2203
+#: spa/plugins/bluez5/bluez5-device.c:2061
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "助听器音频流 (ASHA 信宿)"
-#: spa/plugins/bluez5/bluez5-device.c:2246
+#: spa/plugins/bluez5/bluez5-device.c:2104
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr "高保真回放 (A2DP 信宿, 编码 %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2249
+#: spa/plugins/bluez5/bluez5-device.c:2107
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr "高保真双工 (A2DP 信源/信宿, 编码 %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2257
+#: spa/plugins/bluez5/bluez5-device.c:2115
msgid "High Fidelity Playback (A2DP Sink)"
msgstr "高保真回放 (A2DP 信宿)"
-#: spa/plugins/bluez5/bluez5-device.c:2259
+#: spa/plugins/bluez5/bluez5-device.c:2117
msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "高保真双工 (A2DP 信源/信宿)"
-#: spa/plugins/bluez5/bluez5-device.c:2281
-msgid "Auto: Prefer Quality (A2DP)"
-msgstr "自动:质量优先 (A2DP)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2286
-msgid "Auto: Prefer Latency (A2DP)"
-msgstr "自动:延迟优先 (A2DP)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2366
+#: spa/plugins/bluez5/bluez5-device.c:2194
#, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "高保真回放 (BAP 信宿, 编码 %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2371
+#: spa/plugins/bluez5/bluez5-device.c:2199
#, c-format
msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "高保真输入 (BAP 信源, 编码 %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2375
+#: spa/plugins/bluez5/bluez5-device.c:2203
#, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "高保真双工 (BAP 信源/信宿, 编码 %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2384
+#: spa/plugins/bluez5/bluez5-device.c:2212
msgid "High Fidelity Playback (BAP Sink)"
msgstr "高保真回放 (BAP 信宿)"
-#: spa/plugins/bluez5/bluez5-device.c:2388
+#: spa/plugins/bluez5/bluez5-device.c:2216
msgid "High Fidelity Input (BAP Source)"
msgstr "高保真输入 (BAP 信源)"
-#: spa/plugins/bluez5/bluez5-device.c:2391
+#: spa/plugins/bluez5/bluez5-device.c:2219
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "高保真双工 (BAP 信源/信宿)"
-#: spa/plugins/bluez5/bluez5-device.c:2431
+#: spa/plugins/bluez5/bluez5-device.c:2259
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr "头戴式耳机单元 (HSP/HFP, 编码 %s)"
-#: spa/plugins/bluez5/bluez5-device.c:2585
-#: spa/plugins/bluez5/bluez5-device.c:2590
-#: spa/plugins/bluez5/bluez5-device.c:2597
-#: spa/plugins/bluez5/bluez5-device.c:2603
-#: spa/plugins/bluez5/bluez5-device.c:2609
-#: spa/plugins/bluez5/bluez5-device.c:2615
-#: spa/plugins/bluez5/bluez5-device.c:2621
-#: spa/plugins/bluez5/bluez5-device.c:2627
-#: spa/plugins/bluez5/bluez5-device.c:2633
+#: spa/plugins/bluez5/bluez5-device.c:2411
+#: spa/plugins/bluez5/bluez5-device.c:2416
+#: spa/plugins/bluez5/bluez5-device.c:2423
+#: spa/plugins/bluez5/bluez5-device.c:2429
+#: spa/plugins/bluez5/bluez5-device.c:2435
+#: spa/plugins/bluez5/bluez5-device.c:2441
+#: spa/plugins/bluez5/bluez5-device.c:2447
+#: spa/plugins/bluez5/bluez5-device.c:2453
+#: spa/plugins/bluez5/bluez5-device.c:2459
msgid "Handsfree"
msgstr "免提"
-#: spa/plugins/bluez5/bluez5-device.c:2591
+#: spa/plugins/bluez5/bluez5-device.c:2417
msgid "Handsfree (HFP)"
msgstr "免提(HFP)"
-#: spa/plugins/bluez5/bluez5-device.c:2614
+#: spa/plugins/bluez5/bluez5-device.c:2440
msgid "Portable"
msgstr "便携式"
-#: spa/plugins/bluez5/bluez5-device.c:2620
+#: spa/plugins/bluez5/bluez5-device.c:2446
msgid "Car"
msgstr "车内"
-#: spa/plugins/bluez5/bluez5-device.c:2626
+#: spa/plugins/bluez5/bluez5-device.c:2452
msgid "HiFi"
msgstr "高保真"
-#: spa/plugins/bluez5/bluez5-device.c:2632
+#: spa/plugins/bluez5/bluez5-device.c:2458
msgid "Phone"
msgstr "电话"
-#: spa/plugins/bluez5/bluez5-device.c:2639
+#: spa/plugins/bluez5/bluez5-device.c:2465
msgid "Bluetooth"
msgstr "蓝牙"
-#: spa/plugins/bluez5/bluez5-device.c:2640
+#: spa/plugins/bluez5/bluez5-device.c:2466
msgid "Bluetooth Handsfree"
msgstr "蓝牙免提"
diff --git a/po/zh_TW.po b/po/zh_TW.po
index ab41369cf..1a768a992 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -4,221 +4,110 @@
#
# Cheng-Chia Tseng , 2010, 2012.
# pan93412 , 2020.
-# SPDX-FileCopyrightText: 2026 Kisaragi Hiu
msgid ""
msgstr ""
"Project-Id-Version: PipeWire Volume Control\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
"issues/new\n"
-"POT-Creation-Date: 2026-03-11 22:03+0900\n"
-"PO-Revision-Date: 2026-03-11 21:24+0900\n"
-"Last-Translator: Kisaragi Hiu \n"
-"Language-Team: Chinese (Taiwan) \n"
+"POT-Creation-Date: 2021-04-18 16:54+0800\n"
+"PO-Revision-Date: 2020-01-11 13:49+0800\n"
+"Last-Translator: pan93412 \n"
+"Language-Team: Chinese \n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Lokalize 26.03.70\n"
+"X-Generator: Lokalize 19.12.0\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-#: src/daemon/pipewire.c:29
+#: src/daemon/pipewire.c:43
#, c-format
msgid ""
"%s [options]\n"
" -h, --help Show this help\n"
-" -v, --verbose Increase verbosity by one level\n"
" --version Show version\n"
" -c, --config Load config (Default %s)\n"
-" -P --properties Set context properties\n"
msgstr ""
-"%s [選項]\n"
-" -h, --help 顯示此說明\n"
-" -v, --verbose 提高訊息詳細程度一階\n"
-" --version 顯示版本\n"
-" -c, --config 載入設定檔案(預設 %s)\n"
-" -P --properties 設定前後文屬性\n"
#: src/daemon/pipewire.desktop.in:4
msgid "PipeWire Media System"
-msgstr "PipeWire 媒體系統"
+msgstr ""
#: src/daemon/pipewire.desktop.in:5
msgid "Start the PipeWire Media System"
-msgstr "啟動 PipeWire 媒體系統"
+msgstr ""
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
-#, c-format
-msgid "Tunnel to %s%s%s"
-msgstr "穿隧到 %s%s%s"
+#: src/examples/media-session/alsa-monitor.c:526
+#: spa/plugins/alsa/acp/compat.c:187
+msgid "Built-in Audio"
+msgstr "內部音效"
-#: src/modules/module-fallback-sink.c:40
-msgid "Dummy Output"
-msgstr "虛擬輸出"
+#: src/examples/media-session/alsa-monitor.c:530
+#: spa/plugins/alsa/acp/compat.c:192
+msgid "Modem"
+msgstr "數據機"
-#: src/modules/module-pulse-tunnel.c:761
-#, c-format
-msgid "Tunnel for %s@%s"
-msgstr "%s@%s 的穿隧道"
-
-#: src/modules/module-zeroconf-discover.c:290
+#: src/examples/media-session/alsa-monitor.c:539
msgid "Unknown device"
-msgstr "未知裝置"
+msgstr ""
-#: src/modules/module-zeroconf-discover.c:302
-#, c-format
-msgid "%s on %s@%s"
-msgstr "%s 於 %s@%s"
-
-#: src/modules/module-zeroconf-discover.c:306
-#, c-format
-msgid "%s on %s"
-msgstr "%s 於 %s"
-
-#: src/tools/pw-cat.c:269
-#, c-format
-msgid "Supported formats:\n"
-msgstr "支援的格式:\n"
-
-#: src/tools/pw-cat.c:754
-#, c-format
-msgid "Supported channel layouts:\n"
-msgstr "支援的頻道配置:\n"
-
-#: src/tools/pw-cat.c:764
-#, c-format
-msgid "Supported channel layout aliases:\n"
-msgstr "支援的頻道配置別名:\n"
-
-#: src/tools/pw-cat.c:766
-#, c-format
-msgid " %s -> %s\n"
-msgstr " %s -> %s\n"
-
-#: src/tools/pw-cat.c:771
-#, c-format
-msgid "Supported channel names:\n"
-msgstr "支援的頻道名稱:\n"
-
-#: src/tools/pw-cat.c:1182
+#: src/tools/pw-cat.c:991
#, c-format
msgid ""
-"%s [options] [|-]\n"
+"%s [options] \n"
" -h, --help Show this help\n"
" --version Show version\n"
" -v, --verbose Enable verbose operations\n"
"\n"
msgstr ""
-"%s [選項] [<檔案>|-]\n"
-" -h, --help 顯示此說明\n"
-" --version 顯示版本\n"
-" -v, --verbose 操作進行時顯示詳細訊息\n"
-"\n"
-#: src/tools/pw-cat.c:1189
+#: src/tools/pw-cat.c:998
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
" --media-type Set media type (default %s)\n"
" --media-category Set media category (default %s)\n"
" --media-role Set media role (default %s)\n"
-" --target Set node target serial or name "
-"(default %s)\n"
+" --target Set node target (default %s)\n"
" 0 means don't link\n"
" --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n"
" the rate is the one of the source "
"file\n"
-" -P --properties Set node properties\n"
+" --list-targets List available targets for --target\n"
"\n"
msgstr ""
-" -R, --remote 遠端守護程式名稱\n"
-" --media-type 設定媒體型態(預設 %s)\n"
-" --media-category 設定媒體分類(預設 %s)\n"
-" --media-role 設定媒體角色(預設 %s)\n"
-" --target 設定節點目標序號或名稱(預設 %s)\n"
-" 0 代表不要連結\n"
-" --latency 設定節點延遲(預設 %s)\n"
-" X單位(單位為 s, ms, us 或 ns)\n"
-" 或直接指定樣本數 (256)\n"
-" 取樣率取自來源檔案\n"
-" -P --properties 設定節點屬性\n"
-"\n"
-#: src/tools/pw-cat.c:1207
+#: src/tools/pw-cat.c:1016
#, c-format
msgid ""
-" --rate Sample rate (default %u)\n"
-" --channels Number of channels (default %u)\n"
+" --rate Sample rate (req. for rec) (default "
+"%u)\n"
+" --channels Number of channels (req. for rec) "
+"(default %u)\n"
" --channel-map Channel map\n"
-" a channel layout: \"Stereo\", "
-"\"5.1\",... or\n"
+" one of: \"stereo\", "
+"\"surround-51\",... or\n"
" comma separated list of channel "
"names: eg. \"FL,FR\"\n"
-" --list-layouts List supported channel layouts\n"
-" --list-channel-names List supported channel maps\n"
-" --format Sample format (default %s)\n"
-" --list-formats List supported sample formats\n"
-" --container Container format\n"
-" --list-containers List supported containers and "
-"extensions\n"
+" --format Sample format %s (req. for rec) "
+"(default %s)\n"
" --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default "
"%d)\n"
-" -a, --raw RAW mode\n"
-" -M, --force-midi Force midi format, one of \"midi\" "
-"or \"ump\", (default ump)\n"
-" -n, --sample-count COUNT Stop after COUNT samples\n"
"\n"
msgstr ""
-" --rate 取樣率(預設 %u)\n"
-" --channels 頻道數量(預設 %u)\n"
-" --channel-map 頻道映射\n"
-" 可以是頻道配置名稱,像是 "
-"\"Stereo\"、\"5.1\" 等等,或是\n"
-" 以逗號分隔的頻道名稱清單,像是 "
-"\"FL,FR\"\n"
-" --list-layouts 列出支援的頻道配置\n"
-" --list-channel-names 列出支援的頻道映射\n"
-" --format 取樣格式(預設 %s)\n"
-" --list-formats 列出支援的取樣格式\n"
-" --container 容器格式\n"
-" --list-containers 列出支援的容器與副檔名\n"
-" --volume 串流音量 0-1.0(預設 %.3f)\n"
-" -q --quality 重新取樣器品質 0-15(預設 %d)\n"
-" -a, --raw 原始模式\n"
-" -M, --force-midi 強制使用 midi 格式,可選擇 \"midi\" "
-"或 \"ump\"(預設 ump)\n"
-" -n, --sample-count 樣本數 在「樣本數」個樣本之後停止\n"
-"\n"
-#: src/tools/pw-cat.c:1232
+#: src/tools/pw-cat.c:1033
msgid ""
" -p, --playback Playback mode\n"
" -r, --record Recording mode\n"
" -m, --midi Midi mode\n"
-" -d, --dsd DSD mode\n"
-" -o, --encoded Encoded mode\n"
-" -s, --sysex SysEx mode\n"
-" -c, --midi-clip MIDI clip mode\n"
"\n"
msgstr ""
-" -p, --playback 播放模式\n"
-" -r, --record 錄製模式\n"
-" -m, --midi Midi 模式\n"
-" -d, --dsd DSD 模式\n"
-" -o, --encoded 已編碼模式\n"
-" -s, --sysex SysEx 模式\n"
-" -c, --midi-clip MIDI 素材模式\n"
-"\n"
-#: src/tools/pw-cat.c:1837
-#, c-format
-msgid "Supported containers and extensions:\n"
-msgstr "支援的容器與副檔名:\n"
-
-#: src/tools/pw-cli.c:2386
+#: src/tools/pw-cli.c:2932
#, c-format
msgid ""
"%s [options] [command]\n"
@@ -226,363 +115,357 @@ msgid ""
" --version Show version\n"
" -d, --daemon Start as daemon (Default false)\n"
" -r, --remote Remote daemon name\n"
-" -m, --monitor Monitor activity\n"
"\n"
msgstr ""
-"%s [選項] [指令]\n"
-" -h, --help 顯示此說明\n"
-" --version 顯示版本\n"
-" -d, --daemon 作為守護程式啟動(預設為否)\n"
-" -r, --remote 遠端守護程式名稱\n"
-" -m, --monitor 監控活動\n"
-"\n"
-#: spa/plugins/alsa/acp/acp.c:361
+#: spa/plugins/alsa/acp/acp.c:290
msgid "Pro Audio"
-msgstr "Pro Audio"
+msgstr ""
-#: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
-#: spa/plugins/bluez5/bluez5-device.c:2021
+#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704
+#: spa/plugins/bluez5/bluez5-device.c:1000
msgid "Off"
msgstr "關閉"
-#: spa/plugins/alsa/acp/acp.c:618
-#, c-format
-msgid "%s [ALSA UCM error]"
-msgstr "%s [ALSA UCM 錯誤]"
+#: spa/plugins/alsa/acp/channelmap.h:466
+msgid "(invalid)"
+msgstr "(無效)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2721
+#: spa/plugins/alsa/acp/alsa-mixer.c:2709
msgid "Input"
msgstr "輸入"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2722
+#: spa/plugins/alsa/acp/alsa-mixer.c:2710
msgid "Docking Station Input"
msgstr "Docking Station 輸入"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2723
+#: spa/plugins/alsa/acp/alsa-mixer.c:2711
msgid "Docking Station Microphone"
msgstr "Docking Station 麥克風"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2724
+#: spa/plugins/alsa/acp/alsa-mixer.c:2712
msgid "Docking Station Line In"
msgstr "Docking Station 線路輸入"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2725
-#: spa/plugins/alsa/acp/alsa-mixer.c:2816
+#: spa/plugins/alsa/acp/alsa-mixer.c:2713
+#: spa/plugins/alsa/acp/alsa-mixer.c:2804
msgid "Line In"
msgstr "線路輸入"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2726
-#: spa/plugins/alsa/acp/alsa-mixer.c:2810
-#: spa/plugins/bluez5/bluez5-device.c:2422
+#: spa/plugins/alsa/acp/alsa-mixer.c:2714
+#: spa/plugins/alsa/acp/alsa-mixer.c:2798
+#: spa/plugins/bluez5/bluez5-device.c:1145
msgid "Microphone"
msgstr "麥克風"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2727
-#: spa/plugins/alsa/acp/alsa-mixer.c:2811
+#: spa/plugins/alsa/acp/alsa-mixer.c:2715
+#: spa/plugins/alsa/acp/alsa-mixer.c:2799
msgid "Front Microphone"
msgstr "前方麥克風"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2728
-#: spa/plugins/alsa/acp/alsa-mixer.c:2812
+#: spa/plugins/alsa/acp/alsa-mixer.c:2716
+#: spa/plugins/alsa/acp/alsa-mixer.c:2800
msgid "Rear Microphone"
msgstr "後方麥克風"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2729
+#: spa/plugins/alsa/acp/alsa-mixer.c:2717
msgid "External Microphone"
msgstr "外接麥克風"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2730
-#: spa/plugins/alsa/acp/alsa-mixer.c:2814
+#: spa/plugins/alsa/acp/alsa-mixer.c:2718
+#: spa/plugins/alsa/acp/alsa-mixer.c:2802
msgid "Internal Microphone"
msgstr "內建麥克風"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2731
-#: spa/plugins/alsa/acp/alsa-mixer.c:2817
+#: spa/plugins/alsa/acp/alsa-mixer.c:2719
+#: spa/plugins/alsa/acp/alsa-mixer.c:2805
msgid "Radio"
msgstr "無線電"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2732
-#: spa/plugins/alsa/acp/alsa-mixer.c:2818
+#: spa/plugins/alsa/acp/alsa-mixer.c:2720
+#: spa/plugins/alsa/acp/alsa-mixer.c:2806
msgid "Video"
msgstr "視訊"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2733
+#: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Automatic Gain Control"
msgstr "自動增益控制"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2734
+#: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "No Automatic Gain Control"
msgstr "無自動增益控制"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2735
+#: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Boost"
msgstr "增強"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2736
+#: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "No Boost"
msgstr "無增強"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2737
+#: spa/plugins/alsa/acp/alsa-mixer.c:2725
msgid "Amplifier"
msgstr "擴大器"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2738
+#: spa/plugins/alsa/acp/alsa-mixer.c:2726
msgid "No Amplifier"
msgstr "無擴大器"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2739
+#: spa/plugins/alsa/acp/alsa-mixer.c:2727
msgid "Bass Boost"
msgstr "低音增強"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2740
+#: spa/plugins/alsa/acp/alsa-mixer.c:2728
msgid "No Bass Boost"
msgstr "無低音增強"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2741
-#: spa/plugins/bluez5/bluez5-device.c:2428
+#: spa/plugins/alsa/acp/alsa-mixer.c:2729
+#: spa/plugins/bluez5/bluez5-device.c:1150
msgid "Speaker"
msgstr "喇叭"
-#. Don't call it "headset", the HF one has the mic
-#: spa/plugins/alsa/acp/alsa-mixer.c:2742
-#: spa/plugins/alsa/acp/alsa-mixer.c:2820
-#: spa/plugins/bluez5/bluez5-device.c:2434
-#: spa/plugins/bluez5/bluez5-device.c:2501
+#: spa/plugins/alsa/acp/alsa-mixer.c:2730
+#: spa/plugins/alsa/acp/alsa-mixer.c:2808
msgid "Headphones"
msgstr "頭戴式耳機"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2809
+#: spa/plugins/alsa/acp/alsa-mixer.c:2797
msgid "Analog Input"
msgstr "類比輸入"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2813
+#: spa/plugins/alsa/acp/alsa-mixer.c:2801
msgid "Dock Microphone"
msgstr "臺座麥克風"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2815
+#: spa/plugins/alsa/acp/alsa-mixer.c:2803
msgid "Headset Microphone"
msgstr "耳麥麥克風"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2819
+#: spa/plugins/alsa/acp/alsa-mixer.c:2807
msgid "Analog Output"
msgstr "類比輸出"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2821
+#: spa/plugins/alsa/acp/alsa-mixer.c:2809
+#, fuzzy
msgid "Headphones 2"
-msgstr "頭戴式耳機 2"
+msgstr "頭戴式耳機"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2822
+#: spa/plugins/alsa/acp/alsa-mixer.c:2810
msgid "Headphones Mono Output"
msgstr "頭戴式耳機單聲道輸出"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2823
+#: spa/plugins/alsa/acp/alsa-mixer.c:2811
msgid "Line Out"
msgstr "線路輸出"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2824
+#: spa/plugins/alsa/acp/alsa-mixer.c:2812
msgid "Analog Mono Output"
msgstr "類比單聲道輸出"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2825
+#: spa/plugins/alsa/acp/alsa-mixer.c:2813
msgid "Speakers"
msgstr "喇叭"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2826
+#: spa/plugins/alsa/acp/alsa-mixer.c:2814
msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2827
+#: spa/plugins/alsa/acp/alsa-mixer.c:2815
msgid "Digital Output (S/PDIF)"
msgstr "數位輸出 (S/PDIF)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2828
+#: spa/plugins/alsa/acp/alsa-mixer.c:2816
msgid "Digital Input (S/PDIF)"
msgstr "數位輸入 (S/PDIF)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2829
+#: spa/plugins/alsa/acp/alsa-mixer.c:2817
msgid "Multichannel Input"
msgstr "多聲道輸入"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2830
+#: spa/plugins/alsa/acp/alsa-mixer.c:2818
msgid "Multichannel Output"
msgstr "多聲道輸出"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2831
+#: spa/plugins/alsa/acp/alsa-mixer.c:2819
msgid "Game Output"
msgstr "遊戲輸出"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2832
-#: spa/plugins/alsa/acp/alsa-mixer.c:2833
+#: spa/plugins/alsa/acp/alsa-mixer.c:2820
+#: spa/plugins/alsa/acp/alsa-mixer.c:2821
msgid "Chat Output"
msgstr "聊天輸出"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2834
+#: spa/plugins/alsa/acp/alsa-mixer.c:2822
+#, fuzzy
msgid "Chat Input"
-msgstr "聊天輸入"
+msgstr "聊天輸出"
-#: spa/plugins/alsa/acp/alsa-mixer.c:2835
+#: spa/plugins/alsa/acp/alsa-mixer.c:2823
+#, fuzzy
msgid "Virtual Surround 7.1"
-msgstr "虛擬環繞聲 7.1"
+msgstr "虛擬環繞聲 sink"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4522
+#: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Analog Mono"
msgstr "類比單聲道"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4523
+#: spa/plugins/alsa/acp/alsa-mixer.c:4528
+#, fuzzy
msgid "Analog Mono (Left)"
-msgstr "類比單聲道(左)"
+msgstr "類比單聲道"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4524
+#: spa/plugins/alsa/acp/alsa-mixer.c:4529
+#, fuzzy
msgid "Analog Mono (Right)"
-msgstr "類比單聲道(右)"
+msgstr "類比單聲道"
#. Note: Not translated to "Analog Stereo Input", because the source
#. * name gets "Input" appended to it automatically, so adding "Input"
#. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output.
-#: spa/plugins/alsa/acp/alsa-mixer.c:4525
-#: spa/plugins/alsa/acp/alsa-mixer.c:4533
-#: spa/plugins/alsa/acp/alsa-mixer.c:4534
+#: spa/plugins/alsa/acp/alsa-mixer.c:4530
+#: spa/plugins/alsa/acp/alsa-mixer.c:4538
+#: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Stereo"
msgstr "類比立體聲"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4526
+#: spa/plugins/alsa/acp/alsa-mixer.c:4531
msgid "Mono"
msgstr "單聲道"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4527
+#: spa/plugins/alsa/acp/alsa-mixer.c:4532
msgid "Stereo"
msgstr "立體聲"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4535
-#: spa/plugins/alsa/acp/alsa-mixer.c:4693
-#: spa/plugins/bluez5/bluez5-device.c:2410
+#: spa/plugins/alsa/acp/alsa-mixer.c:4540
+#: spa/plugins/alsa/acp/alsa-mixer.c:4698
+#: spa/plugins/bluez5/bluez5-device.c:1135
msgid "Headset"
msgstr "耳麥"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4536
-#: spa/plugins/alsa/acp/alsa-mixer.c:4694
+#: spa/plugins/alsa/acp/alsa-mixer.c:4541
+#: spa/plugins/alsa/acp/alsa-mixer.c:4699
+#, fuzzy
msgid "Speakerphone"
-msgstr "會議揚聲器"
+msgstr "喇叭"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4537
-#: spa/plugins/alsa/acp/alsa-mixer.c:4538
+#: spa/plugins/alsa/acp/alsa-mixer.c:4542
+#: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Multichannel"
msgstr "多聲道"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4539
+#: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 2.1"
msgstr "類比環繞聲 2.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4540
+#: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 3.0"
msgstr "類比環繞聲 3.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4541
+#: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 3.1"
msgstr "類比環繞聲 3.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4542
+#: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 4.0"
msgstr "類比環繞聲 4.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4543
+#: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 4.1"
msgstr "類比環繞聲 4.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4544
+#: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 5.0"
msgstr "類比環繞聲 5.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4545
+#: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Analog Surround 5.1"
msgstr "類比環繞聲 5.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4546
+#: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Analog Surround 6.0"
msgstr "類比環繞聲 6.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4547
+#: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Analog Surround 6.1"
msgstr "類比環繞聲 6.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4548
+#: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Analog Surround 7.0"
msgstr "類比環繞聲 7.0"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4549
+#: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Analog Surround 7.1"
msgstr "類比環繞聲 7.1"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4550
+#: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Stereo (IEC958)"
msgstr "數位立體聲 (IEC958)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4551
+#: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "數位環繞聲 4.0 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4552
+#: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "數位環繞聲 5.1 (IEC958/AC3)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4553
+#: spa/plugins/alsa/acp/alsa-mixer.c:4558
msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "數位環繞聲 5.1 (IEC958/DTS)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4554
+#: spa/plugins/alsa/acp/alsa-mixer.c:4559
msgid "Digital Stereo (HDMI)"
msgstr "數位立體聲 (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4555
+#: spa/plugins/alsa/acp/alsa-mixer.c:4560
msgid "Digital Surround 5.1 (HDMI)"
msgstr "數位環繞聲 5.1 (HDMI)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4556
+#: spa/plugins/alsa/acp/alsa-mixer.c:4561
msgid "Chat"
-msgstr "聊天"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4557
+#: spa/plugins/alsa/acp/alsa-mixer.c:4562
msgid "Game"
-msgstr "遊戲"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4691
+#: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Analog Mono Duplex"
msgstr "類比單聲道雙工"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4692
+#: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Analog Stereo Duplex"
msgstr "類比立體聲雙工"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4695
+#: spa/plugins/alsa/acp/alsa-mixer.c:4700
msgid "Digital Stereo Duplex (IEC958)"
msgstr "數位立體聲雙工 (IEC958)"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4696
+#: spa/plugins/alsa/acp/alsa-mixer.c:4701
msgid "Multichannel Duplex"
msgstr "多聲道雙工"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4697
+#: spa/plugins/alsa/acp/alsa-mixer.c:4702
msgid "Stereo Duplex"
msgstr "立體聲雙工"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4698
+#: spa/plugins/alsa/acp/alsa-mixer.c:4703
msgid "Mono Chat + 7.1 Surround"
-msgstr "單聲道聊天 + 7.1 立體聲"
+msgstr ""
-#: spa/plugins/alsa/acp/alsa-mixer.c:4799
+#: spa/plugins/alsa/acp/alsa-mixer.c:4806
#, c-format
msgid "%s Output"
msgstr "%s 輸出"
-#: spa/plugins/alsa/acp/alsa-mixer.c:4807
+#: spa/plugins/alsa/acp/alsa-mixer.c:4813
#, c-format
msgid "%s Input"
msgstr "%s 輸入"
-#: spa/plugins/alsa/acp/alsa-util.c:1233 spa/plugins/alsa/acp/alsa-util.c:1327
+#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269
#, c-format
msgid ""
"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
@@ -598,23 +481,23 @@ msgstr[0] ""
"snd_pcm_avail() 傳回超出預期的大值:%lu bytes (%lu ms)。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
-#: spa/plugins/alsa/acp/alsa-util.c:1299
+#: spa/plugins/alsa/acp/alsa-util.c:1241
#, c-format
msgid ""
-"snd_pcm_delay() returned a value that is exceptionally large: %li byte "
-"(%s%lu ms).\n"
+"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
+"%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgid_plural ""
-"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
-"(%s%lu ms).\n"
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
+"%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] ""
"snd_pcm_delay() 傳回超出預期的大值:%li bytes (%s%lu ms)。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
-#: spa/plugins/alsa/acp/alsa-util.c:1346
+#: spa/plugins/alsa/acp/alsa-util.c:1288
#, c-format
msgid ""
"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
@@ -625,7 +508,7 @@ msgstr ""
"snd_pcm_avail_delay() 傳回超出預期的大值:延遲 %lu 少於可用的 %lu。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
-#: spa/plugins/alsa/acp/alsa-util.c:1389
+#: spa/plugins/alsa/acp/alsa-util.c:1331
#, c-format
msgid ""
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
@@ -641,115 +524,62 @@ msgstr[0] ""
"snd_pcm_mmap_begin() 傳回超出預期的大值:%lu bytes (%lu ms)。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
-#: spa/plugins/alsa/acp/channelmap.h:460
-msgid "(invalid)"
-msgstr "(無效)"
-
-#: spa/plugins/alsa/acp/compat.c:194
-msgid "Built-in Audio"
-msgstr "內部音效"
-
-#: spa/plugins/alsa/acp/compat.c:199
-msgid "Modem"
-msgstr "數據機"
-
-#: spa/plugins/bluez5/bluez5-device.c:2032
+#: spa/plugins/bluez5/bluez5-device.c:1010
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
-msgstr "音訊閘道 (A2DP Source & HSP/HFP AG)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2061
-msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
-msgstr "助聽器的音訊串流 (ASHA Sink)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2104
+#: spa/plugins/bluez5/bluez5-device.c:1033
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
-msgstr "高傳真播放裝置 (A2DP Sink,編碼器 %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2107
+#: spa/plugins/bluez5/bluez5-device.c:1035
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
-msgstr "高傳真雙工裝置 (A2DP Source/Sink,編碼器 %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2115
+#: spa/plugins/bluez5/bluez5-device.c:1041
msgid "High Fidelity Playback (A2DP Sink)"
-msgstr "高傳真播放裝置 (A2DP Sink)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2117
+#: spa/plugins/bluez5/bluez5-device.c:1043
msgid "High Fidelity Duplex (A2DP Source/Sink)"
-msgstr "高傳真雙工裝置 (A2DP Source/Sink)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2194
-#, c-format
-msgid "High Fidelity Playback (BAP Sink, codec %s)"
-msgstr "高傳真播放裝置 (BAP Sink,編碼器 %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2199
-#, c-format
-msgid "High Fidelity Input (BAP Source, codec %s)"
-msgstr "高傳真輸入裝置 (BAP Source,編碼器 %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2203
-#, c-format
-msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
-msgstr "高傳真雙工裝置 (BAP Source/Sink,編碼器 %s)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2212
-msgid "High Fidelity Playback (BAP Sink)"
-msgstr "高傳真播放裝置 (BAP Sink)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2216
-msgid "High Fidelity Input (BAP Source)"
-msgstr "高傳真輸入裝置 (BAP Source)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2219
-msgid "High Fidelity Duplex (BAP Source/Sink)"
-msgstr "高傳真雙工裝置 (BAP Source/Sink)"
-
-#: spa/plugins/bluez5/bluez5-device.c:2259
+#: spa/plugins/bluez5/bluez5-device.c:1070
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
-msgstr "耳麥頭戴單元 (HSP/HFP,編碼器 %s)"
+msgstr ""
-#: spa/plugins/bluez5/bluez5-device.c:2411
-#: spa/plugins/bluez5/bluez5-device.c:2416
-#: spa/plugins/bluez5/bluez5-device.c:2423
-#: spa/plugins/bluez5/bluez5-device.c:2429
-#: spa/plugins/bluez5/bluez5-device.c:2435
-#: spa/plugins/bluez5/bluez5-device.c:2441
-#: spa/plugins/bluez5/bluez5-device.c:2447
-#: spa/plugins/bluez5/bluez5-device.c:2453
-#: spa/plugins/bluez5/bluez5-device.c:2459
+#: spa/plugins/bluez5/bluez5-device.c:1074
+msgid "Headset Head Unit (HSP/HFP)"
+msgstr ""
+
+#: spa/plugins/bluez5/bluez5-device.c:1140
msgid "Handsfree"
msgstr "免持裝置"
-#: spa/plugins/bluez5/bluez5-device.c:2417
-msgid "Handsfree (HFP)"
-msgstr "免持裝置 (HFP)"
+#: spa/plugins/bluez5/bluez5-device.c:1155
+msgid "Headphone"
+msgstr "頭戴式耳機"
-#: spa/plugins/bluez5/bluez5-device.c:2440
+#: spa/plugins/bluez5/bluez5-device.c:1160
msgid "Portable"
msgstr "可攜裝置"
-#: spa/plugins/bluez5/bluez5-device.c:2446
+#: spa/plugins/bluez5/bluez5-device.c:1165
msgid "Car"
msgstr "汽車"
-#: spa/plugins/bluez5/bluez5-device.c:2452
+#: spa/plugins/bluez5/bluez5-device.c:1170
msgid "HiFi"
msgstr "HiFi"
-#: spa/plugins/bluez5/bluez5-device.c:2458
+#: spa/plugins/bluez5/bluez5-device.c:1175
msgid "Phone"
msgstr "手機"
-#: spa/plugins/bluez5/bluez5-device.c:2465
+#: spa/plugins/bluez5/bluez5-device.c:1181
+#, fuzzy
msgid "Bluetooth"
-msgstr "藍牙"
-
-#: spa/plugins/bluez5/bluez5-device.c:2466
-msgid "Bluetooth Handsfree"
-msgstr "藍牙免持裝置"
-
-#~ msgid "Headphone"
-#~ msgstr "頭戴式耳機"
+msgstr "藍牙輸入"
diff --git a/spa/include/spa/param/audio/dsd-utils.h b/spa/include/spa/param/audio/dsd-utils.h
index 9af62d99d..2dab7cc19 100644
--- a/spa/include/spa/param/audio/dsd-utils.h
+++ b/spa/include/spa/param/audio/dsd-utils.h
@@ -44,7 +44,7 @@ spa_format_audio_dsd_parse(const struct spa_pod *format, struct spa_audio_info_d
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
if (info->channels > max_position)
- return -EINVAL;
+ return -ECHRNG;
if (position == NULL ||
spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) {
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
diff --git a/spa/include/spa/param/audio/layout-types.h b/spa/include/spa/param/audio/layout-types.h
index 45cca3a6f..33650202a 100644
--- a/spa/include/spa/param/audio/layout-types.h
+++ b/spa/include/spa/param/audio/layout-types.h
@@ -87,7 +87,7 @@ spa_audio_layout_info_parse_name(struct spa_audio_layout_info *layout, size_t si
uint32_t i, n_pos;
if (spa_atou32(name+3, &n_pos, 10)) {
if (n_pos > max_position)
- return -EINVAL;
+ return -ECHRNG;
for (i = 0; i < 0x1000 && i < n_pos; i++)
layout->position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
for (; i < n_pos; i++)
@@ -99,7 +99,7 @@ spa_audio_layout_info_parse_name(struct spa_audio_layout_info *layout, size_t si
SPA_FOR_EACH_ELEMENT_VAR(spa_type_audio_layout_info, i) {
if (spa_streq(name, i->name)) {
if (i->layout.n_channels > max_position)
- return -EINVAL;
+ return -ECHRNG;
*layout = i->layout;
return i->layout.n_channels;
}
diff --git a/spa/include/spa/param/audio/raw-json.h b/spa/include/spa/param/audio/raw-json.h
index 11540a6b0..6b1b25164 100644
--- a/spa/include/spa/param/audio/raw-json.h
+++ b/spa/include/spa/param/audio/raw-json.h
@@ -88,14 +88,14 @@ spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size,
} else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) {
if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) {
if (v > max_position)
- return -EINVAL;
+ return -ECHRNG;
info->channels = v;
}
} else if (spa_streq(key, SPA_KEY_AUDIO_LAYOUT)) {
if (force || info->channels == 0) {
if (spa_audio_parse_layout(val, info->position, max_position, &v) > 0) {
if (v > max_position)
- return -EINVAL;
+ return -ECHRNG;
info->channels = v;
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
}
@@ -105,7 +105,7 @@ spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size,
if (spa_audio_parse_position_n(val, strlen(val), info->position,
max_position, &v) > 0) {
if (v > max_position)
- return -EINVAL;
+ return -ECHRNG;
info->channels = v;
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
}
diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h
index 1e4ae2758..3f81b4a92 100644
--- a/spa/include/spa/param/audio/raw-utils.h
+++ b/spa/include/spa/param/audio/raw-utils.h
@@ -46,7 +46,7 @@ spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_in
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
if (info->channels > max_position)
- return -EINVAL;
+ return -ECHRNG;
if (position == NULL ||
spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) {
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
diff --git a/spa/include/spa/pod/body.h b/spa/include/spa/pod/body.h
index 90eadb82e..4ab5d8e5f 100644
--- a/spa/include/spa/pod/body.h
+++ b/spa/include/spa/pod/body.h
@@ -111,15 +111,8 @@ SPA_API_POD_BODY int spa_pod_choice_n_values(uint32_t choice_type, uint32_t *min
SPA_API_POD_BODY int spa_pod_body_from_data(void *data, size_t maxsize, off_t offset, size_t size,
struct spa_pod *pod, const void **body)
{
- if (offset < 0)
+ if (offset < 0 || offset > (int64_t)UINT32_MAX)
return -EINVAL;
- /* On 32-bit platforms, off_t is a signed 32-bit type (since it tracks pointer
- * width), and consequently can never exceed UINT32_MAX. Skip the upper-bound
- * check on 32-bit platforms to avoid a compiler warning. */
-#if __SIZEOF_POINTER__ > 4
- if (offset > (int64_t)UINT32_MAX)
- return -EINVAL;
-#endif
if (size < sizeof(struct spa_pod) ||
size > maxsize ||
maxsize - size < (uint32_t)offset)
diff --git a/spa/include/spa/support/cpu.h b/spa/include/spa/support/cpu.h
index 2faf42154..c69338855 100644
--- a/spa/include/spa/support/cpu.h
+++ b/spa/include/spa/support/cpu.h
@@ -62,7 +62,6 @@ struct spa_cpu { struct spa_interface iface; };
#define SPA_CPU_FLAG_BMI2 (1<<18) /**< Bit Manipulation Instruction Set 2 */
#define SPA_CPU_FLAG_AVX512 (1<<19) /**< AVX-512 */
#define SPA_CPU_FLAG_SLOW_UNALIGNED (1<<20) /**< unaligned loads/stores are slow */
-#define SPA_CPU_FLAG_SLOW_GATHER (1<<21) /**< gather functions are slow */
/* PPC specific */
#define SPA_CPU_FLAG_ALTIVEC (1<<0) /**< standard */
diff --git a/spa/include/spa/support/system.h b/spa/include/spa/support/system.h
index 56ceea648..07a31a55f 100644
--- a/spa/include/spa/support/system.h
+++ b/spa/include/spa/support/system.h
@@ -41,7 +41,7 @@ struct itimerspec;
#define SPA_TYPE_INTERFACE_System SPA_TYPE_INFO_INTERFACE_BASE "System"
#define SPA_TYPE_INTERFACE_DataSystem SPA_TYPE_INFO_INTERFACE_BASE "DataSystem"
-#define SPA_VERSION_SYSTEM 1
+#define SPA_VERSION_SYSTEM 0
struct spa_system { struct spa_interface iface; };
/* IO events */
@@ -59,18 +59,11 @@ struct spa_system { struct spa_interface iface; };
struct spa_poll_event {
uint32_t events;
- union {
- void *data;
- uint64_t data_u64;
- };
-#ifdef __x86_64__
-} __attribute__((packed));
-#else
+ void *data;
};
-#endif
struct spa_system_methods {
-#define SPA_VERSION_SYSTEM_METHODS 1
+#define SPA_VERSION_SYSTEM_METHODS 0
uint32_t version;
/* read/write/ioctl */
@@ -158,7 +151,7 @@ SPA_API_SYSTEM int spa_system_pollfd_del(struct spa_system *object, int pfd, int
SPA_API_SYSTEM int spa_system_pollfd_wait(struct spa_system *object, int pfd,
struct spa_poll_event *ev, int n_ev, int timeout)
{
- return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_wait, 1, pfd, ev, n_ev, timeout);
+ return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_wait, 0, pfd, ev, n_ev, timeout);
}
SPA_API_SYSTEM int spa_system_timerfd_create(struct spa_system *object, int clockid, int flags)
diff --git a/spa/include/spa/utils/endian.h b/spa/include/spa/utils/endian.h
index 0793ed412..2d002d453 100644
--- a/spa/include/spa/utils/endian.h
+++ b/spa/include/spa/utils/endian.h
@@ -5,7 +5,7 @@
#ifndef SPA_ENDIAN_H
#define SPA_ENDIAN_H
-#if defined(__MidnightBSD__)
+#if defined(__FreeBSD__) || defined(__MidnightBSD__)
#include
#define bswap_16 bswap16
#define bswap_32 bswap32
diff --git a/spa/include/spa/utils/json-builder.h b/spa/include/spa/utils/json-builder.h
deleted file mode 100644
index b41ea9774..000000000
--- a/spa/include/spa/utils/json-builder.h
+++ /dev/null
@@ -1,445 +0,0 @@
-/* Simple Plugin API */
-/* SPDX-FileCopyrightText: Copyright © 2026 Wim Taymans */
-/* SPDX-License-Identifier: MIT */
-
-#ifndef SPA_UTILS_JSON_BUILDER_H
-#define SPA_UTILS_JSON_BUILDER_H
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#else
-#include
-#endif
-
-#ifndef SPA_API_JSON_BUILDER
- #ifdef SPA_API_IMPL
- #define SPA_API_JSON_BUILDER SPA_API_IMPL
- #else
- #define SPA_API_JSON_BUILDER static inline
- #endif
-#endif
-
-/** \defgroup spa_json_builder JSON builder
- * JSON builder functions
- */
-
-/**
- * \addtogroup spa_json_builder
- * \{
- */
-
-struct spa_json_builder {
- FILE *f;
-#define SPA_JSON_BUILDER_FLAG_CLOSE (1<<0)
-#define SPA_JSON_BUILDER_FLAG_INDENT (1<<1)
-#define SPA_JSON_BUILDER_FLAG_SPACE (1<<2)
-#define SPA_JSON_BUILDER_FLAG_PRETTY (SPA_JSON_BUILDER_FLAG_INDENT|SPA_JSON_BUILDER_FLAG_SPACE)
-#define SPA_JSON_BUILDER_FLAG_COLOR (1<<3)
-#define SPA_JSON_BUILDER_FLAG_SIMPLE (1<<4)
-#define SPA_JSON_BUILDER_FLAG_RAW (1<<5)
- uint32_t flags;
- uint32_t indent_off;
- uint32_t level;
- uint32_t indent;
- uint32_t count;
- const char *delim;
- const char *comma;
- const char *key_sep;
-#define SPA_JSON_BUILDER_COLOR_NORMAL 0
-#define SPA_JSON_BUILDER_COLOR_KEY 1
-#define SPA_JSON_BUILDER_COLOR_LITERAL 2
-#define SPA_JSON_BUILDER_COLOR_NUMBER 3
-#define SPA_JSON_BUILDER_COLOR_STRING 4
-#define SPA_JSON_BUILDER_COLOR_CONTAINER 5
- const char *color[8];
-};
-
-SPA_API_JSON_BUILDER int spa_json_builder_file(struct spa_json_builder *b, FILE *f, uint32_t flags)
-{
- bool color = flags & SPA_JSON_BUILDER_FLAG_COLOR;
- bool simple = flags & SPA_JSON_BUILDER_FLAG_SIMPLE;
- bool space = flags & SPA_JSON_BUILDER_FLAG_SPACE;
- spa_zero(*b);
- b->f = f;
- b->flags = flags;
- b->indent = 2;
- b->delim = "";
- b->comma = simple ? space ? "" : " " : ",";
- b->key_sep = simple ? space ? " =" : "=" : ":";
- b->color[0] = (color ? SPA_ANSI_RESET : "");
- b->color[1] = (color ? SPA_ANSI_BRIGHT_BLUE : "");
- b->color[2] = (color ? SPA_ANSI_BRIGHT_MAGENTA : "");
- b->color[3] = (color ? SPA_ANSI_BRIGHT_CYAN : "");
- b->color[4] = (color ? SPA_ANSI_BRIGHT_GREEN : "");
- b->color[5] = (color ? SPA_ANSI_BRIGHT_YELLOW : "");
- return 0;
-}
-
-SPA_API_JSON_BUILDER int spa_json_builder_memstream(struct spa_json_builder *b,
- char **mem, size_t *size, uint32_t flags)
-{
- FILE *f;
- spa_zero(*b);
- if ((f = open_memstream(mem, size)) == NULL)
- return -errno;
- return spa_json_builder_file(b, f, flags | SPA_JSON_BUILDER_FLAG_CLOSE);
-}
-
-SPA_API_JSON_BUILDER int spa_json_builder_membuf(struct spa_json_builder *b,
- char *mem, size_t size, uint32_t flags)
-{
- FILE *f;
- spa_zero(*b);
- if ((f = fmemopen(mem, size, "w")) == NULL)
- return -errno;
- return spa_json_builder_file(b, f, flags | SPA_JSON_BUILDER_FLAG_CLOSE);
-}
-
-SPA_API_JSON_BUILDER void spa_json_builder_close(struct spa_json_builder *b)
-{
- if (b->flags & SPA_JSON_BUILDER_FLAG_CLOSE)
- fclose(b->f);
-}
-
-SPA_API_JSON_BUILDER int spa_json_builder_encode_string(struct spa_json_builder *b,
- bool raw, const char *before, const char *val, int size, const char *after)
-{
- FILE *f = b->f;
- int i, len;
- if (raw) {
- len = fprintf(f, "%s%.*s%s", before, size, val, after) - 1;
- } else {
- len = fprintf(f, "%s\"", before);
- for (i = 0; i < size && val[i]; i++) {
- char v = val[i];
- switch (v) {
- case '\n': len += fprintf(f, "\\n"); break;
- case '\r': len += fprintf(f, "\\r"); break;
- case '\b': len += fprintf(f, "\\b"); break;
- case '\t': len += fprintf(f, "\\t"); break;
- case '\f': len += fprintf(f, "\\f"); break;
- case '\\':
- case '"': len += fprintf(f, "\\%c", v); break;
- default:
- if (v > 0 && v < 0x20)
- len += fprintf(f, "\\u%04x", v);
- else
- len += fprintf(f, "%c", v);
- break;
- }
- }
- len += fprintf(f, "\"%s", after);
- }
- return len-1;
-}
-
-SPA_API_JSON_BUILDER
-void spa_json_builder_add_simple(struct spa_json_builder *b, const char *key, int key_len,
- char type, const char *val, int val_len)
-{
- bool indent = b->indent_off == 0 && (b->flags & SPA_JSON_BUILDER_FLAG_INDENT);
- bool space = b->flags & SPA_JSON_BUILDER_FLAG_SPACE;
- bool force_raw = b->flags & SPA_JSON_BUILDER_FLAG_RAW;
- bool raw = true, simple = b->flags & SPA_JSON_BUILDER_FLAG_SIMPLE;
- int color;
-
- if (val == NULL || val_len == 0) {
- val = "null";
- val_len = 4;
- type = 'l';
- }
- if (type == 0) {
- if (spa_json_is_container(val, val_len))
- type = simple ? 'C' : 'S';
- else if (val_len > 0 && (*val == '}' || *val == ']'))
- type = 'e';
- else if (spa_json_is_null(val, val_len) ||
- spa_json_is_bool(val, val_len))
- type = 'l';
- else if (spa_json_is_string(val, val_len))
- type = 's';
- else if (spa_json_is_json_number(val, val_len))
- type = 'd';
- else if (simple && (spa_json_is_float(val, val_len) ||
- spa_json_is_int(val, val_len)))
- type = 'd';
- else
- type = 'S';
- }
- switch (type) {
- case 'e':
- b->level -= b->indent;
- b->delim = "";
- break;
- }
-
- fprintf(b->f, "%s%s%*s", b->delim, b->count == 0 ? "" : indent ? "\n" : space ? " " : "",
- indent ? b->level : 0, "");
- if (key) {
- bool key_raw = force_raw || (simple && spa_json_make_simple_string(&key, &key_len)) ||
- spa_json_is_string(key, key_len);
- spa_json_builder_encode_string(b, key_raw,
- b->color[1], key, key_len, b->color[0]);
- fprintf(b->f, "%s%s", b->key_sep, space ? " " : "");
- }
- b->delim = b->comma;
- switch (type) {
- case 'c':
- color = SPA_JSON_BUILDER_COLOR_NORMAL;
- val_len = 1;
- b->delim = "";
- b->level += b->indent;
- if (val[1] == '-') b->indent_off++;
- break;
- case 'e':
- color = SPA_JSON_BUILDER_COLOR_NORMAL;
- val_len = 1;
- if (val[1] == '-') b->indent_off--;
- break;
- case 'l':
- color = SPA_JSON_BUILDER_COLOR_LITERAL;
- break;
- case 'd':
- color = SPA_JSON_BUILDER_COLOR_NUMBER;
- break;
- case 's':
- color = SPA_JSON_BUILDER_COLOR_STRING;
- break;
- case 'C':
- color = SPA_JSON_BUILDER_COLOR_CONTAINER;
- break;
- default:
- color = SPA_JSON_BUILDER_COLOR_STRING;
- raw = force_raw || (simple && spa_json_make_simple_string(&val, &val_len));
- break;
- }
- spa_json_builder_encode_string(b, raw, b->color[color], val, val_len, b->color[0]);
- b->count++;
-}
-
-SPA_API_JSON_BUILDER void spa_json_builder_object_push(struct spa_json_builder *b,
- const char *key, const char *val)
-{
- spa_json_builder_add_simple(b, key, INT_MAX, 'c', val, INT_MAX);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_pop(struct spa_json_builder *b,
- const char *val)
-{
- spa_json_builder_add_simple(b, NULL, 0, 'e', val, INT_MAX);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_object_null(struct spa_json_builder *b,
- const char *key)
-{
- spa_json_builder_add_simple(b, key, INT_MAX, 'l', "null", 4);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_object_bool(struct spa_json_builder *b,
- const char *key, bool val)
-{
- spa_json_builder_add_simple(b, key, INT_MAX, 'l', val ? "true" : "false", INT_MAX);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_object_int(struct spa_json_builder *b,
- const char *key, int64_t val)
-{
- char str[128];
- snprintf(str, sizeof(str), "%" PRIi64, val);
- spa_json_builder_add_simple(b, key, INT_MAX, 'd', str, INT_MAX);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_object_uint(struct spa_json_builder *b,
- const char *key, uint64_t val)
-{
- char str[128];
- snprintf(str, sizeof(str), "%" PRIu64, val);
- spa_json_builder_add_simple(b, key, INT_MAX, 'd', str, INT_MAX);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_object_double(struct spa_json_builder *b,
- const char *key, double val)
-{
- char str[64];
- spa_json_format_float(str, sizeof(str), (float)val);
- spa_json_builder_add_simple(b, key, INT_MAX, 'd', str, INT_MAX);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_object_string(struct spa_json_builder *b,
- const char *key, const char *val)
-{
- spa_json_builder_add_simple(b, key, INT_MAX, 'S', val, INT_MAX);
-}
-SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(3,0)
-void spa_json_builder_object_stringv(struct spa_json_builder *b,
- const char *key, const char *fmt, va_list va)
-{
- char *val;
- if (vasprintf(&val, fmt, va) > 0) {
- spa_json_builder_object_string(b, key, val);
- free(val);
- }
-}
-
-SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(3,4)
-void spa_json_builder_object_stringf(struct spa_json_builder *b,
- const char *key, const char *fmt, ...)
-{
- va_list va;
- va_start(va, fmt);
- spa_json_builder_object_stringv(b, key, fmt, va);
- va_end(va);
-}
-
-SPA_API_JSON_BUILDER void spa_json_builder_object_value_iter(struct spa_json_builder *b,
- struct spa_json *it, const char *key, int key_len, const char *val, int len)
-{
- struct spa_json sub;
- if (spa_json_is_array(val, len)) {
- spa_json_builder_add_simple(b, key, key_len, 'c', "[", 1);
- spa_json_enter(it, &sub);
- while ((len = spa_json_next(&sub, &val)) > 0)
- spa_json_builder_object_value_iter(b, &sub, NULL, 0, val, len);
- spa_json_builder_pop(b, "]");
- }
- else if (spa_json_is_object(val, len)) {
- const char *k;
- int kl;
- spa_json_builder_add_simple(b, key, key_len, 'c', "{", 1);
- spa_json_enter(it, &sub);
- while ((kl = spa_json_next(&sub, &k)) > 0) {
- if ((len = spa_json_next(&sub, &val)) < 0)
- break;
- spa_json_builder_object_value_iter(b, &sub, k, kl, val, len);
- }
- spa_json_builder_pop(b, "}");
- }
- else {
- spa_json_builder_add_simple(b, key, key_len, 0, val, len);
- }
-}
-SPA_API_JSON_BUILDER void spa_json_builder_object_value_full(struct spa_json_builder *b,
- bool recurse, const char *key, int key_len, const char *val, int val_len)
-{
- if (!recurse || val == NULL) {
- spa_json_builder_add_simple(b, key, key_len, 0, val, val_len);
- } else {
- struct spa_json it[1];
- const char *v;
- if (spa_json_begin(&it[0], val, val_len, &v) >= 0)
- spa_json_builder_object_value_iter(b, &it[0], key, key_len, val, val_len);
- }
-}
-SPA_API_JSON_BUILDER void spa_json_builder_object_value(struct spa_json_builder *b,
- bool recurse, const char *key, const char *val)
-{
- spa_json_builder_object_value_full(b, recurse, key, key ? strlen(key) : 0,
- val, val ? strlen(val) : 0);
-}
-SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(4,5)
-void spa_json_builder_object_valuef(struct spa_json_builder *b,
- bool recurse, const char *key, const char *fmt, ...)
-{
- va_list va;
- char *val;
- va_start(va, fmt);
- if (vasprintf(&val, fmt, va) > 0) {
- spa_json_builder_object_value(b, recurse, key, val);
- free(val);
- }
- va_end(va);
-}
-
-
-/* array functions */
-SPA_API_JSON_BUILDER void spa_json_builder_array_push(struct spa_json_builder *b,
- const char *val)
-{
- spa_json_builder_object_push(b, NULL, val);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_array_null(struct spa_json_builder *b)
-{
- spa_json_builder_object_null(b, NULL);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_array_bool(struct spa_json_builder *b,
- bool val)
-{
- spa_json_builder_object_bool(b, NULL, val);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_array_int(struct spa_json_builder *b,
- int64_t val)
-{
- spa_json_builder_object_int(b, NULL, val);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_array_uint(struct spa_json_builder *b,
- uint64_t val)
-{
- spa_json_builder_object_uint(b, NULL, val);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_array_double(struct spa_json_builder *b,
- double val)
-{
- spa_json_builder_object_double(b, NULL, val);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_array_string(struct spa_json_builder *b,
- const char *val)
-{
- spa_json_builder_object_string(b, NULL, val);
-}
-SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(2,3)
-void spa_json_builder_array_stringf(struct spa_json_builder *b,
- const char *fmt, ...)
-{
- va_list va;
- va_start(va, fmt);
- spa_json_builder_object_stringv(b, NULL, fmt, va);
- va_end(va);
-}
-SPA_API_JSON_BUILDER void spa_json_builder_array_value(struct spa_json_builder *b,
- bool recurse, const char *val)
-{
- spa_json_builder_object_value(b, recurse, NULL, val);
-}
-SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(3,4)
-void spa_json_builder_array_valuef(struct spa_json_builder *b, bool recurse, const char *fmt, ...)
-{
- va_list va;
- char *val;
- va_start(va, fmt);
- if (vasprintf(&val, fmt, va) > 0) {
- spa_json_builder_object_value(b, recurse, NULL, val);
- free(val);
- }
- va_end(va);
-}
-
-SPA_API_JSON_BUILDER char *spa_json_builder_reformat(const char *json, uint32_t flags)
-{
- struct spa_json_builder b;
- char *mem;
- size_t size;
- int res;
- if ((res = spa_json_builder_memstream(&b, &mem, &size, flags)) < 0) {
- errno = -res;
- return NULL;
- }
- spa_json_builder_array_value(&b, true, json);
- spa_json_builder_close(&b);
- return mem;
-}
-
-/**
- * \}
- */
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* SPA_UTILS_JSON_BUILDER_H */
diff --git a/spa/include/spa/utils/json-core.h b/spa/include/spa/utils/json-core.h
index 9745000cf..5616bffe1 100644
--- a/spa/include/spa/utils/json-core.h
+++ b/spa/include/spa/utils/json-core.h
@@ -232,7 +232,7 @@ SPA_API_JSON int spa_json_next(struct spa_json * iter, const char **value)
switch (cur) {
case '\0':
case '\t': case ' ': case '\r': case '\n':
- case '"': case '#': case '{': case '[':
+ case '"': case '#':
case ':': case ',': case '=': case ']': case '}':
iter->state = __STRUCT | flag;
if (iter->depth > 0)
@@ -399,10 +399,6 @@ SPA_API_JSON int spa_json_is_container(const char *val, int len)
{
return len > 0 && (*val == '{' || *val == '[');
}
-SPA_API_JSON int spa_json_is_container_end(const char *val, int len)
-{
- return len > 0 && (*val == '}' || *val == ']');
-}
/* object */
SPA_API_JSON int spa_json_is_object(const char *val, int len)
@@ -425,11 +421,20 @@ SPA_API_JSON bool spa_json_is_null(const char *val, int len)
/* float */
SPA_API_JSON int spa_json_parse_float(const char *val, int len, float *result)
{
- char buf[96], *end;
+ char buf[96];
+ char *end;
+ int pos;
if (len <= 0 || len >= (int)sizeof(buf))
return 0;
+ for (pos = 0; pos < len; ++pos) {
+ switch (val[pos]) {
+ case '+': case '-': case '0' ... '9': case '.': case 'e': case 'E': break;
+ default: return 0;
+ }
+ }
+
memcpy(buf, val, len);
buf[len] = '\0';
@@ -457,7 +462,8 @@ SPA_API_JSON char *spa_json_format_float(char *str, int size, float val)
/* int */
SPA_API_JSON int spa_json_parse_int(const char *val, int len, int *result)
{
- char buf[64], *end;
+ char buf[64];
+ char *end;
if (len <= 0 || len >= (int)sizeof(buf))
return 0;
@@ -474,39 +480,6 @@ SPA_API_JSON bool spa_json_is_int(const char *val, int len)
return spa_json_parse_int(val, len, &dummy);
}
-SPA_API_JSON bool spa_json_is_json_number(const char *val, int len)
-{
- static const int8_t trans[9][7] = {
- /* '1-9' '0' '-' '+' '.' 'eE' other */
- /* 0 */ {-1, -1, -1, -1, 6, 7, -1 }, /* after '0' */
- /* 1 */ { 1, 1, -1, -1, 6, 7, -1 }, /* in integer */
- /* 2 */ { 2, 2, -1, -1, -1, 7, -1 }, /* in fraction */
- /* 3 */ { 3, 3, -1, -1, -1, -1, -1 }, /* in exponent */
- /* 4 */ { 1, 0, 5, -1, -1, -1, -1 }, /* start */
- /* 5 */ { 1, 0, -1, -1, -1, -1, -1 }, /* after '-' */
- /* 6 */ { 2, 2, -1, -1, -1, -1, -1 }, /* after '.' */
- /* 7 */ { 3, 3, 8, 8, -1, -1, -1 }, /* after 'e'/'E' */
- /* 8 */ { 3, 3, -1, -1, -1, -1, -1 }, /* after exp sign */
- };
- static const int8_t char_class[128] = {
- 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 0-15 */
- 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 16-31 */
- 6,6,6,6,6,6,6,6,6,6,6,3,6,2,4,6, /* 32-47: + - . */
- 1,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6, /* 48-63: 0-9 */
- 6,6,6,6,6,5,6,6,6,6,6,6,6,6,6,6, /* 64-79: E */
- 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 80-95 */
- 6,6,6,6,6,5,6,6,6,6,6,6,6,6,6,6, /* 96-111: e */
- 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 112-127 */
- };
- int i, state = 4;
-
- for (i = 0; i < len; i++) {
- if ((state = trans[state][char_class[val[i]&0x7f]]) < 0)
- return false;
- }
- return state < 4;
-}
-
/* bool */
SPA_API_JSON bool spa_json_is_true(const char *val, int len)
{
@@ -537,46 +510,6 @@ SPA_API_JSON bool spa_json_is_string(const char *val, int len)
{
return len > 1 && *val == '"';
}
-SPA_API_JSON bool spa_json_is_simple_string(const char *val, int size)
-{
- int i;
- static const char *REJECT = "\"\\'=:,{}[]()#";
- for (i = 0; i < size && val[i]; i++) {
- if (val[i] <= 0x20 || strchr(REJECT, val[i]) != NULL)
- return false;
- }
- return true;
-}
-SPA_API_JSON bool spa_json_make_simple_string(const char **val, int *len)
-{
- int i, l = *len;
- const char *v = *val;
- static const char *REJECT = "\"\\'=:,{}[]()#";
- int trimmed = 0, bad = 0;
- for (i = 0; i < l && v[i]; i++) {
- if (i == 0 && v[0] == '\"')
- trimmed++;
- else if ((i+1 == l || !v[i+1]) && v[i] == '\"')
- trimmed++;
- else if (v[i] <= 0x20 || strchr(REJECT, v[i]) != NULL)
- bad++;
- }
- if (trimmed == 0 && bad == 0 && i > 0)
- return true;
- else if (trimmed == 2) {
- if (bad == 0 && i > 2 &&
- !spa_json_is_null(&v[1], i-2) &&
- !spa_json_is_bool(&v[1], i-2) &&
- !spa_json_is_float(&v[1], i-2) &&
- !spa_json_is_container(&v[1], i-2) &&
- !spa_json_is_container_end(&v[1], i-2)) {
- (*len) = i-2;
- (*val)++;
- }
- return true;
- }
- return false;
-}
SPA_API_JSON int spa_json_parse_hex(const char *p, int num, uint32_t *res)
{
diff --git a/spa/include/spa/utils/string.h b/spa/include/spa/utils/string.h
index 35e4ea026..bab60de2b 100644
--- a/spa/include/spa/utils/string.h
+++ b/spa/include/spa/utils/string.h
@@ -380,26 +380,17 @@ SPA_API_STRING void spa_strbuf_init(struct spa_strbuf *buf, char *buffer, size_t
buf->buffer[0] = '\0';
}
-SPA_PRINTF_FUNC(2, 0)
-SPA_API_STRING int spa_strbuf_appendv(struct spa_strbuf *buf, const char *fmt, va_list args)
-{
- size_t remain = buf->maxsize - buf->pos;
- int written = vsnprintf(&buf->buffer[buf->pos], remain, fmt, args);
- if (written > 0)
- buf->pos += SPA_MIN(remain, (size_t)written);
- return written;
-}
-
SPA_PRINTF_FUNC(2, 3)
SPA_API_STRING int spa_strbuf_append(struct spa_strbuf *buf, const char *fmt, ...)
{
+ size_t remain = buf->maxsize - buf->pos;
+ ssize_t written;
va_list args;
- int written;
-
va_start(args, fmt);
- written = spa_strbuf_appendv(buf, fmt, args);
+ written = vsnprintf(&buf->buffer[buf->pos], remain, fmt, args);
va_end(args);
-
+ if (written > 0)
+ buf->pos += SPA_MIN(remain, (size_t)written);
return written;
}
diff --git a/spa/lib/lib.c b/spa/lib/lib.c
index 3ded776ad..0aa35fae3 100644
--- a/spa/lib/lib.c
+++ b/spa/lib/lib.c
@@ -2,6 +2,7 @@
#undef SPA_AUDIO_MAX_CHANNELS
#define SPA_API_IMPL SPA_EXPORT
+#include
#include
#include
#include
@@ -125,17 +126,16 @@
#include
#include
#include
-#include
#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
-#include
#include
#include
#include
@@ -158,7 +158,6 @@
#include
#include
#include
-#include
#include
#include
#include
diff --git a/spa/plugins/aec/aec-webrtc.cpp b/spa/plugins/aec/aec-webrtc.cpp
index 2e451d851..7b8cafcde 100644
--- a/spa/plugins/aec/aec-webrtc.cpp
+++ b/spa/plugins/aec/aec-webrtc.cpp
@@ -39,7 +39,7 @@ struct impl_data {
std::unique_ptr play_buffer, rec_buffer, out_buffer;
};
-SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.aec.webrtc");
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.eac.webrtc");
#undef SPA_LOG_TOPIC_DEFAULT
#define SPA_LOG_TOPIC_DEFAULT &log_topic
@@ -221,11 +221,6 @@ static int webrtc_init2(void *object, const struct spa_dict *args,
}};
#endif
- if (out_info->channels != 1 && rec_info->channels != out_info->channels) {
- spa_log_error(impl->log, "Source channels must be equal to capture channels or 1");
- return -EINVAL;
- }
-
#if defined(HAVE_WEBRTC)
auto apm = std::unique_ptr(webrtc::AudioProcessing::Create(config));
#elif defined(HAVE_WEBRTC1)
diff --git a/spa/plugins/alsa/acp-tool.c b/spa/plugins/alsa/acp-tool.c
index 824d69df6..c12401100 100644
--- a/spa/plugins/alsa/acp-tool.c
+++ b/spa/plugins/alsa/acp-tool.c
@@ -10,9 +10,7 @@
#include
#include
#include
-#ifdef __linux__
#include
-#endif
#include
#include
diff --git a/spa/plugins/alsa/acp/compat.h b/spa/plugins/alsa/acp/compat.h
index 87151d197..f7592e1a6 100644
--- a/spa/plugins/alsa/acp/compat.h
+++ b/spa/plugins/alsa/acp/compat.h
@@ -429,14 +429,14 @@ static PA_PRINTF_FUNC(1,0) inline char *pa_vsprintf_malloc(const char *fmt, va_l
#define pa_fopen_cloexec(f,m) fopen(f,m"e")
-static inline const char *pa_path_get_filename(const char *p)
+static inline char *pa_path_get_filename(const char *p)
{
- const char *fn;
+ char *fn;
if (!p)
return NULL;
if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
return fn+1;
- return p;
+ return (char*) p;
}
static inline bool pa_is_path_absolute(const char *fn)
diff --git a/spa/plugins/alsa/alsa-acp-device.c b/spa/plugins/alsa/alsa-acp-device.c
index eb38b3f9e..44342a7a3 100644
--- a/spa/plugins/alsa/alsa-acp-device.c
+++ b/spa/plugins/alsa/alsa-acp-device.c
@@ -990,22 +990,11 @@ static void card_port_available(void *data, uint32_t index,
for (i = 0; i < p->n_devices; i++) {
struct acp_device *d = p->devices[i];
- struct acp_port *active_port = NULL;
- uint32_t j;
uint32_t best;
if (!(d->flags & ACP_DEVICE_ACTIVE))
continue;
- for (j = 0; j < d->n_ports; j++) {
- if (d->ports[j]->flags & ACP_PORT_ACTIVE) {
- active_port = d->ports[j];
- break;
- }
- }
- if (active_port != NULL && active_port->available != ACP_AVAILABLE_NO)
- continue;
-
best = acp_device_find_best_port_index(d, NULL);
acp_device_set_port(d, best, 0);
}
diff --git a/spa/plugins/alsa/alsa-seq-bridge.c b/spa/plugins/alsa/alsa-seq-bridge.c
index 977c74e0a..06c5bc28f 100644
--- a/spa/plugins/alsa/alsa-seq-bridge.c
+++ b/spa/plugins/alsa/alsa-seq-bridge.c
@@ -227,7 +227,7 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f
if (full)
port->info.change_mask = port->info_all;
if (port->info.change_mask) {
- struct spa_dict_item items[7];
+ struct spa_dict_item items[6];
uint32_t n_items = 0;
int card_id;
snd_seq_port_info_t *info;
@@ -261,7 +261,7 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f
if (spa_strstartswith(pn, client_name))
pn += strlen(client_name);
- snprintf(name, sizeof(name), "%s%s:%s (%s)", prefix,
+ snprintf(name, sizeof(name), "%s%s%s (%s)", prefix,
client_name, pn, dir);
clean_name(name);
@@ -284,9 +284,6 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f
snprintf(card, sizeof(card), "%d", card_id);
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, card);
}
- if (this->ump)
- items[n_items++] = SPA_DICT_ITEM_INIT("control.ump", "true");
-
port->info.props = &SPA_DICT_INIT(items, n_items);
spa_node_emit_port_info(&this->hooks,
@@ -388,29 +385,13 @@ static struct seq_port *alloc_port(struct seq_state *state, struct seq_stream *s
return port;
}
-static int do_port_clear(struct spa_loop *loop, bool async, uint32_t seq,
- const void *data, size_t size, void *user_data)
-{
- struct seq_port *port = user_data;
- port->io = NULL;
- if (port->mixing) {
- spa_list_remove(&port->mix_link);
- port->mixing = false;
- }
- return 0;
-}
-
static void free_port(struct seq_state *state, struct seq_stream *stream, struct seq_port *port)
{
stream->ports[port->id] = NULL;
spa_list_remove(&port->link);
- spa_loop_locked(state->data_loop,
- do_port_clear, SPA_ID_INVALID, NULL, 0, port);
-
spa_node_emit_port_info(&state->hooks,
port->direction, port->id, NULL);
-
spa_zero(*port);
spa_list_append(&state->free_list, &port->link);
}
@@ -457,7 +438,7 @@ static void update_stream_port(struct seq_state *state, struct seq_stream *strea
struct seq_port *port = find_port(state, stream, addr);
if (info == NULL) {
- spa_log_debug(state->log, "free port %d.%d %p", addr->client, addr->port, port);
+ spa_log_debug(state->log, "free port %d.%d", addr->client, addr->port);
if (port)
free_port(state, stream, port);
} else {
@@ -469,7 +450,7 @@ static void update_stream_port(struct seq_state *state, struct seq_stream *strea
init_port(state, port, addr, snd_seq_port_info_get_type(info));
} else if (port != NULL) {
if ((caps & stream->caps) != stream->caps) {
- spa_log_debug(state->log, "free port %d.%d %p", addr->client, addr->port, port);
+ spa_log_debug(state->log, "free port %d.%d", addr->client, addr->port);
free_port(state, stream, port);
}
else {
@@ -486,8 +467,8 @@ static int on_port_info(void *data, const snd_seq_addr_t *addr, const snd_seq_po
struct seq_state *state = data;
if (info == NULL) {
- update_stream_port(state, &state->streams[SPA_DIRECTION_INPUT], addr, 0, NULL);
- update_stream_port(state, &state->streams[SPA_DIRECTION_OUTPUT], addr, 0, NULL);
+ update_stream_port(state, &state->streams[SPA_DIRECTION_INPUT], addr, 0, info);
+ update_stream_port(state, &state->streams[SPA_DIRECTION_OUTPUT], addr, 0, info);
} else {
unsigned int caps = snd_seq_port_info_get_capability(info);
@@ -520,7 +501,6 @@ impl_node_port_enum_params(void *object, int seq,
struct seq_state *this = object;
struct seq_port *port;
struct spa_pod *param;
- struct spa_pod_frame f[1];
struct spa_pod_builder b = { 0 };
uint8_t buffer[1024];
struct spa_result_node_params result;
@@ -544,18 +524,10 @@ impl_node_port_enum_params(void *object, int seq,
case SPA_PARAM_EnumFormat:
if (result.index > 0)
return 0;
- spa_pod_builder_push_object(&b, &f[0],
- SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
- spa_pod_builder_add(&b,
+ param = spa_pod_builder_add_object(&b,
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
- SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control),
- 0);
- if (port->control_types != 0) {
- spa_pod_builder_add(&b,
- SPA_FORMAT_CONTROL_types, SPA_POD_Int(port->control_types),
- 0);
- }
- param = spa_pod_builder_pop(&b, &f[0]);
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
break;
case SPA_PARAM_Format:
@@ -563,18 +535,10 @@ impl_node_port_enum_params(void *object, int seq,
return -EIO;
if (result.index > 0)
return 0;
- spa_pod_builder_push_object(&b, &f[0],
- SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
- spa_pod_builder_add(&b,
+ param = spa_pod_builder_add_object(&b,
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_Format,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
- SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control),
- 0);
- if (port->control_types != 0) {
- spa_pod_builder_add(&b,
- SPA_FORMAT_CONTROL_types, SPA_POD_Int(port->control_types),
- 0);
- }
- param = spa_pod_builder_pop(&b, &f[0]);
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
break;
case SPA_PARAM_Buffers:
@@ -836,7 +800,7 @@ impl_node_port_set_io(void *object,
info.data = data;
info.size = size;
- spa_log_debug(this->log, "%p: %p: io %d.%d %d %p %zd", this, port,
+ spa_log_debug(this->log, "%p: io %d.%d %d %p %zd", this,
direction, port_id, id, data, size);
switch (id) {
@@ -857,7 +821,7 @@ static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t
spa_return_val_if_fail(this != NULL, -EINVAL);
- spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL);
+ spa_return_val_if_fail(!CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL);
port = GET_PORT(this, SPA_DIRECTION_OUTPUT, port_id);
@@ -991,7 +955,7 @@ impl_init(const struct spa_handle_factory *factory,
this->quantum_limit = 8192;
this->min_pool_size = 500;
this->max_pool_size = 2000;
- this->ump = false;
+ this->ump = true;
for (i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key;
diff --git a/spa/plugins/alsa/alsa-seq.c b/spa/plugins/alsa/alsa-seq.c
index c25b49420..8a4ba369c 100644
--- a/spa/plugins/alsa/alsa-seq.c
+++ b/spa/plugins/alsa/alsa-seq.c
@@ -620,8 +620,9 @@ static int process_read(struct seq_state *state)
{
struct seq_stream *stream = &state->streams[SPA_DIRECTION_OUTPUT];
const bool ump = state->ump;
- void *data;
+ uint32_t *data;
uint8_t midi1_data[MAX_EVENT_SIZE];
+ uint32_t ump_data[MAX_EVENT_SIZE];
long size;
int res = -1;
struct seq_port *port;
@@ -632,6 +633,9 @@ static int process_read(struct seq_state *state)
uint64_t ev_time, diff;
uint32_t offset;
void *event;
+ uint8_t *midi1_ptr;
+ size_t midi1_size = 0;
+ uint64_t ump_state = 0;
snd_seq_event_type_t SPA_UNUSED type;
if (ump) {
@@ -698,7 +702,7 @@ static int process_read(struct seq_state *state)
#ifdef HAVE_ALSA_UMP
snd_seq_ump_event_t *ev = event;
- data = &ev->ump[0];
+ data = (uint32_t*)&ev->ump[0];
size = spa_ump_message_size(snd_ump_msg_hdr_type(ev->ump[0])) * 4;
#else
spa_assert_not_reached();
@@ -711,21 +715,34 @@ static int process_read(struct seq_state *state)
spa_log_warn(state->log, "decode failed: %s", snd_strerror(size));
continue;
}
- data = midi1_data;
+
+ midi1_ptr = midi1_data;
+ midi1_size = size;
}
- spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%ld port:%d.%d",
- type, ev_time, offset, size, addr->client, addr->port);
+ do {
+ if (!ump) {
+ data = ump_data;
+ size = spa_ump_from_midi(&midi1_ptr, &midi1_size,
+ ump_data, sizeof(ump_data), 0, &ump_state);
+ if (size <= 0)
+ break;
+ }
- spa_pod_builder_control(&port->builder, offset, ump ? SPA_CONTROL_UMP : SPA_CONTROL_Midi );
- spa_pod_builder_bytes(&port->builder, data, size);
+ spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%ld port:%d.%d",
+ type, ev_time, offset, size, addr->client, addr->port);
- /* make sure we can fit at least one control event of max size otherwise
- * we keep the event in the queue and try to copy it in the next cycle */
- if (port->builder.state.offset +
- sizeof(struct spa_pod_control) +
- MAX_EVENT_SIZE > port->buffer->buf->datas[0].maxsize)
- goto done;
+ spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_UMP);
+ spa_pod_builder_bytes(&port->builder, data, size);
+
+ /* make sure we can fit at least one control event of max size otherwise
+ * we keep the event in the queue and try to copy it in the next cycle */
+ if (port->builder.state.offset +
+ sizeof(struct spa_pod_control) +
+ MAX_EVENT_SIZE > port->buffer->buf->datas[0].maxsize)
+ goto done;
+
+ } while (!ump);
}
done:
@@ -802,6 +819,7 @@ static int process_write(struct seq_state *state)
const void *c_body;
uint64_t out_time;
snd_seq_real_time_t out_rt;
+ bool first = true;
if (io->status != SPA_STATUS_HAVE_DATA ||
io->buffer_id >= port->n_buffers)
@@ -826,6 +844,9 @@ static int process_write(struct seq_state *state)
size_t body_size;
uint8_t *body;
+ if (c.type != SPA_CONTROL_UMP)
+ continue;
+
body = (uint8_t*)c_body;
body_size = c.value.size;
@@ -840,9 +861,6 @@ static int process_write(struct seq_state *state)
#ifdef HAVE_ALSA_UMP
snd_seq_ump_event_t ev;
- if (c.type != SPA_CONTROL_UMP)
- continue;
-
snd_seq_ump_ev_clear(&ev);
snd_seq_ev_set_ump_data(&ev, body, SPA_MIN(sizeof(ev.ump), (size_t)body_size));
snd_seq_ev_set_source(&ev, state->event.addr.port);
@@ -860,26 +878,26 @@ static int process_write(struct seq_state *state)
#endif
} else {
snd_seq_event_t ev;
- int size = 0;
- long s;
-
- if (c.type != SPA_CONTROL_Midi)
- continue;
+ uint8_t data[MAX_EVENT_SIZE];
+ int size;
+ uint64_t st = 0;
while (body_size > 0) {
- if (size == 0)
+ if ((size = spa_ump_to_midi((const uint32_t **)&body, &body_size,
+ data, sizeof(data), &st)) <= 0)
+ break;
+
+ if (first)
snd_seq_ev_clear(&ev);
- if ((s = snd_midi_event_encode(stream->codec, body, body_size, &ev)) < 0) {
+ if ((size = snd_midi_event_encode(stream->codec, data, size, &ev)) < 0) {
spa_log_warn(state->log, "failed to encode event: %s",
snd_strerror(size));
snd_midi_event_reset_encode(stream->codec);
- size = 0;
+ first = true;
continue;
}
- body += s;
- body_size -= s;
- size += s;
+ first = false;
if (ev.type == SND_SEQ_EVENT_NONE)
/* this can happen when the event is not complete yet, like
* a sysex message and we need to encode some more data. */
@@ -895,7 +913,7 @@ static int process_write(struct seq_state *state)
spa_log_warn(state->log, "failed to output event: %s",
snd_strerror(err));
}
- size = 0;
+ first = true;
}
}
}
diff --git a/spa/plugins/alsa/alsa-seq.h b/spa/plugins/alsa/alsa-seq.h
index 3100b9587..354fa1d5d 100644
--- a/spa/plugins/alsa/alsa-seq.h
+++ b/spa/plugins/alsa/alsa-seq.h
@@ -82,8 +82,6 @@ struct seq_port {
struct spa_pod_builder builder;
struct spa_pod_frame frame;
- uint32_t control_types;
-
struct spa_audio_info current_format;
unsigned int have_format:1;
unsigned int active:1;
diff --git a/spa/plugins/alsa/alsa-udev.c b/spa/plugins/alsa/alsa-udev.c
index 9c3e12f20..fdc255864 100644
--- a/spa/plugins/alsa/alsa-udev.c
+++ b/spa/plugins/alsa/alsa-udev.c
@@ -48,7 +48,6 @@ struct card {
unsigned int accessible:1;
unsigned int ignored:1;
unsigned int emitted:1;
- unsigned int wireless_disconnected:1;
/* Local SPA object IDs. (Global IDs are produced by PipeWire
* out of this using its registry.) Compress-Offload or PCM
@@ -60,10 +59,6 @@ struct card {
* is used because 0 is a valid ALSA card number. */
uint32_t pcm_device_id;
uint32_t compress_offload_device_id;
-
- /* Syspath of the USB interface that has wireless_status (e.g.
- * /sys/devices/.../1-5:1.3). Empty string when not applicable. */
- char wireless_status_syspath[256];
};
static uint32_t calc_pcm_device_id(struct card *card)
@@ -97,8 +92,6 @@ struct impl {
struct spa_source source;
struct spa_source notify;
- struct udev_monitor *usb_umonitor;
- struct spa_source usb_source;
unsigned int use_acp:1;
unsigned int expose_busy:1;
int use_ucm;
@@ -360,87 +353,6 @@ static int check_udev_environment(struct udev *udev, const char *devname)
return ret;
}
-static void check_wireless_status(struct impl *this, struct card *card)
-{
- char path[PATH_MAX];
- char buf[32];
- size_t sz;
- bool was_disconnected;
-
- if (card->wireless_status_syspath[0] == '\0')
- return;
-
- was_disconnected = card->wireless_disconnected;
-
- spa_scnprintf(path, sizeof(path), "%s/wireless_status", card->wireless_status_syspath);
-
- spa_autoptr(FILE) f = fopen(path, "re");
- if (f == NULL)
- return;
-
- sz = fread(buf, 1, sizeof(buf) - 1, f);
- buf[sz] = '\0';
-
- card->wireless_disconnected = spa_strstartswith(buf, "disconnected");
-
- if (card->wireless_disconnected != was_disconnected)
- spa_log_info(this->log, "card %u: wireless headset %s",
- card->card_nr,
- card->wireless_disconnected ? "disconnected" : "connected");
-}
-
-static void find_wireless_status(struct impl *this, struct card *card)
-{
- const char *bus, *parent_syspath, *parent_sysname;
- struct udev_device *parent;
- struct dirent *entry;
- char path[PATH_MAX];
- size_t sysname_len;
-
- bus = udev_device_get_property_value(card->udev_device, "ID_BUS");
- if (!spa_streq(bus, "usb"))
- return;
-
- /* udev_device_get_parent_* returns a borrowed reference owned by the child; do not unref it. */
- parent = udev_device_get_parent_with_subsystem_devtype(card->udev_device, "usb", "usb_device");
- if (parent == NULL)
- return;
-
- parent_syspath = udev_device_get_syspath(parent);
- parent_sysname = udev_device_get_sysname(parent);
- if (parent_syspath == NULL || parent_sysname == NULL)
- return;
-
- sysname_len = strlen(parent_sysname);
-
- spa_autoptr(DIR) dir = opendir(parent_syspath);
- if (dir == NULL)
- return;
-
- while ((entry = readdir(dir)) != NULL) {
- /* USB interface directories are named ":." */
- if (strncmp(entry->d_name, parent_sysname, sysname_len) != 0 ||
- entry->d_name[sysname_len] != ':')
- continue;
-
- spa_scnprintf(path, sizeof(path), "%s/%s/wireless_status",
- parent_syspath, entry->d_name);
- if (access(path, R_OK) < 0)
- continue;
-
- spa_scnprintf(card->wireless_status_syspath,
- sizeof(card->wireless_status_syspath),
- "%s/%s", parent_syspath, entry->d_name);
-
- check_wireless_status(this, card);
-
- spa_log_debug(this->log, "card %u: found wireless_status at %s (%s)",
- card->card_nr, card->wireless_status_syspath,
- card->wireless_disconnected ? "disconnected" : "connected");
- return;
- }
-}
-
static int check_pcm_device_availability(struct impl *this, struct card *card,
int *num_pcm_devices)
{
@@ -822,11 +734,7 @@ static void process_card(struct impl *this, enum action action, struct card *car
return;
check_access(this, card);
- check_wireless_status(this, card);
-
- bool effective_accessible = card->accessible && !card->wireless_disconnected;
-
- if (effective_accessible && !card->emitted) {
+ if (card->accessible && !card->emitted) {
int res = emit_added_object_info(this, card);
if (res < 0) {
if (card->ignored)
@@ -845,7 +753,7 @@ static void process_card(struct impl *this, enum action action, struct card *car
card->card_nr);
card->unavailable = false;
}
- } else if (!effective_accessible && card->emitted) {
+ } else if (!card->accessible && card->emitted) {
card->emitted = false;
if (card->pcm_device_id != ID_DEVICE_NOT_SUPPORTED)
@@ -882,11 +790,8 @@ static void process_udev_device(struct impl *this, enum action action, struct ud
return;
card = find_card(this, card_nr);
- if (action == ACTION_CHANGE && !card) {
+ if (action == ACTION_CHANGE && !card)
card = add_card(this, card_nr, udev_device);
- if (card)
- find_wireless_status(this, card);
- }
if (!card)
return;
@@ -1013,41 +918,6 @@ static void impl_on_fd_events(struct spa_source *source)
udev_device_unref(udev_device);
}
-static void impl_on_usb_events(struct spa_source *source)
-{
- struct impl *this = source->data;
- struct udev_device *udev_device;
- const char *action, *syspath;
- unsigned int i;
-
- udev_device = udev_monitor_receive_device(this->usb_umonitor);
- if (udev_device == NULL)
- return;
-
- if ((action = udev_device_get_action(udev_device)) == NULL)
- action = "change";
-
- if (!spa_streq(action, "change"))
- goto done;
-
- syspath = udev_device_get_syspath(udev_device);
- if (syspath == NULL)
- goto done;
-
- for (i = 0; i < this->n_cards; i++) {
- struct card *card = &this->cards[i];
- if (card->wireless_status_syspath[0] == '\0')
- continue;
- if (!spa_streq(card->wireless_status_syspath, syspath))
- continue;
- spa_log_debug(this->log, "wireless_status change for card %u", card->card_nr);
- process_card(this, ACTION_CHANGE, card);
- }
-
-done:
- udev_device_unref(udev_device);
-}
-
static int start_monitor(struct impl *this)
{
int res;
@@ -1071,20 +941,6 @@ static int start_monitor(struct impl *this)
spa_log_debug(this->log, "monitor %p", this->umonitor);
spa_loop_add_source(this->main_loop, &this->source);
- this->usb_umonitor = udev_monitor_new_from_netlink(this->udev, "udev");
- if (this->usb_umonitor != NULL) {
- udev_monitor_filter_add_match_subsystem_devtype(this->usb_umonitor,
- "usb", "usb_interface");
- udev_monitor_enable_receiving(this->usb_umonitor);
-
- this->usb_source.func = impl_on_usb_events;
- this->usb_source.data = this;
- this->usb_source.fd = udev_monitor_get_fd(this->usb_umonitor);
- this->usb_source.mask = SPA_IO_IN | SPA_IO_ERR;
-
- spa_loop_add_source(this->main_loop, &this->usb_source);
- }
-
if ((res = start_inotify(this)) < 0)
return res;
@@ -1102,12 +958,6 @@ static int stop_monitor(struct impl *this)
udev_monitor_unref(this->umonitor);
this->umonitor = NULL;
- if (this->usb_umonitor != NULL) {
- spa_loop_remove_source(this->main_loop, &this->usb_source);
- udev_monitor_unref(this->usb_umonitor);
- this->usb_umonitor = NULL;
- }
-
stop_inotify(this);
return 0;
diff --git a/spa/plugins/alsa/mixer/paths/analog-output-headphones.conf b/spa/plugins/alsa/mixer/paths/analog-output-headphones.conf
index 0e698bd9c..3c62c5e67 100644
--- a/spa/plugins/alsa/mixer/paths/analog-output-headphones.conf
+++ b/spa/plugins/alsa/mixer/paths/analog-output-headphones.conf
@@ -93,12 +93,6 @@ volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
-; Keep Line Out disabled in the headphones path so selecting headphones
-; does not also drive speaker/line outputs on shared controls.
-[Element Line Out]
-switch = off
-volume = off
-
; This path is intended to control the first headphones, not
; the second headphones. But it should not hurt if we leave the second
; headphone jack enabled nonetheless.
diff --git a/spa/plugins/alsa/mixer/paths/analog-output-lineout.conf b/spa/plugins/alsa/mixer/paths/analog-output-lineout.conf
index a6fcfcb83..1ffce2225 100644
--- a/spa/plugins/alsa/mixer/paths/analog-output-lineout.conf
+++ b/spa/plugins/alsa/mixer/paths/analog-output-lineout.conf
@@ -113,23 +113,10 @@ override-map.1 = all
override-map.2 = all-left,all-right
required-any = any
-[Element Line Out]
-switch = mute
-volume = merge
-override-map.1 = all
-override-map.2 = all-left,all-right
-
[Element Master Mono]
switch = off
volume = off
-; Prefer manual routing decisions by disabling codec auto-mute here.
-[Element Auto-Mute Mode]
-enumeration = select
-
-[Option Auto-Mute Mode:Disabled]
-name = analog-output-lineout
-
[Element Line HP Swap]
switch = off
required-any = any
diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c
index 51d87d4e3..ccc9f48df 100644
--- a/spa/plugins/audioconvert/audioadapter.c
+++ b/spa/plugins/audioconvert/audioadapter.c
@@ -1086,8 +1086,6 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
spa_log_debug(this->log, "%p: suspending", this);
break;
case SPA_NODE_COMMAND_Pause:
- if ((res = negotiate_format(this)) < 0)
- return res;
spa_log_debug(this->log, "%p: pausing", this);
break;
case SPA_NODE_COMMAND_Flush:
diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c
index d1b5cd683..e9355ca41 100644
--- a/spa/plugins/audioconvert/audioconvert.c
+++ b/spa/plugins/audioconvert/audioconvert.c
@@ -16,7 +16,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -269,7 +268,6 @@ struct impl {
struct spa_list active_graphs;
struct filter_graph graphs[MAX_GRAPH];
struct spa_process_latency_info latency;
- char *graph_descs[MAX_GRAPH];
int in_filter_props;
int filter_props_count;
@@ -724,34 +722,6 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 19:
- *param = spa_pod_builder_add_object(b,
- SPA_TYPE_OBJECT_PropInfo, id,
- SPA_PROP_INFO_name, SPA_POD_String("channelmix.center-level"),
- SPA_PROP_INFO_description, SPA_POD_String("Center up/downmix level"),
- SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
- this->mix.center_level, 0.0, 10.0),
- SPA_PROP_INFO_params, SPA_POD_Bool(true));
- break;
- case 20:
- *param = spa_pod_builder_add_object(b,
- SPA_TYPE_OBJECT_PropInfo, id,
- SPA_PROP_INFO_name, SPA_POD_String("channelmix.surround-level"),
- SPA_PROP_INFO_description, SPA_POD_String("Surround up/downmix level"),
- SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
- this->mix.surround_level, 0.0, 10.0),
- SPA_PROP_INFO_params, SPA_POD_Bool(true));
- break;
- case 21:
- *param = spa_pod_builder_add_object(b,
- SPA_TYPE_OBJECT_PropInfo, id,
- SPA_PROP_INFO_name, SPA_POD_String("channelmix.lfe-level"),
- SPA_PROP_INFO_description, SPA_POD_String("LFE up/downmix level"),
- SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
- this->mix.lfe_level, 0.0, 10.0),
- SPA_PROP_INFO_params, SPA_POD_Bool(true));
- break;
-
- case 22:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.hilbert-taps"),
@@ -760,7 +730,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
this->mix.hilbert_taps, 0, MAX_TAPS),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
- case 23:
+ case 20:
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(b,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix-method"),
@@ -779,14 +749,14 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_pop(b, &f[1]);
*param = spa_pod_builder_pop(b, &f[0]);
break;
- case 24:
+ case 21:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_rate),
SPA_PROP_INFO_description, SPA_POD_String("Rate scaler"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Double(p->rate, 0.0, 10.0));
break;
- case 25:
+ case 22:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_quality),
@@ -795,7 +765,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->resample_quality, 0, 14),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
- case 26:
+ case 23:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("resample.disable"),
@@ -803,7 +773,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->resample_disabled),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
- case 27:
+ case 24:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("dither.noise"),
@@ -811,7 +781,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dir[1].conv.noise_bits, 0, 16),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
- case 28:
+ case 25:
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(b,
SPA_PROP_INFO_name, SPA_POD_String("dither.method"),
@@ -829,7 +799,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_pop(b, &f[1]);
*param = spa_pod_builder_pop(b, &f[0]);
break;
- case 29:
+ case 26:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("debug.wav-path"),
@@ -837,7 +807,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_String(p->wav_path),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
- case 30:
+ case 27:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.lock-volumes"),
@@ -845,7 +815,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->lock_volumes),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
- case 31:
+ case 28:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.disable"),
@@ -853,7 +823,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->filter_graph_disabled),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
- case 32:
+ case 29:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.N"),
@@ -864,7 +834,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
default:
if (this->filter_graph[0] && this->filter_graph[0]->graph) {
return spa_filter_graph_enum_prop_info(this->filter_graph[0]->graph,
- index - 33, b, param);
+ index - 30, b, param);
}
return 0;
}
@@ -876,7 +846,6 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
{
struct props *p = &this->props;
struct spa_pod_frame f[2];
- struct filter_graph *g;
switch (index) {
case 0:
@@ -931,12 +900,6 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_float(b, this->mix.rear_delay);
spa_pod_builder_string(b, "channelmix.stereo-widen");
spa_pod_builder_float(b, this->mix.widen);
- spa_pod_builder_string(b, "channelmix.center-level");
- spa_pod_builder_float(b, this->mix.center_level);
- spa_pod_builder_string(b, "channelmix.surround-level");
- spa_pod_builder_float(b, this->mix.surround_level);
- spa_pod_builder_string(b, "channelmix.lfe-level");
- spa_pod_builder_float(b, this->mix.lfe_level);
spa_pod_builder_string(b, "channelmix.hilbert-taps");
spa_pod_builder_int(b, this->mix.hilbert_taps);
spa_pod_builder_string(b, "channelmix.upmix-method");
@@ -955,12 +918,8 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_bool(b, p->lock_volumes);
spa_pod_builder_string(b, "audioconvert.filter-graph.disable");
spa_pod_builder_bool(b, p->filter_graph_disabled);
- spa_list_for_each(g, &this->active_graphs, link) {
- char key[64];
- snprintf(key, sizeof(key), "audioconvert.filter-graph.%d", g->order);
- spa_pod_builder_string(b, key);
- spa_pod_builder_string(b, this->graph_descs[g->order]);
- }
+ spa_pod_builder_string(b, "audioconvert.filter-graph");
+ spa_pod_builder_string(b, "");
spa_pod_builder_pop(b, &f[1]);
*param = spa_pod_builder_pop(b, &f[0]);
break;
@@ -994,7 +953,7 @@ static int impl_node_enum_params(void *object, int seq,
struct impl *this = object;
struct spa_pod *param;
struct spa_pod_builder b = { 0 };
- uint8_t buffer[16384];
+ uint8_t buffer[4096];
struct spa_result_node_params result;
uint32_t count = 0;
int res = 0;
@@ -1450,7 +1409,6 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order)
g->removing = true;
spa_log_info(impl->log, "removing filter-graph order:%d", order);
}
- free(impl->graph_descs[order]);
}
if (graph != NULL && graph[0] != '\0') {
@@ -1476,8 +1434,6 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order)
spa_list_remove(&pending->link);
insert_graph(&impl->active_graphs, pending);
- impl->graph_descs[order] = spa_json_builder_reformat(graph, 0);
-
spa_log_info(impl->log, "loading filter-graph order:%d", order);
}
if (impl->setup)
@@ -1524,12 +1480,6 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
spa_atof(s, &this->mix.rear_delay);
else if (spa_streq(k, "channelmix.stereo-widen"))
spa_atof(s, &this->mix.widen);
- else if (spa_streq(k, "channelmix.center-level"))
- spa_atof(s, &this->mix.center_level);
- else if (spa_streq(k, "channelmix.surround-level"))
- spa_atof(s, &this->mix.surround_level);
- else if (spa_streq(k, "channelmix.lfe-level"))
- spa_atof(s, &this->mix.lfe_level);
else if (spa_streq(k, "channelmix.hilbert-taps"))
spa_atou32(s, &this->mix.hilbert_taps, 0);
else if (spa_streq(k, "channelmix.upmix-method"))
@@ -2165,7 +2115,7 @@ static int setup_in_convert(struct impl *this)
return res;
spa_log_debug(this->log, "%p: got converter features %08x:%08x passthrough:%d remap:%d %s", this,
- this->cpu_flags, in->conv.func_cpu_flags, in->conv.is_passthrough,
+ this->cpu_flags, in->conv.cpu_flags, in->conv.is_passthrough,
remap, in->conv.func_name);
return 0;
@@ -2322,7 +2272,7 @@ static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *posi
set_volume(this);
spa_log_debug(this->log, "%p: got channelmix features %08x:%08x flags:%08x %s",
- this, this->cpu_flags, this->mix.func_cpu_flags,
+ this, this->cpu_flags, this->mix.cpu_flags,
this->mix.flags, this->mix.func_name);
return 0;
}
@@ -2370,7 +2320,7 @@ static int setup_resample(struct impl *this)
res = resample_native_init(&this->resample);
spa_log_debug(this->log, "%p: got resample features %08x:%08x %s",
- this, this->cpu_flags, this->resample.func_cpu_flags,
+ this, this->cpu_flags, this->resample.cpu_flags,
this->resample.func_name);
return res;
}
@@ -2462,7 +2412,7 @@ static int setup_out_convert(struct impl *this)
spa_log_debug(this->log, "%p: got converter features %08x:%08x quant:%d:%d"
" passthrough:%d remap:%d %s", this,
- this->cpu_flags, out->conv.func_cpu_flags, out->conv.method,
+ this->cpu_flags, out->conv.cpu_flags, out->conv.method,
out->conv.noise_bits, out->conv.is_passthrough, remap, out->conv.func_name);
return 0;
@@ -2642,11 +2592,8 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
break;
case SPA_NODE_COMMAND_Suspend:
reset_node(this);
- this->started = false;
- break;
+ SPA_FALLTHROUGH;
case SPA_NODE_COMMAND_Pause:
- if ((res = setup_convert(this)) < 0)
- return res;
this->started = false;
break;
case SPA_NODE_COMMAND_Flush:
@@ -4287,7 +4234,6 @@ static void free_dir(struct dir *dir)
static int impl_clear(struct spa_handle *handle)
{
struct impl *this;
- int i;
spa_return_val_if_fail(handle != NULL, -EINVAL);
@@ -4299,10 +4245,6 @@ static int impl_clear(struct spa_handle *handle)
free_tmp(this);
clean_filter_handles(this, true);
- for (i = 0; i < MAX_GRAPH; i++) {
- if (this->graph_descs[i])
- free(this->graph_descs[i]);
- }
if (this->resample.free)
resample_free(&this->resample);
@@ -4357,13 +4299,18 @@ impl_init(const struct spa_handle_factory *factory,
struct filter_graph *g = &this->graphs[i];
g->impl = this;
spa_list_append(&this->free_graphs, &g->link);
- this->graph_descs[i] = NULL;
}
this->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
this->rate_limit.burst = 1;
- channelmix_reset(&this->mix);
+ this->mix.options = CHANNELMIX_OPTION_UPMIX | CHANNELMIX_OPTION_MIX_LFE;
+ this->mix.upmix = CHANNELMIX_UPMIX_NONE;
+ this->mix.log = this->log;
+ this->mix.lfe_cutoff = 0.0f;
+ this->mix.fc_cutoff = 0.0f;
+ this->mix.rear_delay = 0.0f;
+ this->mix.widen = 0.0f;
for (i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key;
diff --git a/spa/plugins/audioconvert/benchmark-fmt-ops.c b/spa/plugins/audioconvert/benchmark-fmt-ops.c
index e59f1f56b..9ea43ec65 100644
--- a/spa/plugins/audioconvert/benchmark-fmt-ops.c
+++ b/spa/plugins/audioconvert/benchmark-fmt-ops.c
@@ -51,9 +51,9 @@ static void run_test1(const char *name, const char *impl, bool in_packed, bool o
void *op[n_channels];
struct timespec ts;
uint64_t count, t1, t2;
- struct convert conv = {
- .n_channels = n_channels,
- };
+ struct convert conv;
+
+ conv.n_channels = n_channels;
for (j = 0; j < n_channels; j++) {
ip[j] = &samp_in[j * n_samples * 4];
diff --git a/spa/plugins/audioconvert/channelmix-ops-avx.c b/spa/plugins/audioconvert/channelmix-ops-avx.c
deleted file mode 100644
index 08d8e2b00..000000000
--- a/spa/plugins/audioconvert/channelmix-ops-avx.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Spa */
-/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
-/* SPDX-License-Identifier: MIT */
-
-#include "channelmix-ops.h"
-
-#include
-#include
-#include
-
-static inline void clear_avx(float *d, uint32_t n_samples)
-{
- memset(d, 0, n_samples * sizeof(float));
-}
-
-static inline void copy_avx(float *d, const float *s, uint32_t n_samples)
-{
- spa_memcpy(d, s, n_samples * sizeof(float));
-}
-
-static inline void vol_avx(float *d, const float *s, float vol, uint32_t n_samples)
-{
- uint32_t n, unrolled;
- if (vol == 0.0f) {
- clear_avx(d, n_samples);
- } else if (vol == 1.0f) {
- copy_avx(d, s, n_samples);
- } else {
- __m256 t[4];
- const __m256 v = _mm256_set1_ps(vol);
-
- if (SPA_IS_ALIGNED(d, 32) &&
- SPA_IS_ALIGNED(s, 32))
- unrolled = n_samples & ~31;
- else
- unrolled = 0;
-
- for(n = 0; n < unrolled; n += 32) {
- t[0] = _mm256_load_ps(&s[n]);
- t[1] = _mm256_load_ps(&s[n+8]);
- t[2] = _mm256_load_ps(&s[n+16]);
- t[3] = _mm256_load_ps(&s[n+24]);
- _mm256_store_ps(&d[n], _mm256_mul_ps(t[0], v));
- _mm256_store_ps(&d[n+8], _mm256_mul_ps(t[1], v));
- _mm256_store_ps(&d[n+16], _mm256_mul_ps(t[2], v));
- _mm256_store_ps(&d[n+24], _mm256_mul_ps(t[3], v));
- }
- for(; n < n_samples; n++) {
- __m128 v = _mm_set1_ps(vol);
- _mm_store_ss(&d[n], _mm_mul_ss(_mm_load_ss(&s[n]), v));
- }
- }
-}
-
-void channelmix_copy_avx(struct channelmix *mix, void * SPA_RESTRICT dst[],
- const void * SPA_RESTRICT src[], uint32_t n_samples)
-{
- uint32_t i, n_dst = mix->dst_chan;
- float **d = (float **)dst;
- const float **s = (const float **)src;
- for (i = 0; i < n_dst; i++)
- vol_avx(d[i], s[i], mix->matrix[i][i], n_samples);
-}
diff --git a/spa/plugins/audioconvert/channelmix-ops.c b/spa/plugins/audioconvert/channelmix-ops.c
index 574d0dd1b..12edb4b5a 100644
--- a/spa/plugins/audioconvert/channelmix-ops.c
+++ b/spa/plugins/audioconvert/channelmix-ops.c
@@ -36,11 +36,6 @@ static const struct channelmix_info {
uint32_t cpu_flags;
} channelmix_table[] =
{
-#if defined (HAVE_AVX)
- MAKE(2, MASK_MONO, 2, MASK_MONO, channelmix_copy_avx, SPA_CPU_FLAG_AVX),
- MAKE(2, MASK_STEREO, 2, MASK_STEREO, channelmix_copy_avx, SPA_CPU_FLAG_AVX),
- MAKE(EQ, 0, EQ, 0, channelmix_copy_avx, SPA_CPU_FLAG_AVX),
-#endif
#if defined (HAVE_SSE)
MAKE(2, MASK_MONO, 2, MASK_MONO, channelmix_copy_sse, SPA_CPU_FLAG_SSE),
MAKE(2, MASK_STEREO, 2, MASK_STEREO, channelmix_copy_sse, SPA_CPU_FLAG_SSE),
@@ -198,9 +193,9 @@ static int make_matrix(struct channelmix *mix)
uint32_t dst_chan = mix->dst_chan;
uint64_t unassigned, keep;
uint32_t i, j, ic, jc, matrix_encoding = MATRIX_NORMAL;
- float clev = mix->center_level;
- float slev = mix->surround_level;
- float llev = mix->lfe_level;
+ float clev = SQRT1_2;
+ float slev = SQRT1_2;
+ float llev = 0.5f;
float maxsum = 0.0f;
bool filter_fc = false, filter_lfe = false, matched = false, normalize;
#define _MATRIX(s,d) matrix[_CH(s)][_CH(d)]
@@ -725,7 +720,7 @@ done:
if (src_paired == 0)
src_paired = ~0LU;
- for (jc = 0, ic = 0, i = 0; ic < dst_chan && i < MAX_CHANNELS; i++) {
+ for (jc = 0, ic = 0, i = 0; ic < dst_chan; i++) {
float sum = 0.0f;
char str1[1024], str2[1024];
struct spa_strbuf sb1, sb2;
@@ -735,7 +730,7 @@ done:
if (i < CHANNEL_BITS && (dst_paired & (1UL << i)) == 0)
continue;
- for (jc = 0, j = 0; jc < src_chan && j < MAX_CHANNELS; j++) {
+ for (jc = 0, j = 0; jc < src_chan; j++) {
if (j < CHANNEL_BITS && (src_paired & (1UL << j)) == 0)
continue;
@@ -874,21 +869,6 @@ static void impl_channelmix_free(struct channelmix *mix)
mix->process = NULL;
}
-void channelmix_reset(struct channelmix *mix)
-{
- spa_zero(*mix);
- mix->options = CHANNELMIX_DEFAULT_OPTIONS;
- mix->upmix = CHANNELMIX_DEFAULT_UPMIX;
- mix->lfe_cutoff = CHANNELMIX_DEFAULT_LFE_CUTOFF;
- mix->fc_cutoff = CHANNELMIX_DEFAULT_FC_CUTOFF;
- mix->rear_delay = CHANNELMIX_DEFAULT_REAR_DELAY;
- mix->center_level = CHANNELMIX_DEFAULT_CENTER_LEVEL;
- mix->surround_level = CHANNELMIX_DEFAULT_SURROUND_LEVEL;
- mix->lfe_level = CHANNELMIX_DEFAULT_LFE_LEVEL;
- mix->widen = CHANNELMIX_DEFAULT_WIDEN;
- mix->hilbert_taps = CHANNELMIX_DEFAULT_HILBERT_TAPS;
-}
-
int channelmix_init(struct channelmix *mix)
{
const struct channelmix_info *info;
@@ -905,8 +885,8 @@ int channelmix_init(struct channelmix *mix)
mix->free = impl_channelmix_free;
mix->process = info->process;
mix->set_volume = impl_channelmix_set_volume;
+ mix->cpu_flags = info->cpu_flags;
mix->delay = (uint32_t)(mix->rear_delay * mix->freq / 1000.0f);
- mix->func_cpu_flags = info->cpu_flags;
mix->func_name = info->name;
spa_zero(mix->taps_mem);
diff --git a/spa/plugins/audioconvert/channelmix-ops.h b/spa/plugins/audioconvert/channelmix-ops.h
index c66eaddd3..6ea2b9451 100644
--- a/spa/plugins/audioconvert/channelmix-ops.h
+++ b/spa/plugins/audioconvert/channelmix-ops.h
@@ -28,17 +28,6 @@
#define CHANNELMIX_OPS_MAX_ALIGN 16
-#define CHANNELMIX_DEFAULT_OPTIONS (CHANNELMIX_OPTION_UPMIX | CHANNELMIX_OPTION_MIX_LFE)
-#define CHANNELMIX_DEFAULT_UPMIX CHANNELMIX_UPMIX_NONE
-#define CHANNELMIX_DEFAULT_LFE_CUTOFF 0.0f
-#define CHANNELMIX_DEFAULT_FC_CUTOFF 0.0f
-#define CHANNELMIX_DEFAULT_REAR_DELAY 0.0f
-#define CHANNELMIX_DEFAULT_CENTER_LEVEL 0.707106781f
-#define CHANNELMIX_DEFAULT_SURROUND_LEVEL 0.707106781f
-#define CHANNELMIX_DEFAULT_LFE_LEVEL 0.5f
-#define CHANNELMIX_DEFAULT_WIDEN 0.0f
-#define CHANNELMIX_DEFAULT_HILBERT_TAPS 0
-
struct channelmix {
uint32_t src_chan;
uint32_t dst_chan;
@@ -55,7 +44,6 @@ struct channelmix {
uint32_t upmix;
struct spa_log *log;
- uint32_t func_cpu_flags;
const char *func_name;
#define CHANNELMIX_FLAG_ZERO (1<<0) /**< all zero components */
@@ -71,9 +59,6 @@ struct channelmix {
float fc_cutoff; /* in Hz, 0 is disabled */
float rear_delay; /* in ms, 0 is disabled */
float widen; /* stereo widen. 0 is disabled */
- float center_level; /* center down/upmix level, sqrt(1/2) */
- float lfe_level; /* lfe down/upmix level, 1/2 */
- float surround_level; /* surround down/upmix level, sqrt(1/2) */
uint32_t hilbert_taps; /* to phase shift, 0 disabled */
struct lr4 lr4[MAX_CHANNELS];
@@ -94,7 +79,6 @@ struct channelmix {
void *data;
};
-void channelmix_reset(struct channelmix *mix);
int channelmix_init(struct channelmix *mix);
static const struct channelmix_upmix_info {
@@ -155,8 +139,4 @@ DEFINE_FUNCTION(f32_5p1_4, sse);
DEFINE_FUNCTION(f32_7p1_4, sse);
#endif
-#if defined (HAVE_AVX)
-DEFINE_FUNCTION(copy, avx);
-#endif
-
#undef DEFINE_FUNCTION
diff --git a/spa/plugins/audioconvert/fmt-ops-avx2.c b/spa/plugins/audioconvert/fmt-ops-avx2.c
index 9c3dce52d..a939da458 100644
--- a/spa/plugins/audioconvert/fmt-ops-avx2.c
+++ b/spa/plugins/audioconvert/fmt-ops-avx2.c
@@ -4,8 +4,6 @@
#include "fmt-ops.h"
-#include
-
#include
// GCC: workaround for missing AVX intrinsic: "_mm256_setr_m128()"
// (see https://stackoverflow.com/questions/32630458/setting-m256i-to-the-value-of-two-m128i-values)
@@ -32,38 +30,6 @@
_mm256_srli_epi16(x, 8)); \
})
-#define _MM_TRANS_1x4_PS(v0,v1,v2,v3) \
-({ \
- v1 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(0, 3, 2, 1)); \
- v2 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(1, 0, 3, 2)); \
- v3 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(2, 1, 0, 3)); \
-})
-#define _MM_TRANS_1x4_EPI32(v0,v1,v2,v3) \
-({ \
- v1 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(0, 3, 2, 1)); \
- v2 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(1, 0, 3, 2)); \
- v3 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(2, 1, 0, 3)); \
-})
-
-#define _MM_STOREM_PS(d0,d1,d2,d3,v) \
-({ \
- __m128 o[3]; \
- _MM_TRANS_1x4_PS(v, o[0], o[1], o[2]); \
- _mm_store_ss(d0, v); \
- _mm_store_ss(d1, o[0]); \
- _mm_store_ss(d2, o[1]); \
- _mm_store_ss(d3, o[2]); \
-})
-#define _MM_STOREM_EPI32(d0,d1,d2,d3,v) \
-({ \
- __m128i o[3]; \
- _MM_TRANS_1x4_EPI32(v, o[0], o[1], o[2]); \
- *d0 = _mm_cvtsi128_si32(v); \
- *d1 = _mm_cvtsi128_si32(o[0]); \
- *d2 = _mm_cvtsi128_si32(o[1]); \
- *d3 = _mm_cvtsi128_si32(o[2]); \
-})
-
static void
conv_s16_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
@@ -287,7 +253,7 @@ conv_s16s_to_f32d_2_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const
}
static void
-conv_s24_to_f32d_1s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
+conv_s24_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int8_t *s = src;
@@ -323,7 +289,7 @@ conv_s24_to_f32d_1s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const voi
}
static void
-conv_s24_to_f32d_2s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
+conv_s24_to_f32d_2s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int8_t *s = src;
@@ -375,7 +341,7 @@ conv_s24_to_f32d_2s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const voi
}
}
static void
-conv_s24_to_f32d_4s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
+conv_s24_to_f32d_4s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int8_t *s = src;
@@ -431,13 +397,18 @@ conv_s24_to_f32d_4s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const voi
s += 12 * n_channels;
}
for(; n < n_samples; n++) {
- in[0] = _mm_setr_epi32(s24_to_s32(*((int24_t*)s+0)),
- s24_to_s32(*((int24_t*)s+1)),
- s24_to_s32(*((int24_t*)s+2)),
- s24_to_s32(*((int24_t*)s+3)));
- out[0] = _mm_cvtepi32_ps(in[0]);
- out[0] = _mm_mul_ps(out[0], factor);
- _MM_STOREM_PS(&d0[n], &d1[n], &d2[n], &d3[n], out[0]);
+ out[0] = _mm_cvtsi32_ss(factor, s24_to_s32(*((int24_t*)s+0)));
+ out[1] = _mm_cvtsi32_ss(factor, s24_to_s32(*((int24_t*)s+1)));
+ out[2] = _mm_cvtsi32_ss(factor, s24_to_s32(*((int24_t*)s+2)));
+ out[3] = _mm_cvtsi32_ss(factor, s24_to_s32(*((int24_t*)s+3)));
+ out[0] = _mm_mul_ss(out[0], factor);
+ out[1] = _mm_mul_ss(out[1], factor);
+ out[2] = _mm_mul_ss(out[2], factor);
+ out[3] = _mm_mul_ss(out[3], factor);
+ _mm_store_ss(&d0[n], out[0]);
+ _mm_store_ss(&d1[n], out[1]);
+ _mm_store_ss(&d2[n], out[2]);
+ _mm_store_ss(&d3[n], out[3]);
s += 3 * n_channels;
}
}
@@ -449,22 +420,16 @@ conv_s24_to_f32d_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const voi
const int8_t *s = src[0];
uint32_t i = 0, n_channels = conv->n_channels;
- if (conv->cpu_flags & SPA_CPU_FLAG_SLOW_GATHER) {
-#if defined (HAVE_SSE2)
- conv_s24_to_f32d_sse2(conv, dst, src, n_samples);
-#endif
- } else {
- for(; i + 3 < n_channels; i += 4)
- conv_s24_to_f32d_4s_gather_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
- for(; i + 1 < n_channels; i += 2)
- conv_s24_to_f32d_2s_gather_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
- for(; i < n_channels; i++)
- conv_s24_to_f32d_1s_gather_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
- }
+ for(; i + 3 < n_channels; i += 4)
+ conv_s24_to_f32d_4s_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
+ for(; i + 1 < n_channels; i += 2)
+ conv_s24_to_f32d_2s_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
+ for(; i < n_channels; i++)
+ conv_s24_to_f32d_1s_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
}
static void
-conv_s32_to_f32d_4s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
+conv_s32_to_f32d_4s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int32_t *s = src;
@@ -508,17 +473,24 @@ conv_s32_to_f32d_4s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const voi
}
for(; n < n_samples; n++) {
__m128 out[4], factor = _mm_set1_ps(1.0f / S32_SCALE_I2F);
- __m128i in[1];
- in[0] = _mm_setr_epi32(s[0], s[1], s[2], s[3]);
- out[0] = _mm_cvtepi32_ps(in[0]);
- out[0] = _mm_mul_ps(out[0], factor);
- _MM_STOREM_PS(&d0[n], &d1[n], &d2[n], &d3[n], out[0]);
+ out[0] = _mm_cvtsi32_ss(factor, s[0]);
+ out[1] = _mm_cvtsi32_ss(factor, s[1]);
+ out[2] = _mm_cvtsi32_ss(factor, s[2]);
+ out[3] = _mm_cvtsi32_ss(factor, s[3]);
+ out[0] = _mm_mul_ss(out[0], factor);
+ out[1] = _mm_mul_ss(out[1], factor);
+ out[2] = _mm_mul_ss(out[2], factor);
+ out[3] = _mm_mul_ss(out[3], factor);
+ _mm_store_ss(&d0[n], out[0]);
+ _mm_store_ss(&d1[n], out[1]);
+ _mm_store_ss(&d2[n], out[2]);
+ _mm_store_ss(&d3[n], out[3]);
s += n_channels;
}
}
static void
-conv_s32_to_f32d_2s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
+conv_s32_to_f32d_2s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int32_t *s = src;
@@ -563,7 +535,7 @@ conv_s32_to_f32d_2s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const voi
}
static void
-conv_s32_to_f32d_1s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
+conv_s32_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int32_t *s = src;
@@ -603,169 +575,6 @@ conv_s32_to_f32d_1s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const voi
}
}
-
-static void
-conv_s32_to_f32d_2s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
- uint32_t n_channels, uint32_t n_samples)
-{
- const int32_t *s = src;
- float *d0 = dst[0], *d1 = dst[1];
- uint32_t n, unrolled;
- __m256i in[4];
- __m256 out[4], t[4], factor = _mm256_set1_ps(1.0f / S32_SCALE_I2F);
-
- if (SPA_IS_ALIGNED(d0, 32) &&
- SPA_IS_ALIGNED(d1, 32))
- unrolled = n_samples & ~7;
- else
- unrolled = 0;
-
- for(n = 0; n < unrolled; n += 8) {
- in[0] = _mm256_setr_epi64x(
- *((uint64_t*)&s[0*n_channels]),
- *((uint64_t*)&s[1*n_channels]),
- *((uint64_t*)&s[4*n_channels]),
- *((uint64_t*)&s[5*n_channels]));
- in[1] = _mm256_setr_epi64x(
- *((uint64_t*)&s[2*n_channels]),
- *((uint64_t*)&s[3*n_channels]),
- *((uint64_t*)&s[6*n_channels]),
- *((uint64_t*)&s[7*n_channels]));
-
- out[0] = _mm256_cvtepi32_ps(in[0]);
- out[1] = _mm256_cvtepi32_ps(in[1]);
-
- out[0] = _mm256_mul_ps(out[0], factor); /* a0 b0 a1 b1 a4 b4 a5 b5 */
- out[1] = _mm256_mul_ps(out[1], factor); /* a2 b2 a3 b3 a6 b6 a7 b7 */
-
- t[0] = _mm256_unpacklo_ps(out[0], out[1]); /* a0 a2 b0 b2 a4 a6 b4 b6 */
- t[1] = _mm256_unpackhi_ps(out[0], out[1]); /* a1 a3 b1 b3 a5 a7 b5 b7 */
-
- out[0] = _mm256_unpacklo_ps(t[0], t[1]); /* a0 a1 a2 a3 a4 a5 a6 a7 */
- out[1] = _mm256_unpackhi_ps(t[0], t[1]); /* b0 b1 b2 b3 b4 b5 b6 b7 */
-
- _mm256_store_ps(&d0[n], out[0]);
- _mm256_store_ps(&d1[n], out[1]);
-
- s += 8*n_channels;
- }
- for(; n < n_samples; n++) {
- __m128 out[2], factor = _mm_set1_ps(1.0f / S32_SCALE_I2F);
- out[0] = _mm_cvtsi32_ss(factor, s[0]);
- out[1] = _mm_cvtsi32_ss(factor, s[1]);
- out[0] = _mm_mul_ss(out[0], factor);
- out[1] = _mm_mul_ss(out[1], factor);
- _mm_store_ss(&d0[n], out[0]);
- _mm_store_ss(&d1[n], out[1]);
- s += n_channels;
- }
-}
-
-static void
-conv_s32_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
- uint32_t n_channels, uint32_t n_samples)
-{
- const int32_t *s = src;
- float *d0 = dst[0];
- uint32_t n, unrolled;
- __m256i in[2];
- __m256 out[2], factor = _mm256_set1_ps(1.0f / S32_SCALE_I2F);
-
- if (SPA_IS_ALIGNED(d0, 32))
- unrolled = n_samples & ~7;
- else
- unrolled = 0;
-
- for(n = 0; n < unrolled; n += 8) {
- in[0] = _mm256_setr_epi32(
- s[0*n_channels], s[1*n_channels],
- s[2*n_channels], s[3*n_channels],
- s[4*n_channels], s[5*n_channels],
- s[6*n_channels], s[7*n_channels]);
- out[0] = _mm256_cvtepi32_ps(in[0]);
- out[0] = _mm256_mul_ps(out[0], factor);
- _mm256_store_ps(&d0[n+0], out[0]);
- s += 8*n_channels;
- }
- for(; n < n_samples; n++) {
- __m128 out, factor = _mm_set1_ps(1.0f / S32_SCALE_I2F);
- out = _mm_cvtsi32_ss(factor, s[0]);
- out = _mm_mul_ss(out, factor);
- _mm_store_ss(&d0[n], out);
- s += n_channels;
- }
-}
-
-static void
-conv_s32_to_f32d_4s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
- uint32_t n_channels, uint32_t n_samples)
-{
- const int32_t *s = src;
- float *d0 = dst[0], *d1 = dst[1], *d2 = dst[2], *d3 = dst[3];
- uint32_t n, unrolled;
- __m256i in[4];
- __m256 out[4], t[4], factor = _mm256_set1_ps(1.0f / S32_SCALE_I2F);
-
- if (SPA_IS_ALIGNED(d0, 32) &&
- SPA_IS_ALIGNED(d1, 32) &&
- SPA_IS_ALIGNED(d2, 32) &&
- SPA_IS_ALIGNED(d3, 32))
- unrolled = n_samples & ~7;
- else
- unrolled = 0;
-
- for(n = 0; n < unrolled; n += 8) {
- in[0] = _mm256_setr_m128i(
- _mm_loadu_si128((__m128i*)&s[0*n_channels]),
- _mm_loadu_si128((__m128i*)&s[4*n_channels]));
- in[1] = _mm256_setr_m128i(
- _mm_loadu_si128((__m128i*)&s[1*n_channels]),
- _mm_loadu_si128((__m128i*)&s[5*n_channels]));
- in[2] = _mm256_setr_m128i(
- _mm_loadu_si128((__m128i*)&s[2*n_channels]),
- _mm_loadu_si128((__m128i*)&s[6*n_channels]));
- in[3] = _mm256_setr_m128i(
- _mm_loadu_si128((__m128i*)&s[3*n_channels]),
- _mm_loadu_si128((__m128i*)&s[7*n_channels]));
-
- out[0] = _mm256_cvtepi32_ps(in[0]); /* a0 b0 c0 d0 a4 b4 c4 d4 */
- out[1] = _mm256_cvtepi32_ps(in[1]); /* a1 b1 c1 d1 a5 b5 c5 d5 */
- out[2] = _mm256_cvtepi32_ps(in[2]); /* a2 b2 c2 d2 a6 b6 c6 d6 */
- out[3] = _mm256_cvtepi32_ps(in[3]); /* a3 b3 c3 d3 a7 b7 c7 d7 */
-
- out[0] = _mm256_mul_ps(out[0], factor);
- out[1] = _mm256_mul_ps(out[1], factor);
- out[2] = _mm256_mul_ps(out[2], factor);
- out[3] = _mm256_mul_ps(out[3], factor);
-
- t[0] = _mm256_unpacklo_ps(out[0], out[2]); /* a0 a2 b0 b2 a4 a6 b4 b6 */
- t[1] = _mm256_unpackhi_ps(out[0], out[2]); /* c0 c2 d0 d2 c4 c6 d4 d6 */
- t[2] = _mm256_unpacklo_ps(out[1], out[3]); /* a1 a3 b1 b3 a5 a7 b5 b7 */
- t[3] = _mm256_unpackhi_ps(out[1], out[3]); /* c1 c3 d1 d3 c5 c7 d5 d7 */
-
- out[0] = _mm256_unpacklo_ps(t[0], t[2]); /* a0 a1 a2 a3 a4 a5 a6 a7 */
- out[1] = _mm256_unpackhi_ps(t[0], t[2]); /* b0 b1 b2 b3 b4 b5 b6 b7 */
- out[2] = _mm256_unpacklo_ps(t[1], t[3]); /* c0 c1 c2 c3 c4 c5 c6 c7 */
- out[3] = _mm256_unpackhi_ps(t[1], t[3]); /* d0 d1 d2 d3 d4 d5 d6 d7 */
-
- _mm256_store_ps(&d0[n], out[0]);
- _mm256_store_ps(&d1[n], out[1]);
- _mm256_store_ps(&d2[n], out[2]);
- _mm256_store_ps(&d3[n], out[3]);
-
- s += 8*n_channels;
- }
- for(; n < n_samples; n++) {
- __m128 out[4], factor = _mm_set1_ps(1.0f / S32_SCALE_I2F);
- __m128i in[1];
- in[0] = _mm_setr_epi32(s[0], s[1], s[2], s[3]);
- out[0] = _mm_cvtepi32_ps(in[0]);
- out[0] = _mm_mul_ps(out[0], factor);
- _MM_STOREM_PS(&d0[n], &d1[n], &d2[n], &d3[n], out[0]);
- s += n_channels;
- }
-}
-
void
conv_s32_to_f32d_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
uint32_t n_samples)
@@ -773,21 +582,12 @@ conv_s32_to_f32d_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const voi
const int32_t *s = src[0];
uint32_t i = 0, n_channels = conv->n_channels;
- if (conv->cpu_flags & SPA_CPU_FLAG_SLOW_GATHER) {
- for(; i + 3 < n_channels; i += 4)
- conv_s32_to_f32d_4s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
- for(; i + 1 < n_channels; i += 2)
- conv_s32_to_f32d_2s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
- for(; i < n_channels; i++)
- conv_s32_to_f32d_1s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
- } else {
- for(; i + 3 < n_channels; i += 4)
- conv_s32_to_f32d_4s_gather_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
- for(; i + 1 < n_channels; i += 2)
- conv_s32_to_f32d_2s_gather_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
- for(; i < n_channels; i++)
- conv_s32_to_f32d_1s_gather_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
- }
+ for(; i + 3 < n_channels; i += 4)
+ conv_s32_to_f32d_4s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
+ for(; i + 1 < n_channels; i += 2)
+ conv_s32_to_f32d_2s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
+ for(; i < n_channels; i++)
+ conv_s32_to_f32d_1s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
}
static void
@@ -812,10 +612,14 @@ conv_f32d_to_s32_1s_avx2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
in[0] = _mm_mul_ps(_mm_load_ps(&s0[n]), scale);
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]);
- _MM_STOREM_EPI32(&d[0*n_channels],
- &d[1*n_channels],
- &d[2*n_channels],
- &d[3*n_channels], out[0]);
+ out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
+ out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
+ out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
+
+ d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
+ d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
+ d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
+ d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {
@@ -970,7 +774,15 @@ conv_f32d_to_s32_4s_avx2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
__m128 int_min = _mm_set1_ps(S32_MIN_F2I);
__m128 int_max = _mm_set1_ps(S32_MAX_F2I);
- in[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]);
+ in[0] = _mm_load_ss(&s0[n]);
+ in[1] = _mm_load_ss(&s1[n]);
+ in[2] = _mm_load_ss(&s2[n]);
+ in[3] = _mm_load_ss(&s3[n]);
+
+ in[0] = _mm_unpacklo_ps(in[0], in[2]);
+ in[1] = _mm_unpacklo_ps(in[1], in[3]);
+ in[0] = _mm_unpacklo_ps(in[0], in[1]);
+
in[0] = _mm_mul_ps(in[0], scale);
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]);
@@ -1160,16 +972,18 @@ conv_f32d_to_s16_4s_avx2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
__m128 int_max = _mm_set1_ps(S16_MAX);
__m128 int_min = _mm_set1_ps(S16_MIN);
- in[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]);
- in[0] = _mm_mul_ps(in[0], int_scale);
- in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
-
- _MM_TRANS_1x4_PS(in[0], in[1], in[2], in[3]);
+ in[0] = _mm_mul_ss(_mm_load_ss(&s0[n]), int_scale);
+ in[1] = _mm_mul_ss(_mm_load_ss(&s1[n]), int_scale);
+ in[2] = _mm_mul_ss(_mm_load_ss(&s2[n]), int_scale);
+ in[3] = _mm_mul_ss(_mm_load_ss(&s3[n]), int_scale);
+ in[0] = _MM_CLAMP_SS(in[0], int_min, int_max);
+ in[1] = _MM_CLAMP_SS(in[1], int_min, int_max);
+ in[2] = _MM_CLAMP_SS(in[2], int_min, int_max);
+ in[3] = _MM_CLAMP_SS(in[3], int_min, int_max);
d[0] = _mm_cvtss_si32(in[0]);
d[1] = _mm_cvtss_si32(in[1]);
d[2] = _mm_cvtss_si32(in[2]);
d[3] = _mm_cvtss_si32(in[3]);
-
d += n_channels;
}
}
@@ -1241,10 +1055,14 @@ conv_f32d_to_s16_4_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const v
__m128 int_max = _mm_set1_ps(S16_MAX);
__m128 int_min = _mm_set1_ps(S16_MIN);
- in[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]);
- in[0] = _mm_mul_ps(in[0], int_scale);
- in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
- _MM_TRANS_1x4_PS(in[0], in[1], in[2], in[3]);
+ in[0] = _mm_mul_ss(_mm_load_ss(&s0[n]), int_scale);
+ in[1] = _mm_mul_ss(_mm_load_ss(&s1[n]), int_scale);
+ in[2] = _mm_mul_ss(_mm_load_ss(&s2[n]), int_scale);
+ in[3] = _mm_mul_ss(_mm_load_ss(&s3[n]), int_scale);
+ in[0] = _MM_CLAMP_SS(in[0], int_min, int_max);
+ in[1] = _MM_CLAMP_SS(in[1], int_min, int_max);
+ in[2] = _MM_CLAMP_SS(in[2], int_min, int_max);
+ in[3] = _MM_CLAMP_SS(in[3], int_min, int_max);
d[0] = _mm_cvtss_si32(in[0]);
d[1] = _mm_cvtss_si32(in[1]);
d[2] = _mm_cvtss_si32(in[2]);
@@ -1367,4 +1185,3 @@ conv_f32d_to_s16s_2_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const
d += 2;
}
}
-
diff --git a/spa/plugins/audioconvert/fmt-ops-sse2.c b/spa/plugins/audioconvert/fmt-ops-sse2.c
index dcba6d2e1..ee5c89c06 100644
--- a/spa/plugins/audioconvert/fmt-ops-sse2.c
+++ b/spa/plugins/audioconvert/fmt-ops-sse2.c
@@ -26,72 +26,6 @@
a = _mm_shufflehi_epi16(a, _MM_SHUFFLE(2, 3, 0, 1)); \
})
-#define spa_read_unaligned(ptr, type) \
-__extension__ ({ \
- __typeof__(type) _val; \
- memcpy(&_val, (ptr), sizeof(_val)); \
- _val; \
-})
-
-#define spa_write_unaligned(ptr, type, val) \
-__extension__ ({ \
- __typeof__(type) _val = (val); \
- memcpy((ptr), &_val, sizeof(_val)); \
-})
-
-#define _MM_TRANS_1x4_PS(v0,v1,v2,v3) \
-({ \
- v1 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(0, 3, 2, 1)); \
- v2 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(1, 0, 3, 2)); \
- v3 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(2, 1, 0, 3)); \
-})
-
-#define _MM_TRANS_1x4_EPI32(v0,v1,v2,v3) \
-({ \
- v1 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(0, 3, 2, 1)); \
- v2 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(1, 0, 3, 2)); \
- v3 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(2, 1, 0, 3)); \
-})
-#if 0
-#define _MM_STOREM_PS(d0,d1,d2,d3,v) \
-({ \
- *d0 = v[0]; \
- *d1 = v[1]; \
- *d2 = v[2]; \
- *d3 = v[3]; \
-})
-#else
-#define _MM_STOREM_PS(d0,d1,d2,d3,v) \
-({ \
- __m128 o[3]; \
- _MM_TRANS_1x4_PS(v, o[0], o[1], o[2]); \
- _mm_store_ss(d0, v); \
- _mm_store_ss(d1, o[0]); \
- _mm_store_ss(d2, o[1]); \
- _mm_store_ss(d3, o[2]); \
-})
-#endif
-
-#define _MM_STOREM_EPI32(d0,d1,d2,d3,v) \
-({ \
- __m128i o[3]; \
- _MM_TRANS_1x4_EPI32(v, o[0], o[1], o[2]); \
- *d0 = _mm_cvtsi128_si32(v); \
- *d1 = _mm_cvtsi128_si32(o[0]); \
- *d2 = _mm_cvtsi128_si32(o[1]); \
- *d3 = _mm_cvtsi128_si32(o[2]); \
-})
-
-#define _MM_STOREUM_EPI32(d0,d1,d2,d3,v) \
-({ \
- __m128i o[3]; \
- _MM_TRANS_1x4_EPI32(v, o[0], o[1], o[2]); \
- spa_write_unaligned(d0, uint32_t, _mm_cvtsi128_si32(v)); \
- spa_write_unaligned(d1, uint32_t, _mm_cvtsi128_si32(o[0])); \
- spa_write_unaligned(d2, uint32_t, _mm_cvtsi128_si32(o[1])); \
- spa_write_unaligned(d3, uint32_t, _mm_cvtsi128_si32(o[2])); \
-})
-
static void
conv_s16_to_f32d_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
@@ -299,6 +233,18 @@ conv_s16s_to_f32d_2_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const
}
}
+#define spa_read_unaligned(ptr, type) \
+__extension__ ({ \
+ __typeof__(type) _val; \
+ memcpy(&_val, (ptr), sizeof(_val)); \
+ _val; \
+})
+
+#define spa_write_unaligned(ptr, type, val) \
+__extension__ ({ \
+ __typeof__(type) _val = (val); \
+ memcpy((ptr), &_val, sizeof(_val)); \
+})
void
conv_s24_to_f32d_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
@@ -470,13 +416,18 @@ conv_s24_to_f32d_4s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA
s += 4 * n_channels;
}
for(; n < n_samples; n++) {
- in[0] = _mm_setr_epi32(s24_to_s32(*s),
- s24_to_s32(*(s+1)),
- s24_to_s32(*(s+2)),
- s24_to_s32(*(s+3)));
- out[0] = _mm_cvtepi32_ps(in[0]);
- out[0] = _mm_mul_ps(out[0], factor);
- _MM_STOREM_PS(&d0[n], &d1[n], &d2[n], &d3[n], out[0]);
+ out[0] = _mm_cvtsi32_ss(factor, s24_to_s32(*s));
+ out[1] = _mm_cvtsi32_ss(factor, s24_to_s32(*(s+1)));
+ out[2] = _mm_cvtsi32_ss(factor, s24_to_s32(*(s+2)));
+ out[3] = _mm_cvtsi32_ss(factor, s24_to_s32(*(s+3)));
+ out[0] = _mm_mul_ss(out[0], factor);
+ out[1] = _mm_mul_ss(out[1], factor);
+ out[2] = _mm_mul_ss(out[2], factor);
+ out[3] = _mm_mul_ss(out[3], factor);
+ _mm_store_ss(&d0[n], out[0]);
+ _mm_store_ss(&d1[n], out[1]);
+ _mm_store_ss(&d2[n], out[2]);
+ _mm_store_ss(&d3[n], out[3]);
s += n_channels;
}
}
@@ -496,59 +447,6 @@ conv_s24_to_f32d_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const voi
conv_s24_to_f32d_1s_sse2(conv, &dst[i], &s[3*i], n_channels, n_samples);
}
-static void
-conv_s32_to_f32d_4s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
- uint32_t n_channels, uint32_t n_samples)
-{
- const int32_t *s = src;
- float *d0 = dst[0], *d1 = dst[1], *d2 = dst[2], *d3 = dst[3];
- uint32_t n, unrolled;
- __m128i in[4];
- __m128 out[4], factor = _mm_set1_ps(1.0f / S32_SCALE_I2F);
-
- if (SPA_IS_ALIGNED(d0, 16) &&
- SPA_IS_ALIGNED(d1, 16) &&
- SPA_IS_ALIGNED(d2, 16) &&
- SPA_IS_ALIGNED(d3, 16) &&
- SPA_IS_ALIGNED(s, 16) && (n_channels & 3) == 0)
- unrolled = n_samples & ~3;
- else
- unrolled = 0;
-
- for(n = 0; n < unrolled; n += 4) {
- in[0] = _mm_load_si128((__m128i*)(s + 0*n_channels));
- in[1] = _mm_load_si128((__m128i*)(s + 1*n_channels));
- in[2] = _mm_load_si128((__m128i*)(s + 2*n_channels));
- in[3] = _mm_load_si128((__m128i*)(s + 3*n_channels));
-
- out[0] = _mm_cvtepi32_ps(in[0]);
- out[1] = _mm_cvtepi32_ps(in[1]);
- out[2] = _mm_cvtepi32_ps(in[2]);
- out[3] = _mm_cvtepi32_ps(in[3]);
-
- out[0] = _mm_mul_ps(out[0], factor);
- out[1] = _mm_mul_ps(out[1], factor);
- out[2] = _mm_mul_ps(out[2], factor);
- out[3] = _mm_mul_ps(out[3], factor);
-
- _MM_TRANSPOSE4_PS(out[0], out[1], out[2], out[3]);
-
- _mm_store_ps(&d0[n], out[0]);
- _mm_store_ps(&d1[n], out[1]);
- _mm_store_ps(&d2[n], out[2]);
- _mm_store_ps(&d3[n], out[3]);
-
- s += 4*n_channels;
- }
- for(; n < n_samples; n++) {
- in[0] = _mm_setr_epi32(s[0], s[1], s[2], s[3]);
- out[0] = _mm_cvtepi32_ps(in[0]);
- out[0] = _mm_mul_ps(out[0], factor);
- _MM_STOREM_PS(&d0[n], &d1[n], &d2[n], &d3[n], out[0]);
- s += n_channels;
- }
-}
-
static void
conv_s32_to_f32d_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
@@ -589,8 +487,6 @@ conv_s32_to_f32d_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const voi
const int32_t *s = src[0];
uint32_t i = 0, n_channels = conv->n_channels;
- for(; i + 3 < n_channels; i += 4)
- conv_s32_to_f32d_4s_sse2(conv, &dst[i], &s[i], n_channels, n_samples);
for(; i < n_channels; i++)
conv_s32_to_f32d_1s_sse2(conv, &dst[i], &s[i], n_channels, n_samples);
}
@@ -617,10 +513,14 @@ conv_f32d_to_s32_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
in[0] = _mm_mul_ps(_mm_load_ps(&s0[n]), scale);
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]);
- _MM_STOREM_EPI32(&d[0*n_channels],
- &d[1*n_channels],
- &d[2*n_channels],
- &d[3*n_channels], out[0]);
+ out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
+ out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
+ out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
+
+ d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
+ d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
+ d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
+ d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {
@@ -730,7 +630,15 @@ conv_f32d_to_s32_4s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
d += 4*n_channels;
}
for(; n < n_samples; n++) {
- in[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]);
+ in[0] = _mm_load_ss(&s0[n]);
+ in[1] = _mm_load_ss(&s1[n]);
+ in[2] = _mm_load_ss(&s2[n]);
+ in[3] = _mm_load_ss(&s3[n]);
+
+ in[0] = _mm_unpacklo_ps(in[0], in[2]);
+ in[1] = _mm_unpacklo_ps(in[1], in[3]);
+ in[0] = _mm_unpacklo_ps(in[0], in[1]);
+
in[0] = _mm_mul_ps(in[0], scale);
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]);
@@ -846,10 +754,14 @@ conv_f32d_to_s32_1s_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst, co
in[0] = _mm_add_ps(in[0], _mm_load_ps(&noise[n]));
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]);
- _MM_STOREM_EPI32(&d[0*n_channels],
- &d[1*n_channels],
- &d[2*n_channels],
- &d[3*n_channels], out[0]);
+ out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
+ out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
+ out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
+
+ d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
+ d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
+ d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
+ d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {
@@ -898,10 +810,14 @@ conv_interleave_32_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA
for(n = 0; n < unrolled; n += 4) {
out[0] = _mm_load_si128((__m128i*)&s0[n]);
- _MM_STOREM_EPI32(&d[0*n_channels],
- &d[1*n_channels],
- &d[2*n_channels],
- &d[3*n_channels], out[0]);
+ out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
+ out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
+ out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
+
+ d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
+ d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
+ d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
+ d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {
@@ -977,10 +893,14 @@ conv_interleave_32s_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SP
for(n = 0; n < unrolled; n += 4) {
out[0] = _mm_load_si128((__m128i*)&s0[n]);
out[0] = _MM_BSWAP_EPI32(out[0]);
- _MM_STOREM_EPI32(&d[0*n_channels],
- &d[1*n_channels],
- &d[2*n_channels],
- &d[3*n_channels], out[0]);
+ out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
+ out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
+ out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
+
+ d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
+ d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
+ d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
+ d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {
@@ -1337,10 +1257,14 @@ conv_f32d_to_s16_2s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
t[1] = _mm_packs_epi32(t[1], t[1]);
out[0] = _mm_unpacklo_epi16(t[0], t[1]);
- _MM_STOREUM_EPI32(&d[0*n_channels],
- &d[1*n_channels],
- &d[2*n_channels],
- &d[3*n_channels], out[0]);
+ out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
+ out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
+ out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
+
+ spa_write_unaligned(d + 0*n_channels, uint32_t, _mm_cvtsi128_si32(out[0]));
+ spa_write_unaligned(d + 1*n_channels, uint32_t, _mm_cvtsi128_si32(out[1]));
+ spa_write_unaligned(d + 2*n_channels, uint32_t, _mm_cvtsi128_si32(out[2]));
+ spa_write_unaligned(d + 3*n_channels, uint32_t, _mm_cvtsi128_si32(out[3]));
d += 4*n_channels;
}
for(; n < n_samples; n++) {
diff --git a/spa/plugins/audioconvert/fmt-ops.c b/spa/plugins/audioconvert/fmt-ops.c
index 34de40445..3fc2c5f0a 100644
--- a/spa/plugins/audioconvert/fmt-ops.c
+++ b/spa/plugins/audioconvert/fmt-ops.c
@@ -631,7 +631,7 @@ int convert_init(struct convert *conv)
conv->random[i] = random();
conv->is_passthrough = conv->src_fmt == conv->dst_fmt;
- conv->func_cpu_flags = info->cpu_flags;
+ conv->cpu_flags = info->cpu_flags;
conv->update_noise = ninfo->noise;
conv->process = info->process;
conv->clear = cinfo ? cinfo->clear : NULL;
diff --git a/spa/plugins/audioconvert/fmt-ops.h b/spa/plugins/audioconvert/fmt-ops.h
index 24b4b1aaf..f738e3858 100644
--- a/spa/plugins/audioconvert/fmt-ops.h
+++ b/spa/plugins/audioconvert/fmt-ops.h
@@ -219,7 +219,6 @@ struct convert {
uint32_t n_channels;
uint32_t rate;
uint32_t cpu_flags;
- uint32_t func_cpu_flags;
const char *func_name;
unsigned int is_passthrough:1;
diff --git a/spa/plugins/audioconvert/meson.build b/spa/plugins/audioconvert/meson.build
index 71d5d56cd..bd60872b6 100644
--- a/spa/plugins/audioconvert/meson.build
+++ b/spa/plugins/audioconvert/meson.build
@@ -44,7 +44,7 @@ endif
if have_sse2
audioconvert_sse2 = static_library('audioconvert_sse2',
['fmt-ops-sse2.c' ],
- c_args : [sse2_args, '-O3', '-DHAVE_SSE2', simd_cargs],
+ c_args : [sse2_args, '-O3', '-DHAVE_SSE2'],
dependencies : [ spa_dep ],
install : false
)
@@ -55,7 +55,7 @@ if have_ssse3
audioconvert_ssse3 = static_library('audioconvert_ssse3',
['fmt-ops-ssse3.c',
'resample-native-ssse3.c' ],
- c_args : [ssse3_args, '-O3', '-DHAVE_SSSE3', simd_cargs],
+ c_args : [ssse3_args, '-O3', '-DHAVE_SSSE3'],
dependencies : [ spa_dep ],
install : false
)
@@ -65,27 +65,17 @@ endif
if have_sse41
audioconvert_sse41 = static_library('audioconvert_sse41',
['fmt-ops-sse41.c'],
- c_args : [sse41_args, '-O3', '-DHAVE_SSE41', simd_cargs],
+ c_args : [sse41_args, '-O3', '-DHAVE_SSE41'],
dependencies : [ spa_dep ],
install : false
)
simd_cargs += ['-DHAVE_SSE41']
simd_dependencies += audioconvert_sse41
endif
-if have_avx
- audioconvert_avx = static_library('audioconvert_avx',
- ['channelmix-ops-avx.c'],
- c_args : [avx_args, '-O3', '-DHAVE_AVX', simd_cargs],
- dependencies : [ spa_dep ],
- install : false
- )
- simd_cargs += ['-DHAVE_AVX']
- simd_dependencies += audioconvert_avx
-endif
if have_avx2 and have_fma
audioconvert_avx2_fma = static_library('audioconvert_avx2_fma',
['resample-native-avx2.c'],
- c_args : [avx2_args, fma_args, '-O3', '-DHAVE_AVX2', '-DHAVE_FMA', simd_cargs],
+ c_args : [avx2_args, fma_args, '-O3', '-DHAVE_AVX2', '-DHAVE_FMA'],
dependencies : [ spa_dep ],
install : false
)
@@ -95,7 +85,7 @@ endif
if have_avx2
audioconvert_avx2 = static_library('audioconvert_avx2',
['fmt-ops-avx2.c'],
- c_args : [avx2_args, '-O3', '-DHAVE_AVX2', simd_cargs],
+ c_args : [avx2_args, '-O3', '-DHAVE_AVX2'],
dependencies : [ spa_dep ],
install : false
)
diff --git a/spa/plugins/audioconvert/peaks-ops.c b/spa/plugins/audioconvert/peaks-ops.c
index f7a897f90..29b93a081 100644
--- a/spa/plugins/audioconvert/peaks-ops.c
+++ b/spa/plugins/audioconvert/peaks-ops.c
@@ -60,7 +60,7 @@ int peaks_init(struct peaks *peaks)
if (info == NULL)
return -ENOTSUP;
- peaks->func_cpu_flags = info->cpu_flags;
+ peaks->cpu_flags = info->cpu_flags;
peaks->func_name = info->name;
peaks->free = impl_peaks_free;
peaks->min_max = info->min_max;
diff --git a/spa/plugins/audioconvert/peaks-ops.h b/spa/plugins/audioconvert/peaks-ops.h
index 40b20cfbc..24092a4f7 100644
--- a/spa/plugins/audioconvert/peaks-ops.h
+++ b/spa/plugins/audioconvert/peaks-ops.h
@@ -14,7 +14,6 @@ extern struct spa_log_topic resample_log_topic;
struct peaks {
uint32_t cpu_flags;
- uint32_t func_cpu_flags;
const char *func_name;
struct spa_log *log;
diff --git a/spa/plugins/audioconvert/resample-native.c b/spa/plugins/audioconvert/resample-native.c
index 5bb33ffc1..3604c5b45 100644
--- a/spa/plugins/audioconvert/resample-native.c
+++ b/spa/plugins/audioconvert/resample-native.c
@@ -576,7 +576,7 @@ int resample_native_init(struct resample *r)
r, c->cutoff, r->quality, c->window, r->i_rate, r->o_rate, gcd, n_taps, n_phases,
r->cpu_flags, d->info->cpu_flags);
- r->func_cpu_flags = d->info->cpu_flags;
+ r->cpu_flags = d->info->cpu_flags;
impl_native_reset(r);
impl_native_update_rate(r, 1.0);
diff --git a/spa/plugins/audioconvert/resample.h b/spa/plugins/audioconvert/resample.h
index 7b6e58415..fec3bf963 100644
--- a/spa/plugins/audioconvert/resample.h
+++ b/spa/plugins/audioconvert/resample.h
@@ -38,7 +38,6 @@ struct resample {
#define RESAMPLE_OPTION_PREFILL (1<<0)
uint32_t options;
uint32_t cpu_flags;
- uint32_t func_cpu_flags;
const char *func_name;
uint32_t channels;
diff --git a/spa/plugins/audioconvert/test-audioconvert.c b/spa/plugins/audioconvert/test-audioconvert.c
index de18d524b..de3ebb8b5 100644
--- a/spa/plugins/audioconvert/test-audioconvert.c
+++ b/spa/plugins/audioconvert/test-audioconvert.c
@@ -54,7 +54,7 @@ static int setup_context(struct context *ctx)
size_t size;
int res;
struct spa_support support[1];
- struct spa_dict_item items[9];
+ struct spa_dict_item items[6];
const struct spa_handle_factory *factory;
void *iface;
@@ -76,13 +76,10 @@ static int setup_context(struct context *ctx)
items[3] = SPA_DICT_ITEM_INIT("channelmix.lfe-cutoff", "150");
items[4] = SPA_DICT_ITEM_INIT("channelmix.fc-cutoff", "12000");
items[5] = SPA_DICT_ITEM_INIT("channelmix.rear-delay", "12.0");
- items[6] = SPA_DICT_ITEM_INIT("channelmix.center-level", "0.707106781");
- items[7] = SPA_DICT_ITEM_INIT("channelmix.surround-level", "0.707106781");
- items[8] = SPA_DICT_ITEM_INIT("channelmix.lfe-level", "0.5");
res = spa_handle_factory_init(factory,
ctx->convert_handle,
- &SPA_DICT_INIT(items, 9),
+ &SPA_DICT_INIT(items, 6),
support, 1);
spa_assert_se(res >= 0);
diff --git a/spa/plugins/audioconvert/test-channelmix.c b/spa/plugins/audioconvert/test-channelmix.c
index 529db880f..b68c956bf 100644
--- a/spa/plugins/audioconvert/test-channelmix.c
+++ b/spa/plugins/audioconvert/test-channelmix.c
@@ -45,7 +45,7 @@ static void test_mix(uint32_t src_chan, uint32_t src_mask, uint32_t dst_chan, ui
spa_log_debug(&logger.log, "start %d->%d (%08x -> %08x)", src_chan, dst_chan, src_mask, dst_mask);
- channelmix_reset(&mix);
+ spa_zero(mix);
mix.options = options;
mix.src_chan = src_chan;
mix.dst_chan = dst_chan;
@@ -340,7 +340,7 @@ static void test_n_m_impl(void)
src[i] = src_data[i];
}
- channelmix_reset(&mix);
+ spa_zero(mix);
mix.src_chan = 16;
mix.dst_chan = 12;
mix.log = &logger.log;
diff --git a/spa/plugins/audioconvert/test-fmt-ops.c b/spa/plugins/audioconvert/test-fmt-ops.c
index d5ca414ef..17a26a351 100644
--- a/spa/plugins/audioconvert/test-fmt-ops.c
+++ b/spa/plugins/audioconvert/test-fmt-ops.c
@@ -45,9 +45,9 @@ static void run_test(const char *name,
void *tp[N_CHANNELS];
int i, j;
const uint8_t *in8 = in, *out8 = out;
- struct convert conv = {
- .n_channels = N_CHANNELS,
- };
+ struct convert conv;
+
+ conv.n_channels = N_CHANNELS;
for (j = 0; j < N_SAMPLES; j++) {
memcpy(&samp_in[j * in_size], &in8[(j % n_samples) * in_size], in_size);
diff --git a/spa/plugins/audioconvert/volume-ops.c b/spa/plugins/audioconvert/volume-ops.c
index b76ab4bec..bf6aa6909 100644
--- a/spa/plugins/audioconvert/volume-ops.c
+++ b/spa/plugins/audioconvert/volume-ops.c
@@ -56,7 +56,7 @@ int volume_init(struct volume *vol)
if (info == NULL)
return -ENOTSUP;
- vol->func_cpu_flags = info->cpu_flags;
+ vol->cpu_flags = info->cpu_flags;
vol->func_name = info->name;
vol->free = impl_volume_free;
vol->process = info->process;
diff --git a/spa/plugins/audioconvert/volume-ops.h b/spa/plugins/audioconvert/volume-ops.h
index 51642110f..a50ee9a6f 100644
--- a/spa/plugins/audioconvert/volume-ops.h
+++ b/spa/plugins/audioconvert/volume-ops.h
@@ -13,7 +13,6 @@
struct volume {
uint32_t cpu_flags;
- uint32_t func_cpu_flags;
const char *func_name;
struct spa_log *log;
diff --git a/spa/plugins/audiotestsrc/audiotestsrc.c b/spa/plugins/audiotestsrc/audiotestsrc.c
index 3414e8b18..5e7c521b8 100644
--- a/spa/plugins/audiotestsrc/audiotestsrc.c
+++ b/spa/plugins/audiotestsrc/audiotestsrc.c
@@ -41,11 +41,13 @@ enum wave_type {
#define DEFAULT_RATE 48000
#define DEFAULT_CHANNELS 2
+#define DEFAULT_LIVE true
#define DEFAULT_WAVE WAVE_SINE
#define DEFAULT_FREQ 440.0
#define DEFAULT_VOLUME 1.0
struct props {
+ bool live;
uint32_t wave;
float freq;
float volume;
@@ -53,6 +55,7 @@ struct props {
static void reset_props(struct props *props)
{
+ props->live = DEFAULT_LIVE;
props->wave = DEFAULT_WAVE;
props->freq = DEFAULT_FREQ;
props->volume = DEFAULT_VOLUME;
@@ -115,7 +118,9 @@ struct impl {
struct spa_hook_list hooks;
struct spa_callbacks callbacks;
+ bool async;
struct spa_source timer_source;
+ struct itimerspec timerspec;
bool started;
uint64_t start_time;
@@ -157,6 +162,13 @@ static int impl_node_enum_params(void *object, int seq,
switch (result.index) {
case 0:
+ param = spa_pod_builder_add_object(&b,
+ SPA_TYPE_OBJECT_PropInfo, id,
+ SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_live),
+ SPA_PROP_INFO_description, SPA_POD_String("Configure live mode of the source"),
+ SPA_PROP_INFO_type, SPA_POD_Bool(p->live));
+ break;
+ case 1:
spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(&b,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_waveType),
@@ -172,14 +184,14 @@ static int impl_node_enum_params(void *object, int seq,
spa_pod_builder_pop(&b, &f[1]);
param = spa_pod_builder_pop(&b, &f[0]);
break;
- case 1:
+ case 2:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_frequency),
SPA_PROP_INFO_description, SPA_POD_String("Select the frequency"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->freq, 0.0, 50000000.0));
break;
- case 2:
+ case 3:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_volume),
@@ -199,6 +211,7 @@ static int impl_node_enum_params(void *object, int seq,
case 0:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, id,
+ SPA_PROP_live, SPA_POD_Bool(p->live),
SPA_PROP_waveType, SPA_POD_Int(p->wave),
SPA_PROP_frequency, SPA_POD_Float(p->freq),
SPA_PROP_volume, SPA_POD_Float(p->volume));
@@ -252,6 +265,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
if (id == SPA_PARAM_Props) {
struct props *p = &this->props;
+ struct port *port = &this->port;
if (param == NULL) {
reset_props(p);
@@ -259,9 +273,15 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
}
spa_pod_parse_object(param,
SPA_TYPE_OBJECT_Props, NULL,
+ SPA_PROP_live, SPA_POD_OPT_Bool(&p->live),
SPA_PROP_waveType, SPA_POD_OPT_Int(&p->wave),
SPA_PROP_frequency, SPA_POD_OPT_Float(&p->freq),
SPA_PROP_volume, SPA_POD_OPT_Float(&p->volume));
+
+ if (p->live)
+ port->info.flags |= SPA_PORT_FLAG_LIVE;
+ else
+ port->info.flags &= ~SPA_PORT_FLAG_LIVE;
}
else
return -ENOENT;
@@ -296,15 +316,23 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
static void set_timer(struct impl *this, bool enabled)
{
- struct itimerspec ts = {0};
-
- if (enabled) {
- uint64_t next_time = this->start_time + this->elapsed_time;
- ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
- ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
+ if (this->async || this->props.live) {
+ if (enabled) {
+ if (this->props.live) {
+ uint64_t next_time = this->start_time + this->elapsed_time;
+ this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
+ this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
+ } else {
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 1;
+ }
+ } else {
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ }
+ spa_system_timerfd_settime(this->data_system,
+ this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
}
-
- spa_system_timerfd_settime(this->data_system, this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
}
static int read_timer(struct impl *this)
@@ -312,12 +340,14 @@ static int read_timer(struct impl *this)
uint64_t expirations;
int res = 0;
- if ((res = spa_system_timerfd_read(this->data_system, this->timer_source.fd, &expirations)) < 0) {
- if (res != -EAGAIN)
- spa_log_error(this->log, "%p: timerfd error: %s",
- this, spa_strerror(res));
+ if (this->async || this->props.live) {
+ if ((res = spa_system_timerfd_read(this->data_system,
+ this->timer_source.fd, &expirations)) < 0) {
+ if (res != -EAGAIN)
+ spa_log_error(this->log, "%p: timerfd error: %s",
+ this, spa_strerror(res));
+ }
}
-
return 0;
}
@@ -441,7 +471,10 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
return 0;
clock_gettime(CLOCK_MONOTONIC, &now);
- this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
+ if (this->props.live)
+ this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
+ else
+ this->start_time = 0;
this->sample_count = 0;
this->elapsed_time = 0;
@@ -862,6 +895,9 @@ static inline void reuse_buffer(struct impl *this, struct port *port, uint32_t i
b->outstanding = false;
spa_list_append(&port->empty, &b->link);
+
+ if (!this->props.live && !this->following)
+ set_timer(this, true);
}
static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
@@ -935,7 +971,7 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
}
- if (this->following)
+ if (!this->props.live || this->following)
return make_buffer(this);
else
return SPA_STATUS_OK;
@@ -1069,6 +1105,10 @@ impl_init(const struct spa_handle_factory *factory,
CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ this->timerspec.it_interval.tv_sec = 0;
+ this->timerspec.it_interval.tv_nsec = 0;
if (this->data_loop)
spa_loop_add_source(this->data_loop, &this->timer_source);
@@ -1077,7 +1117,9 @@ impl_init(const struct spa_handle_factory *factory,
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
SPA_PORT_CHANGE_MASK_PARAMS;
port->info = SPA_PORT_INFO_INIT();
- port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_LIVE;
+ port->info.flags = SPA_PORT_FLAG_NO_REF;
+ if (this->props.live)
+ port->info.flags |= SPA_PORT_FLAG_LIVE;
port->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
port->params[1] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
port->params[2] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
diff --git a/spa/plugins/bluez5/a2dp-codec-aac.c b/spa/plugins/bluez5/a2dp-codec-aac.c
index f4cbecd9b..c49f7a616 100644
--- a/spa/plugins/bluez5/a2dp-codec-aac.c
+++ b/spa/plugins/bluez5/a2dp-codec-aac.c
@@ -106,8 +106,8 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
static const struct media_codec_config
aac_frequencies[] = {
- { AAC_SAMPLING_FREQ_44100, 44100, 10 },
{ AAC_SAMPLING_FREQ_48000, 48000, 11 },
+ { AAC_SAMPLING_FREQ_44100, 44100, 10 },
{ AAC_SAMPLING_FREQ_96000, 96000, 9 },
{ AAC_SAMPLING_FREQ_88200, 88200, 8 },
{ AAC_SAMPLING_FREQ_64000, 64000, 7 },
@@ -194,6 +194,75 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags,
return sizeof(conf);
}
+static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
+ const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
+ struct spa_pod_builder *b, struct spa_pod **param)
+{
+ a2dp_aac_t conf;
+ struct spa_pod_frame f[2];
+ struct spa_pod_choice *choice;
+ uint32_t position[2];
+ uint32_t i = 0;
+
+ if (caps_size < sizeof(conf))
+ return -EINVAL;
+
+ memcpy(&conf, caps, sizeof(conf));
+
+ if (idx > 0)
+ return 0;
+
+ spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id);
+ spa_pod_builder_add(b,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
+ SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16),
+ 0);
+ spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0);
+
+ spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
+ choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
+ i = 0;
+ SPA_FOR_EACH_ELEMENT_VAR(aac_frequencies, f) {
+ if (AAC_GET_FREQUENCY(conf) & f->config) {
+ if (i++ == 0)
+ spa_pod_builder_int(b, f->value);
+ spa_pod_builder_int(b, f->value);
+ }
+ }
+ if (i > 1)
+ choice->body.type = SPA_CHOICE_Enum;
+ spa_pod_builder_pop(b, &f[1]);
+
+ if (i == 0)
+ return -EINVAL;
+
+ if (SPA_FLAG_IS_SET(conf.channels, AAC_CHANNELS_1 | AAC_CHANNELS_2)) {
+ spa_pod_builder_add(b,
+ SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2),
+ 0);
+ } else if (conf.channels & AAC_CHANNELS_1) {
+ position[0] = SPA_AUDIO_CHANNEL_MONO;
+ spa_pod_builder_add(b,
+ SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1),
+ SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
+ SPA_TYPE_Id, 1, position),
+ 0);
+ } else if (conf.channels & AAC_CHANNELS_2) {
+ position[0] = SPA_AUDIO_CHANNEL_FL;
+ position[1] = SPA_AUDIO_CHANNEL_FR;
+ spa_pod_builder_add(b,
+ SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2),
+ SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
+ SPA_TYPE_Id, 2, position),
+ 0);
+ } else
+ return -EINVAL;
+
+ *param = spa_pod_builder_pop(b, &f[0]);
+ return *param == NULL ? -EIO : 1;
+}
+
static int codec_validate_config(const struct media_codec *codec, uint32_t flags,
const void *caps, size_t caps_size,
struct spa_audio_info *info)
@@ -214,10 +283,8 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
/*
* A2DP v1.3.2, 4.5.2: only one bit shall be set in bitfields.
* However, there is a report (#1342) of device setting multiple
- * bits for AAC object type. In addition AirPods set multiple bits.
- *
- * Some devices also set multiple bits in frequencies & channels.
- * For these, pick a "preferred" choice.
+ * bits for AAC object type. It's not clear if this was due to
+ * a BlueZ bug, but we can be lax here and below in codec_init.
*/
if (!(conf.object_type & (AAC_OBJECT_TYPE_MPEG2_AAC_LC |
AAC_OBJECT_TYPE_MPEG4_AAC_LC |
@@ -248,35 +315,6 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
return 0;
}
-static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
- const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
- struct spa_pod_builder *b, struct spa_pod **param)
-{
- struct spa_audio_info info;
- struct spa_pod_frame f[1];
- int res;
-
- if ((res = codec_validate_config(codec, flags, caps, caps_size, &info)) < 0)
- return res;
-
- if (idx > 0)
- return 0;
-
- spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id);
- spa_pod_builder_add(b,
- SPA_FORMAT_mediaType, SPA_POD_Id(info.media_type),
- SPA_FORMAT_mediaSubtype, SPA_POD_Id(info.media_subtype),
- SPA_FORMAT_AUDIO_format, SPA_POD_Id(info.info.raw.format),
- SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info.info.raw.rate),
- SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info.info.raw.channels),
- SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
- SPA_TYPE_Id, info.info.raw.channels, info.info.raw.position),
- 0);
-
- *param = spa_pod_builder_pop(b, &f[0]);
- return *param == NULL ? -EIO : 1;
-}
-
static void *codec_init_props(const struct media_codec *codec, uint32_t flags, const struct spa_dict *settings)
{
struct props *p = calloc(1, sizeof(struct props));
@@ -286,7 +324,7 @@ static void *codec_init_props(const struct media_codec *codec, uint32_t flags, c
return NULL;
if (settings == NULL || (str = spa_dict_lookup(settings, "bluez5.a2dp.aac.bitratemode")) == NULL)
- str = "5";
+ str = "0";
p->bitratemode = SPA_CLAMP(atoi(str), 0, 5);
return p;
@@ -331,14 +369,14 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
goto error;
/* If object type has multiple bits set (invalid per spec, see above),
- * assume the device usually means MPEG2 AAC LC which is mandatory.
+ * assume the device usually means AAC-LC.
*/
- if (conf->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) {
- res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_MP2_AAC_LC);
+ if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) {
+ res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_AAC_LC);
if (res != AACENC_OK)
goto error;
- } else if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) {
- res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_AAC_LC);
+ } else if (conf->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) {
+ res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_MP2_AAC_LC);
if (res != AACENC_OK)
goto error;
} else if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_ELD) {
@@ -380,12 +418,8 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
// Fragmentation is not implemented yet,
// so make sure every encoded AAC frame fits in (mtu - header)
this->max_bitrate = ((this->mtu - sizeof(struct rtp_header)) * 8 * this->rate) / 1024;
- this->cur_bitrate = SPA_MIN(this->max_bitrate, get_valid_aac_bitrate(conf));
- spa_log_debug(log, "AAC: max (peak) bitrate: %d, cur bitrate: %d, mode: %d (vbr: %d)",
- this->max_bitrate,
- this->cur_bitrate,
- bitratemode,
- conf->vbr);
+ this->max_bitrate = SPA_MIN(this->max_bitrate, get_valid_aac_bitrate(conf));
+ this->cur_bitrate = this->max_bitrate;
res = aacEncoder_SetParam(this->aacenc, AACENC_BITRATE, this->cur_bitrate);
if (res != AACENC_OK)
@@ -395,15 +429,6 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
if (res != AACENC_OK)
goto error;
- // Assume >110 kbit/s as a "high bitrate" CBR and increase the
- // band pass cutout up to 19.3 kHz (as in mode 5 VBR).
- if (!conf->vbr && this->cur_bitrate > 110000) {
- res = aacEncoder_SetParam(this->aacenc, AACENC_BANDWIDTH,
- 19293);
- if (res != AACENC_OK)
- goto error;
- }
-
res = aacEncoder_SetParam(this->aacenc, AACENC_TRANSMUX, TT_MP4_LATM_MCP1);
if (res != AACENC_OK)
goto error;
diff --git a/spa/plugins/bluez5/a2dp-codec-sbc.c b/spa/plugins/bluez5/a2dp-codec-sbc.c
index f1b86e76b..b4ebfc2a2 100644
--- a/spa/plugins/bluez5/a2dp-codec-sbc.c
+++ b/spa/plugins/bluez5/a2dp-codec-sbc.c
@@ -343,27 +343,77 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{
- struct spa_audio_info info;
- struct spa_pod_frame f[1];
- int res;
+ a2dp_sbc_t conf;
+ struct spa_pod_frame f[2];
+ struct spa_pod_choice *choice;
+ uint32_t i = 0;
+ uint32_t position[2];
- if ((res = codec_validate_config(codec, flags, caps, caps_size, &info)) < 0)
- return res;
+ if (caps_size < sizeof(conf))
+ return -EINVAL;
+
+ memcpy(&conf, caps, sizeof(conf));
if (idx > 0)
return 0;
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id);
spa_pod_builder_add(b,
- SPA_FORMAT_mediaType, SPA_POD_Id(info.media_type),
- SPA_FORMAT_mediaSubtype, SPA_POD_Id(info.media_subtype),
- SPA_FORMAT_AUDIO_format, SPA_POD_Id(info.info.raw.format),
- SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info.info.raw.rate),
- SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info.info.raw.channels),
- SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
- SPA_TYPE_Id, info.info.raw.channels, info.info.raw.position),
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
+ SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16),
0);
+ spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0);
+ spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
+ choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
+ i = 0;
+ if (conf.frequency & SBC_SAMPLING_FREQ_48000) {
+ if (i++ == 0)
+ spa_pod_builder_int(b, 48000);
+ spa_pod_builder_int(b, 48000);
+ }
+ if (conf.frequency & SBC_SAMPLING_FREQ_44100) {
+ if (i++ == 0)
+ spa_pod_builder_int(b, 44100);
+ spa_pod_builder_int(b, 44100);
+ }
+ if (conf.frequency & SBC_SAMPLING_FREQ_32000) {
+ if (i++ == 0)
+ spa_pod_builder_int(b, 32000);
+ spa_pod_builder_int(b, 32000);
+ }
+ if (conf.frequency & SBC_SAMPLING_FREQ_16000) {
+ if (i++ == 0)
+ spa_pod_builder_int(b, 16000);
+ spa_pod_builder_int(b, 16000);
+ }
+ if (i > 1)
+ choice->body.type = SPA_CHOICE_Enum;
+ spa_pod_builder_pop(b, &f[1]);
+
+ if (conf.channel_mode & SBC_CHANNEL_MODE_MONO &&
+ conf.channel_mode & (SBC_CHANNEL_MODE_JOINT_STEREO |
+ SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_DUAL_CHANNEL)) {
+ spa_pod_builder_add(b,
+ SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2),
+ 0);
+ } else if (conf.channel_mode & SBC_CHANNEL_MODE_MONO) {
+ position[0] = SPA_AUDIO_CHANNEL_MONO;
+ spa_pod_builder_add(b,
+ SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1),
+ SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
+ SPA_TYPE_Id, 1, position),
+ 0);
+ } else {
+ position[0] = SPA_AUDIO_CHANNEL_FL;
+ position[1] = SPA_AUDIO_CHANNEL_FR;
+ spa_pod_builder_add(b,
+ SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2),
+ SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
+ SPA_TYPE_Id, 2, position),
+ 0);
+ }
*param = spa_pod_builder_pop(b, &f[0]);
return *param == NULL ? -EIO : 1;
}
diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c
index 64a4c25c1..4d14183e7 100644
--- a/spa/plugins/bluez5/backend-native.c
+++ b/spa/plugins/bluez5/backend-native.c
@@ -1969,9 +1969,6 @@ static void hfp_hf_remove_disconnected_calls(struct rfcomm *rfcomm)
struct updated_call *updated_call;
bool found;
- if (!rfcomm->telephony_ag)
- return;
-
spa_list_for_each_safe(call, call_tmp, &rfcomm->telephony_ag->call_list, link) {
found = false;
spa_list_for_each(updated_call, &rfcomm->updated_call_list, link) {
@@ -2100,8 +2097,6 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
if (spa_streq(rfcomm->hf_indicators[indicator], "battchg")) {
spa_bt_device_report_battery_level(rfcomm->device, value * 100 / 5);
- } else if (!rfcomm->telephony_ag) {
- /* noop */
} else if (spa_streq(rfcomm->hf_indicators[indicator], "callsetup")) {
if (rfcomm->hfp_hf_clcc) {
rfcomm_send_cmd(rfcomm, hfp_hf_clcc_update, NULL, "AT+CLCC");
@@ -2250,8 +2245,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
rfcomm->hfp_hf_in_progress = false;
}
}
- } else if (sscanf(token, "+CLIP: \"%16[^\"]\",%u", number, &type) == 2
- && rfcomm->telephony_ag) {
+ } else if (sscanf(token, "+CLIP: \"%16[^\"]\",%u", number, &type) == 2) {
struct spa_bt_telephony_call *call;
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
if (call->state == CALL_STATE_INCOMING && !spa_streq(number, call->line_identification)) {
@@ -2262,8 +2256,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
break;
}
}
- } else if (sscanf(token, "+CCWA: \"%16[^\"]\",%u", number, &type) == 2
- && rfcomm->telephony_ag) {
+ } else if (sscanf(token, "+CCWA: \"%16[^\"]\",%u", number, &type) == 2) {
struct spa_bt_telephony_call *call;
bool found = false;
@@ -2280,7 +2273,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
if (call == NULL)
spa_log_warn(backend->log, "failed to create waiting call");
}
- } else if (spa_strstartswith(token, "+CLCC:") && rfcomm->telephony_ag) {
+ } else if (spa_strstartswith(token, "+CLCC:")) {
struct spa_bt_telephony_call *call;
size_t pos;
char *token_end;
@@ -2428,19 +2421,17 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
}
}
- if (backend->telephony) {
- rfcomm->telephony_ag = telephony_ag_new(backend->telephony, 0);
- rfcomm->telephony_ag->address = strdup(rfcomm->device->address);
- rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_RX] = rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = backend->hfp_default_speaker_volume;
- rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_TX] = rfcomm->volumes[SPA_BT_VOLUME_ID_TX].hw_volume = backend->hfp_default_mic_volume;
- telephony_ag_set_callbacks(rfcomm->telephony_ag,
+ rfcomm->telephony_ag = telephony_ag_new(backend->telephony, 0);
+ rfcomm->telephony_ag->address = strdup(rfcomm->device->address);
+ rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_RX] = rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = backend->hfp_default_speaker_volume;
+ rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_TX] = rfcomm->volumes[SPA_BT_VOLUME_ID_TX].hw_volume = backend->hfp_default_mic_volume;
+ telephony_ag_set_callbacks(rfcomm->telephony_ag,
&telephony_ag_callbacks, rfcomm);
- if (rfcomm->transport) {
- rfcomm->telephony_ag->transport.codec = rfcomm->transport->media_codec->codec_id;
- rfcomm->telephony_ag->transport.state = rfcomm->transport->state;
- }
- telephony_ag_register(rfcomm->telephony_ag);
+ if (rfcomm->transport) {
+ rfcomm->telephony_ag->transport.codec = rfcomm->transport->media_codec->codec_id;
+ rfcomm->telephony_ag->transport.state = rfcomm->transport->state;
}
+ telephony_ag_register(rfcomm->telephony_ag);
rfcomm_send_cmd(rfcomm, hfp_hf_clip, NULL, "AT+CLIP=1");
break;
@@ -2487,7 +2478,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
break;
case hfp_hf_chld1_hangup:
/* For HFP/HF/TWC/BV-03-C - see 0e92ab9307e05758b3f70b4c0648e29c1d1e50be */
- if (!rfcomm->hfp_hf_clcc && rfcomm->telephony_ag) {
+ if (!rfcomm->hfp_hf_clcc) {
struct spa_bt_telephony_call *call, *tcall;
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
if (call->state == CALL_STATE_ACTIVE) {
diff --git a/spa/plugins/bluez5/bap-codec-lc3.c b/spa/plugins/bluez5/bap-codec-lc3.c
index 881af4e14..74761f26b 100644
--- a/spa/plugins/bluez5/bap-codec-lc3.c
+++ b/spa/plugins/bluez5/bap-codec-lc3.c
@@ -126,22 +126,22 @@ static const struct bap_qos bap_qos_configs[] = {
BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 5, 20, 40000, 27, "low-latency"), /* 48_6_1 */
/* BAP v1.0.1 Table 5.2; high-reliability */
- BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 13, 75, 40000, 10, "high-reliability"), /* 8_1_2 */
- BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 13, 95, 40000, 0, "high-reliability"), /* 8_2_2 */
- BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 13, 75, 40000, 11, "high-reliability"), /* 16_1_2 */
- BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 13, 95, 40000, 1, "high-reliability"), /* 16_2_2 */
- BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 13, 75, 40000, 12, "high-reliability"), /* 24_1_2 */
- BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2, "high-reliability"), /* 24_2_2 */
- BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13, "high-reliability"), /* 32_1_2 */
- BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3, "high-reliability"), /* 32_2_2 */
- BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 54, "high-reliability"), /* 441_1_2 */
- BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 44, "high-reliability"), /* 441_2_2 */
- BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 55, "high-reliability"), /* 48_1_2 */
- BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 45, "high-reliability"), /* 48_2_2 */
- BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 56, "high-reliability"), /* 48_3_2 */
- BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 46, "high-reliability"), /* 48_4_2 */
- BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 57, "high-reliability"), /* 48_5_2 */
- BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 47, "high-reliability"), /* 48_6_2 */
+ BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 13, 75, 40000, 10, "high-reliabilty"), /* 8_1_2 */
+ BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 13, 95, 40000, 0, "high-reliabilty"), /* 8_2_2 */
+ BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 13, 75, 40000, 11, "high-reliabilty"), /* 16_1_2 */
+ BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 13, 95, 40000, 1, "high-reliabilty"), /* 16_2_2 */
+ BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 13, 75, 40000, 12, "high-reliabilty"), /* 24_1_2 */
+ BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2, "high-reliabilty"), /* 24_2_2 */
+ BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13, "high-reliabilty"), /* 32_1_2 */
+ BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3, "high-reliabilty"), /* 32_2_2 */
+ BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 54, "high-reliabilty"), /* 441_1_2 */
+ BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 44, "high-reliabilty"), /* 441_2_2 */
+ BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 55, "high-reliabilty"), /* 48_1_2 */
+ BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 45, "high-reliabilty"), /* 48_2_2 */
+ BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 56, "high-reliabilty"), /* 48_3_2 */
+ BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 46, "high-reliabilty"), /* 48_4_2 */
+ BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 57, "high-reliabilty"), /* 48_5_2 */
+ BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 47, "high-reliabilty"), /* 48_6_2 */
};
static const struct bap_qos bap_bcast_qos_configs[] = {
@@ -167,22 +167,22 @@ static const struct bap_qos bap_bcast_qos_configs[] = {
BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 20, 40000, 27, "low-latency"), /* 48_6_1 */
/* BAP v1.0.1 Table 6.4; high-reliability */
- BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 4, 45, 40000, 10, "high-reliability"), /* 8_1_2 */
- BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 4, 60, 40000, 0, "high-reliability"), /* 8_2_2 */
- BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 4, 45, 40000, 11, "high-reliability"), /* 16_1_2 */
- BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 4, 60, 40000, 1, "high-reliability"), /* 16_2_2 */
- BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 4, 45, 40000, 12, "high-reliability"), /* 24_1_2 */
- BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 4, 60, 40000, 2, "high-reliability"), /* 24_2_2 */
- BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 4, 45, 40000, 13, "high-reliability"), /* 32_1_2 */
- BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 4, 60, 40000, 3, "high-reliability"), /* 32_2_2 */
- BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 54, 40000, 14, "high-reliability"), /* 441_1_2 */
- BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 60, 40000, 4, "high-reliability"), /* 441_2_2 */
- BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 50, 40000, 15, "high-reliability"), /* 48_1_2 */
- BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 65, 40000, 5, "high-reliability"), /* 48_2_2 */
- BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 50, 40000, 16, "high-reliability"), /* 48_3_2 */
- BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 65, 40000, 6, "high-reliability"), /* 48_4_2 */
- BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 50, 40000, 17, "high-reliability"), /* 48_5_2 */
- BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 65, 40000, 7, "high-reliability"), /* 48_6_2 */
+ BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 4, 45, 40000, 10, "high-reliabilty"), /* 8_1_2 */
+ BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 4, 60, 40000, 0, "high-reliabilty"), /* 8_2_2 */
+ BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 4, 45, 40000, 11, "high-reliabilty"), /* 16_1_2 */
+ BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 4, 60, 40000, 1, "high-reliabilty"), /* 16_2_2 */
+ BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 4, 45, 40000, 12, "high-reliabilty"), /* 24_1_2 */
+ BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 4, 60, 40000, 2, "high-reliabilty"), /* 24_2_2 */
+ BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 4, 45, 40000, 13, "high-reliabilty"), /* 32_1_2 */
+ BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 4, 60, 40000, 3, "high-reliabilty"), /* 32_2_2 */
+ BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 54, 40000, 14, "high-reliabilty"), /* 441_1_2 */
+ BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 60, 40000, 4, "high-reliabilty"), /* 441_2_2 */
+ BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 50, 40000, 15, "high-reliabilty"), /* 48_1_2 */
+ BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 65, 40000, 5, "high-reliabilty"), /* 48_2_2 */
+ BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 50, 40000, 16, "high-reliabilty"), /* 48_3_2 */
+ BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 65, 40000, 6, "high-reliabilty"), /* 48_4_2 */
+ BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 50, 40000, 17, "high-reliabilty"), /* 48_5_2 */
+ BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 65, 40000, 7, "high-reliabilty"), /* 48_6_2 */
};
static unsigned int get_rate_mask(uint8_t rate) {
@@ -1503,10 +1503,6 @@ static int codec_get_bis_config(const struct media_codec *codec, uint8_t *caps,
struct ltv_writer writer = LTV_WRITER(caps, *caps_size);
const struct bap_qos *preset = NULL;
- uint32_t retransmissions = 0;
- uint8_t rtn_manual_set = 0;
- uint32_t max_transport_latency = 0;
- uint32_t presentation_delay = 0;
*caps_size = 0;
if (settings) {
@@ -1515,14 +1511,6 @@ static int codec_get_bis_config(const struct media_codec *codec, uint8_t *caps,
sscanf(settings->items[i].value, "%"PRIu32, &channel_allocation);
if (spa_streq(settings->items[i].key, "preset"))
preset_name = settings->items[i].value;
- if (spa_streq(settings->items[i].key, "max_transport_latency"))
- spa_atou32(settings->items[i].value, &max_transport_latency, 0);
- if (spa_streq(settings->items[i].key, "presentation_delay"))
- spa_atou32(settings->items[i].value, &presentation_delay, 0);
- if (spa_streq(settings->items[i].key, "retransmissions")) {
- spa_atou32(settings->items[i].value, &retransmissions, 0);
- rtn_manual_set = 1;
- }
}
}
@@ -1549,9 +1537,9 @@ static int codec_get_bis_config(const struct media_codec *codec, uint8_t *caps,
else
qos->framing = 0;
qos->sdu = preset->framelen * get_channel_count(channel_allocation);
- qos->retransmission = rtn_manual_set ? retransmissions : preset->retransmission;
- qos->latency = max_transport_latency ? max_transport_latency : preset->latency;
- qos->delay = presentation_delay ? presentation_delay : preset->delay;
+ qos->retransmission = preset->retransmission;
+ qos->latency = preset->latency;
+ qos->delay = preset->delay;
qos->phy = 2;
qos->interval = (preset->frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000);
diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c
index 22d971c37..7dfe45911 100644
--- a/spa/plugins/bluez5/bluez5-dbus.c
+++ b/spa/plugins/bluez5/bluez5-dbus.c
@@ -188,16 +188,9 @@ struct spa_bt_metadata {
uint8_t value[METADATA_MAX_LEN - 1];
};
-#define RTN_MAX 0x1E
-#define MAX_TRANSPORT_LATENCY_MIN 0x5
-#define MAX_TRANSPORT_LATENCY_MAX 0x0FA0
-
struct spa_bt_bis {
struct spa_list link;
char qos_preset[255];
- int retransmissions;
- int rtn_manual_set;
- int max_transport_latency;
int channel_allocation;
struct spa_list metadata_list;
};
@@ -594,35 +587,18 @@ static enum spa_bt_profile get_codec_profile(const struct media_codec *codec,
{
switch (direction) {
case SPA_BT_MEDIA_SOURCE:
- if (codec->kind == MEDIA_CODEC_A2DP)
- return SPA_BT_PROFILE_A2DP_SOURCE;
- else if (codec->kind == MEDIA_CODEC_BAP)
- return SPA_BT_PROFILE_BAP_SOURCE;
- else if (codec->kind == MEDIA_CODEC_HFP)
- return SPA_BT_PROFILE_HEADSET_AUDIO;
- else
- return SPA_BT_PROFILE_NULL;
+ return codec->kind == MEDIA_CODEC_BAP ? SPA_BT_PROFILE_BAP_SOURCE : SPA_BT_PROFILE_A2DP_SOURCE;
case SPA_BT_MEDIA_SINK:
- if (codec->kind == MEDIA_CODEC_A2DP)
- return SPA_BT_PROFILE_A2DP_SINK;
- else if (codec->kind == MEDIA_CODEC_ASHA)
+ if (codec->kind == MEDIA_CODEC_ASHA)
return SPA_BT_PROFILE_ASHA_SINK;
else if (codec->kind == MEDIA_CODEC_BAP)
return SPA_BT_PROFILE_BAP_SINK;
- else if (codec->kind == MEDIA_CODEC_HFP)
- return SPA_BT_PROFILE_HEADSET_AUDIO;
else
- return SPA_BT_PROFILE_NULL;
+ return SPA_BT_PROFILE_A2DP_SINK;
case SPA_BT_MEDIA_SOURCE_BROADCAST:
- if (codec->kind == MEDIA_CODEC_BAP)
- return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
- else
- return SPA_BT_PROFILE_NULL;
+ return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
case SPA_BT_MEDIA_SINK_BROADCAST:
- if (codec->kind == MEDIA_CODEC_BAP)
- return SPA_BT_PROFILE_BAP_BROADCAST_SINK;
- else
- return SPA_BT_PROFILE_NULL;
+ return SPA_BT_PROFILE_BAP_BROADCAST_SINK;
default:
spa_assert_not_reached();
}
@@ -2801,18 +2777,16 @@ bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const stru
bool is_bap = codec->kind == MEDIA_CODEC_BAP;
size_t i;
- if (codec->kind == MEDIA_CODEC_HFP) {
- if (!(profile & SPA_BT_PROFILE_HEADSET_AUDIO))
- return false;
- if (!is_media_codec_enabled(monitor, codec))
- return false;
- return spa_bt_backend_supports_codec(monitor->backend, device, codec->codec_id) == 1;
- }
-
codec_target_profile = get_codec_target_profile(monitor, codec);
if (!codec_target_profile)
return false;
+ if (codec->kind == MEDIA_CODEC_HFP) {
+ if (!(profile & SPA_BT_PROFILE_HEADSET_AUDIO))
+ return false;
+ return spa_bt_backend_supports_codec(monitor->backend, device, codec->codec_id) == 1;
+ }
+
if (!device->adapter->a2dp_application_registered && is_a2dp) {
/* Codec switching not supported: only plain SBC allowed */
return (codec->codec_id == A2DP_CODEC_SBC && spa_streq(codec->name, "sbc") &&
@@ -6199,11 +6173,8 @@ static void configure_bis(struct spa_bt_monitor *monitor,
struct bap_codec_qos qos;
struct spa_bt_metadata *metadata_entry;
struct spa_dict settings;
- struct spa_dict_item setting_items[4];
- uint32_t n_items = 0;
+ struct spa_dict_item setting_items[2];
char channel_allocation[64] = {0};
- char retransmissions[3] = {0};
- char max_transport_latency[5] = {0};
int mse = 0;
int options = 0;
@@ -6228,27 +6199,12 @@ static void configure_bis(struct spa_bt_monitor *monitor,
metadata_size += metadata_entry->length - 1;
}
-
spa_log_debug(monitor->log, "bis->channel_allocation %d", bis->channel_allocation);
- if (bis->channel_allocation) {
+ if (bis->channel_allocation)
spa_scnprintf(channel_allocation, sizeof(channel_allocation), "%"PRIu32, bis->channel_allocation);
- }
- spa_log_debug(monitor->log, "bis->rtn_manual_set %d", bis->rtn_manual_set);
- spa_log_debug(monitor->log, "bis->retransmissions %d", bis->retransmissions);
- if (bis->rtn_manual_set) {
- spa_scnprintf(retransmissions, sizeof(retransmissions), "%"PRIu8, bis->retransmissions);
- setting_items[n_items++] = SPA_DICT_ITEM_INIT("retransmissions", retransmissions);
- }
- spa_log_debug(monitor->log, "bis->max_transport_latency %d", bis->max_transport_latency);
- if (bis->max_transport_latency) {
- spa_scnprintf(max_transport_latency, sizeof(max_transport_latency), "%"PRIu32, bis->max_transport_latency);
- setting_items[n_items++] = SPA_DICT_ITEM_INIT("max_transport_latency", max_transport_latency);
- }
-
- setting_items[n_items++] = SPA_DICT_ITEM_INIT("preset", bis->qos_preset);
- setting_items[n_items++] = SPA_DICT_ITEM_INIT("channel_allocation", channel_allocation);
-
- settings = SPA_DICT_INIT(setting_items, n_items);
+ setting_items[0] = SPA_DICT_ITEM_INIT("channel_allocation", channel_allocation);
+ setting_items[1] = SPA_DICT_ITEM_INIT("preset", bis->qos_preset);
+ settings = SPA_DICT_INIT(setting_items, 2);
caps_size = sizeof(caps);
ret = codec->get_bis_config(codec, caps, &caps_size, &settings, &qos);
@@ -7122,7 +7078,7 @@ static void parse_broadcast_source_config(struct spa_bt_monitor *monitor, const
memcpy(big_entry->broadcast_code, bcode, strlen(bcode));
spa_log_debug(monitor->log, "big_entry->broadcast_code %s", big_entry->broadcast_code);
} else if (spa_streq(key, "adapter")) {
- if (spa_json_get_string(&it[0], big_entry->adapter, sizeof(big_entry->adapter)) <= 0)
+ if (spa_json_get_string(&it[1], big_entry->adapter, sizeof(big_entry->adapter)) <= 0)
goto parse_failed;
spa_log_debug(monitor->log, "big_entry->adapter %s", big_entry->adapter);
} else if (spa_streq(key, "encryption")) {
@@ -7151,20 +7107,6 @@ static void parse_broadcast_source_config(struct spa_bt_monitor *monitor, const
if (spa_json_get_string(&it[1], bis_entry->qos_preset, sizeof(bis_entry->qos_preset)) <= 0)
goto parse_failed;
spa_log_debug(monitor->log, "bis_entry->qos_preset %s", bis_entry->qos_preset);
- } else if (spa_streq(bis_key, "retransmissions")) {
- if (spa_json_get_int(&it[2], &bis_entry->retransmissions) <= 0)
- goto parse_failed;
- if (bis_entry->retransmissions > RTN_MAX)
- goto parse_failed;
- bis_entry->rtn_manual_set = 1;
- spa_log_debug(monitor->log, "bis_entry->retransmissions %d", bis_entry->retransmissions);
- } else if (spa_streq(bis_key, "max_transport_latency")) {
- if (spa_json_get_int(&it[2], &bis_entry->max_transport_latency) <= 0)
- goto parse_failed;
- if (bis_entry->max_transport_latency < MAX_TRANSPORT_LATENCY_MIN &&
- bis_entry->max_transport_latency > MAX_TRANSPORT_LATENCY_MAX)
- goto parse_failed;
- spa_log_debug(monitor->log, "bis_entry->max_transport_latency %d", bis_entry->max_transport_latency);
} else if (spa_streq(bis_key, "audio_channel_allocation")) {
if (spa_json_get_int(&it[1], &bis_entry->channel_allocation) <= 0)
goto parse_failed;
diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c
index 897241954..fc659f655 100644
--- a/spa/plugins/bluez5/bluez5-device.c
+++ b/spa/plugins/bluez5/bluez5-device.c
@@ -59,8 +59,6 @@ enum device_profile {
DEVICE_PROFILE_OFF = 0,
DEVICE_PROFILE_AG,
DEVICE_PROFILE_A2DP,
- DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY,
- DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY,
DEVICE_PROFILE_HSP_HFP,
DEVICE_PROFILE_BAP,
DEVICE_PROFILE_BAP_SINK,
@@ -69,12 +67,6 @@ enum device_profile {
DEVICE_PROFILE_LAST,
};
-enum codec_order {
- CODEC_ORDER_NONE = 0,
- CODEC_ORDER_QUALITY,
- CODEC_ORDER_LATENCY,
-};
-
enum {
ROUTE_INPUT = 0,
ROUTE_OUTPUT,
@@ -212,89 +204,9 @@ static bool profile_is_bap(enum device_profile profile)
return false;
}
-static bool profile_is_a2dp(enum device_profile profile)
-{
- switch (profile) {
- case DEVICE_PROFILE_A2DP:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
- return true;
- default:
- break;
- }
- return false;
-}
-
-static size_t get_media_codec_quality_priority (const struct media_codec *mc)
-{
- /* From lowest quality to highest quality */
- static const enum spa_bluetooth_audio_codec quality_priorities[] = {
- SPA_BLUETOOTH_AUDIO_CODEC_START,
- SPA_BLUETOOTH_AUDIO_CODEC_SBC,
- SPA_BLUETOOTH_AUDIO_CODEC_APTX,
- SPA_BLUETOOTH_AUDIO_CODEC_AAC,
- SPA_BLUETOOTH_AUDIO_CODEC_OPUS_G,
- SPA_BLUETOOTH_AUDIO_CODEC_LC3PLUS_HR,
- SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD,
- SPA_BLUETOOTH_AUDIO_CODEC_LDAC,
- };
- size_t i;
-
- for (i = 0; i < SPA_N_ELEMENTS(quality_priorities); ++i) {
- if (quality_priorities[i] == mc->id)
- return i;
- }
-
- return 0;
-}
-
-static size_t get_media_codec_latency_priority (const struct media_codec *mc)
-{
- /* From highest latency to lowest latency */
- static const enum spa_bluetooth_audio_codec latency_priorities[] = {
- SPA_BLUETOOTH_AUDIO_CODEC_START,
- SPA_BLUETOOTH_AUDIO_CODEC_SBC,
- SPA_BLUETOOTH_AUDIO_CODEC_APTX,
- SPA_BLUETOOTH_AUDIO_CODEC_OPUS_G,
- SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM_DUPLEX,
- SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM,
- SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX,
- SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL,
- };
- size_t i;
-
- for (i = 0; i < SPA_N_ELEMENTS(latency_priorities); ++i) {
- if (latency_priorities[i] == mc->id)
- return i;
- }
-
- return 0;
-}
-
-static int media_codec_quality_cmp(const void *a, const void *b) {
- const struct media_codec *ca = *(const struct media_codec **)a;
- const struct media_codec *cb = *(const struct media_codec **)b;
- size_t ca_prio = get_media_codec_quality_priority (ca);
- size_t cb_prio = get_media_codec_quality_priority (cb);
- if (ca_prio > cb_prio) return -1;
- if (ca_prio < cb_prio) return 1;
- return 0;
-}
-
-static int media_codec_latency_cmp(const void *a, const void *b) {
- const struct media_codec *ca = *(const struct media_codec **)a;
- const struct media_codec *cb = *(const struct media_codec **)b;
- size_t ca_prio = get_media_codec_latency_priority (ca);
- size_t cb_prio = get_media_codec_latency_priority (cb);
- if (ca_prio > cb_prio) return -1;
- if (ca_prio < cb_prio) return 1;
- return 0;
-}
-
-static void get_media_codecs(struct impl *this, enum codec_order order, enum spa_bluetooth_audio_codec id, const struct media_codec **codecs, size_t size)
+static void get_media_codecs(struct impl *this, enum spa_bluetooth_audio_codec id, const struct media_codec **codecs, size_t size)
{
const struct media_codec * const *c;
- size_t n = 0;
spa_assert(size > 0);
spa_assert(this->supported_codecs);
@@ -304,24 +216,12 @@ static void get_media_codecs(struct impl *this, enum codec_order order, enum spa
continue;
if ((*c)->id == id || id == 0) {
- codecs[n++] = *c;
+ *codecs++ = *c;
--size;
}
}
- codecs[n] = NULL;
-
- switch (order) {
- case CODEC_ORDER_QUALITY:
- qsort(codecs, n, sizeof(struct media_codec *), media_codec_quality_cmp);
- break;
- case CODEC_ORDER_LATENCY:
- qsort(codecs, n, sizeof(struct media_codec *), media_codec_latency_cmp);
- break;
- case CODEC_ORDER_NONE:
- default:
- break;
- }
+ *codecs = NULL;
}
static const struct media_codec *get_supported_media_codec(struct impl *this, enum spa_bluetooth_audio_codec id,
@@ -480,8 +380,6 @@ static bool node_update_volume_from_transport(struct node *node, bool reset)
/* PW is the controller for remote device. */
if (impl->profile != DEVICE_PROFILE_A2DP
- && impl->profile != DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY
- && impl->profile != DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY
&& impl->profile != DEVICE_PROFILE_BAP
&& impl->profile != DEVICE_PROFILE_BAP_SINK
&& impl->profile != DEVICE_PROFILE_BAP_SOURCE
@@ -1364,8 +1262,6 @@ static int emit_nodes(struct impl *this)
}
break;
case DEVICE_PROFILE_A2DP:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE) {
t = find_transport(this, SPA_BT_PROFILE_A2DP_SOURCE);
if (t) {
@@ -1568,13 +1464,13 @@ static int set_profile(struct impl *this, uint32_t profile, enum spa_bluetooth_a
* XXX: source-only case, as it will only switch the sink, and we only
* XXX: list the sink codecs here. TODO: fix this
*/
- if ((profile_is_a2dp (profile) || (profile_is_bap(profile) && is_bap_client(this)))
+ if ((profile == DEVICE_PROFILE_A2DP || (profile_is_bap(profile) && is_bap_client(this)))
&& !(this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE)) {
int ret;
const struct media_codec *codecs[64];
uint32_t profiles;
- get_media_codecs(this, CODEC_ORDER_NONE, codec, codecs, SPA_N_ELEMENTS(codecs));
+ get_media_codecs(this, codec, codecs, SPA_N_ELEMENTS(codecs));
this->switching_codec = true;
@@ -1591,14 +1487,6 @@ static int set_profile(struct impl *this, uint32_t profile, enum spa_bluetooth_a
case DEVICE_PROFILE_A2DP:
profiles = this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_DUPLEX;
break;
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
- get_media_codecs(this, CODEC_ORDER_QUALITY, 0, codecs, SPA_N_ELEMENTS(codecs));
- profiles = this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_DUPLEX;
- break;
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
- get_media_codecs(this, CODEC_ORDER_LATENCY, 0, codecs, SPA_N_ELEMENTS(codecs));
- profiles = this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_DUPLEX;
- break;
default:
profiles = 0;
break;
@@ -1758,8 +1646,6 @@ static void profiles_changed(void *userdata, uint32_t connected_change)
nodes_changed);
break;
case DEVICE_PROFILE_A2DP:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
nodes_changed = (connected_change & SPA_BT_PROFILE_A2DP_DUPLEX);
spa_log_debug(this->log, "profiles changed: A2DP nodes changed: %d",
nodes_changed);
@@ -1915,8 +1801,6 @@ static uint32_t profile_direction_mask(struct impl *this, uint32_t index, enum s
switch (index) {
case DEVICE_PROFILE_A2DP:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK)
have_output = true;
@@ -1966,8 +1850,6 @@ static uint32_t get_profile_from_index(struct impl *this, uint32_t index, uint32
switch (profile) {
case DEVICE_PROFILE_OFF:
case DEVICE_PROFILE_AG:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
*codec = 0;
*next = (profile + 1) << 16;
return profile;
@@ -2002,8 +1884,6 @@ static uint32_t get_index_from_profile(struct impl *this, uint32_t profile, enum
switch (profile) {
case DEVICE_PROFILE_OFF:
case DEVICE_PROFILE_AG:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
return (profile << 16);
case DEVICE_PROFILE_ASHA:
@@ -2097,37 +1977,15 @@ static void set_initial_profile(struct impl *this)
t = find_transport(this, i);
if (t) {
- if (i == SPA_BT_PROFILE_A2DP_SOURCE || i == SPA_BT_PROFILE_BAP_SOURCE) {
+ if (i == SPA_BT_PROFILE_A2DP_SOURCE || i == SPA_BT_PROFILE_BAP_SOURCE)
this->profile = DEVICE_PROFILE_AG;
- this->props.codec = t->media_codec->id;
- } else if (i == SPA_BT_PROFILE_BAP_SINK) {
+ else if (i == SPA_BT_PROFILE_BAP_SINK)
this->profile = DEVICE_PROFILE_BAP;
- this->props.codec = t->media_codec->id;
- } else if (i == SPA_BT_PROFILE_ASHA_SINK) {
+ else if (i == SPA_BT_PROFILE_ASHA_SINK)
this->profile = DEVICE_PROFILE_ASHA;
- this->props.codec = t->media_codec->id;
- } else {
- const struct media_codec *codecs[64];
- const struct media_codec *quality_codec = NULL;
- int j;
-
- get_media_codecs(this, CODEC_ORDER_QUALITY, 0, codecs, SPA_N_ELEMENTS(codecs));
- for (j = 0; codecs[j] != NULL; ++j) {
- if (codecs[j]->kind == MEDIA_CODEC_A2DP) {
- quality_codec = codecs[j];
- break;
- }
- }
-
- if (quality_codec) {
- this->profile = DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY;
- this->props.codec = quality_codec->id;
- } else {
- this->profile = DEVICE_PROFILE_A2DP;
- this->props.codec = t->media_codec->id;
- }
- }
-
+ else
+ this->profile = DEVICE_PROFILE_A2DP;
+ this->props.codec = t->media_codec->id;
spa_log_debug(this->log, "initial profile media profile:%d codec:%d",
this->profile, this->props.codec);
return;
@@ -2265,36 +2123,6 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
n_source++;
break;
}
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
- {
- uint32_t profile;
-
- /* make this device profile visible only if there is an A2DP sink */
- profile = device->connected_profiles & (SPA_BT_PROFILE_A2DP_SINK | SPA_BT_PROFILE_A2DP_SOURCE);
- if (!(profile & SPA_BT_PROFILE_A2DP_SINK))
- return NULL;
-
- switch (profile_index) {
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
- name = "a2dp-auto-prefer-quality";
- desc = _("Auto: Prefer Quality (A2DP)");
- priority = 255;
- break;
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
- name = "a2dp-auto-prefer-latency";
- desc = _("Auto: Prefer Latency (A2DP)");
- priority = 254;
- break;
- default:
- return NULL;
- }
-
- n_sink++;
- if (this->autoswitch_routes && (device->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT))
- n_source++;
- break;
- }
case DEVICE_PROFILE_BAP_SINK:
case DEVICE_PROFILE_BAP_SOURCE:
/* These are client-only */
@@ -2496,8 +2324,6 @@ static bool profile_has_route(uint32_t profile, uint32_t route)
case DEVICE_PROFILE_AG:
break;
case DEVICE_PROFILE_A2DP:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
- case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
switch (route) {
case ROUTE_INPUT:
case ROUTE_OUTPUT:
@@ -2797,7 +2623,7 @@ static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b,
spa_pod_builder_array(b, sizeof(uint32_t), SPA_TYPE_Id,
node->n_channels, node->channels);
- if ((profile_is_a2dp (this->profile) || profile_is_bap(this->profile)) &&
+ if ((this->profile == DEVICE_PROFILE_A2DP || profile_is_bap(this->profile)) &&
(dev & SINK_ID_FLAG)) {
spa_pod_builder_prop(b, SPA_PROP_latencyOffsetNsec, 0);
spa_pod_builder_long(b, node->latency_offset);
@@ -2833,7 +2659,7 @@ next:
c = this->supported_codecs[*j];
- if (!(profile_is_a2dp (this->profile) && c->kind == MEDIA_CODEC_A2DP) &&
+ if (!(this->profile == DEVICE_PROFILE_A2DP && c->kind == MEDIA_CODEC_A2DP) &&
!(profile_is_bap(this->profile) && c->kind == MEDIA_CODEC_BAP) &&
!(this->profile == DEVICE_PROFILE_HSP_HFP && c->kind == MEDIA_CODEC_HFP) &&
!(this->profile == DEVICE_PROFILE_ASHA && c->kind == MEDIA_CODEC_ASHA))
@@ -3414,7 +3240,7 @@ static int impl_set_param(void *object,
return 0;
}
- if (profile_is_a2dp (this->profile) || profile_is_bap(this->profile) ||
+ if (this->profile == DEVICE_PROFILE_A2DP || profile_is_bap(this->profile) ||
this->profile == DEVICE_PROFILE_ASHA || this->profile == DEVICE_PROFILE_HSP_HFP) {
size_t j;
for (j = 0; j < this->supported_codec_count; ++j) {
diff --git a/spa/plugins/bluez5/iso-io.c b/spa/plugins/bluez5/iso-io.c
index ce1fd7d0c..2cc65a2bf 100644
--- a/spa/plugins/bluez5/iso-io.c
+++ b/spa/plugins/bluez5/iso-io.c
@@ -411,7 +411,7 @@ static void group_on_timeout(struct spa_source *source)
/* Ensure controller fill level */
fill_count = UINT_MAX;
spa_list_for_each(stream, &group->streams, link) {
- if (!stream->sink || !group->started || !stream->tx_latency.enabled)
+ if (!stream->sink || !group->started)
continue;
if (stream->tx_latency.queue < MIN_FILL)
fill_count = SPA_MIN(fill_count, MIN_FILL - stream->tx_latency.queue);
diff --git a/spa/plugins/bluez5/media-source.c b/spa/plugins/bluez5/media-source.c
index e1c01d90c..da2e57b5a 100644
--- a/spa/plugins/bluez5/media-source.c
+++ b/spa/plugins/bluez5/media-source.c
@@ -1784,7 +1784,7 @@ static uint32_t get_samples(struct impl *this, int64_t *duration_ns)
static void update_target_latency(struct impl *this)
{
struct port *port = &this->port;
- int32_t target = 0;
+ int32_t target;
int samples;
if (this->transport == NULL || !port->have_format)
@@ -1803,7 +1803,7 @@ static void update_target_latency(struct impl *this)
*/
if (this->decode_buffer_target)
target = this->decode_buffer_target;
- else if (this->transport->iso_io)
+ else
target = spa_bt_iso_io_get_source_target_latency(this->transport->iso_io);
spa_bt_decode_buffer_set_target_latency(&port->buffer, target);
diff --git a/spa/plugins/bluez5/midi-node.c b/spa/plugins/bluez5/midi-node.c
index 671035b34..7146d6f8a 100644
--- a/spa/plugins/bluez5/midi-node.c
+++ b/spa/plugins/bluez5/midi-node.c
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
@@ -449,7 +450,7 @@ static void midi_event_recv(void *user_data, uint16_t timestamp, uint8_t *data,
struct impl *this = user_data;
struct port *port = &this->ports[PORT_OUT];
struct time_sync *sync = &port->sync;
- uint64_t time;
+ uint64_t time, state = 0;
int res;
spa_assert(size > 0);
@@ -459,11 +460,19 @@ static void midi_event_recv(void *user_data, uint16_t timestamp, uint8_t *data,
spa_log_trace(this->log, "%p: event:0x%x size:%d timestamp:%d time:%"PRIu64"",
this, (int)data[0], (int)size, (int)timestamp, (uint64_t)time);
- res = midi_event_ringbuffer_push(&this->event_rbuf, time, data, size);
- if (res < 0) {
- midi_event_ringbuffer_init(&this->event_rbuf);
- spa_log_warn(this->log, "%p: MIDI receive buffer overflow: %s",
- this, spa_strerror(res));
+ while (size > 0) {
+ uint32_t ump[4];
+ int ump_size = spa_ump_from_midi(&data, &size,
+ ump, sizeof(ump), 0, &state);
+ if (ump_size <= 0)
+ break;
+
+ res = midi_event_ringbuffer_push(&this->event_rbuf, time, (uint8_t*)ump, ump_size);
+ if (res < 0) {
+ midi_event_ringbuffer_init(&this->event_rbuf);
+ spa_log_warn(this->log, "%p: MIDI receive buffer overflow: %s",
+ this, spa_strerror(res));
+ }
}
}
@@ -704,7 +713,7 @@ static int process_output(struct impl *this)
offset = time * this->rate / SPA_NSEC_PER_SEC;
offset = SPA_CLAMP(offset, 0u, this->duration - 1);
- spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_Midi);
+ spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_UMP);
buf = spa_pod_builder_reserve_bytes(&port->builder, size);
if (buf) {
midi_event_ringbuffer_pop(&this->event_rbuf, buf, size);
@@ -777,28 +786,37 @@ static int write_data(struct impl *this, struct spa_data *d)
time = 0;
while (spa_pod_parser_get_control_body(&parser, &c, &c_body) >= 0) {
- const uint8_t *event = c_body;
- uint32_t size = c.value.size;
+ int size;
+ uint8_t event[32];
+ const uint32_t *ump = c_body;
+ size_t ump_size = c.value.size;
+ uint64_t state = 0;
- if (c.type != SPA_CONTROL_Midi)
+ if (c.type != SPA_CONTROL_UMP)
continue;
time = SPA_MAX(time, this->current_time + c.offset * SPA_NSEC_PER_SEC / this->rate);
- spa_log_trace(this->log, "%p: output event:0x%x time:%"PRIu64, this,
- (size > 0) ? event[0] : 0, time);
+ while (ump_size > 0) {
+ size = spa_ump_to_midi(&ump, &ump_size, event, sizeof(event), &state);
+ if (size <= 0)
+ break;
- do {
- res = spa_bt_midi_writer_write(&this->writer,
- time, event, size);
- if (res < 0) {
- return res;
- } else if (res) {
- int res2;
- if ((res2 = flush_packet(this)) < 0)
- return res2;
- }
- } while (res);
+ spa_log_trace(this->log, "%p: output event:0x%x time:%"PRIu64, this,
+ (size > 0) ? event[0] : 0, time);
+
+ do {
+ res = spa_bt_midi_writer_write(&this->writer,
+ time, event, size);
+ if (res < 0) {
+ return res;
+ } else if (res) {
+ int res2;
+ if ((res2 = flush_packet(this)) < 0)
+ return res2;
+ }
+ } while (res);
+ }
}
if ((res = flush_packet(this)) < 0)
diff --git a/spa/plugins/bluez5/rate-control.h b/spa/plugins/bluez5/rate-control.h
index 6837c2fa5..d1a36f193 100644
--- a/spa/plugins/bluez5/rate-control.h
+++ b/spa/plugins/bluez5/rate-control.h
@@ -128,7 +128,7 @@ static inline bool spa_bt_ptp_valid(struct spa_bt_ptp *p)
* in 1/z expansion. This guarantees f(z) is causal, and G(z) = (z-1) q(z) / p(z).
* We can choose p(z) and q(z) to improve low-pass properties of F(z).
*
- * Simplest choice is p(z)=(z-1)^2 and q(z)=1, but that does not suppress
+ * Simplest choice is p(z)=(z-1)^2 and q(z)=1, but that does not supress
* high frequency response in F(z). Better choice is p(z) = (z-u)*(z-v)*(z-w)
* and q(z) = z - r. Causality requires r = u + v + w - 2.
* Then,
diff --git a/spa/plugins/control/mixer.c b/spa/plugins/control/mixer.c
index 1b106d3f7..caf3a6b06 100644
--- a/spa/plugins/control/mixer.c
+++ b/spa/plugins/control/mixer.c
@@ -72,7 +72,6 @@ struct impl {
struct spa_node node;
uint32_t quantum_limit;
- uint32_t control_types;
struct spa_log *log;
@@ -474,9 +473,9 @@ static int port_set_format(void *object,
if (!port->have_format) {
this->n_formats++;
port->have_format = true;
- port->types = types == 0 ? this->control_types : types;
- spa_log_debug(this->log, "%p: set format on port %d:%d types:%08x %08x",
- this, direction, port_id, port->types, this->control_types);
+ port->types = types;
+ spa_log_debug(this->log, "%p: set format on port %d:%d",
+ this, direction, port_id);
}
}
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
@@ -590,7 +589,7 @@ static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
port->io[0] = info->data;
port->io[1] = info->data;
}
- if (port->direction == SPA_DIRECTION_INPUT && !port->active) {
+ if (!port->active) {
spa_list_append(&info->impl->mix_list, &port->mix_link);
port->active = true;
}
@@ -956,8 +955,6 @@ impl_init(const struct spa_handle_factory *factory,
}
this->quantum_limit = 8192;
- /* by default we convert to midi1 */
- this->control_types = 1u<n_items; i++) {
const char *k = info->items[i].key;
@@ -965,14 +962,6 @@ impl_init(const struct spa_handle_factory *factory,
if (spa_streq(k, "clock.quantum-limit")) {
spa_atou32(s, &this->quantum_limit, 0);
}
- else if (spa_streq(k, "control.ump")) {
- if (spa_atob(s))
- /* we convert to UMP when forced */
- this->control_types = 1u<control_types = 0;
- }
}
spa_hook_list_init(&this->hooks);
diff --git a/spa/plugins/filter-graph/audio-dsp-avx2.c b/spa/plugins/filter-graph/audio-dsp-avx2.c
index 346b26ab3..76c7b17d5 100644
--- a/spa/plugins/filter-graph/audio-dsp-avx2.c
+++ b/spa/plugins/filter-graph/audio-dsp-avx2.c
@@ -140,10 +140,10 @@ static void dsp_add_n_gain_avx2(void *obj, float *dst,
for (i = 1; i < n_src; i++) {
g = _mm256_set1_ps(gain[i]);
- in[0] = _mm256_fmadd_ps(g, _mm256_load_ps(&s[i][n+ 0]), in[0]);
- in[1] = _mm256_fmadd_ps(g, _mm256_load_ps(&s[i][n+ 8]), in[1]);
- in[2] = _mm256_fmadd_ps(g, _mm256_load_ps(&s[i][n+16]), in[2]);
- in[3] = _mm256_fmadd_ps(g, _mm256_load_ps(&s[i][n+24]), in[3]);
+ in[0] = _mm256_add_ps(in[0], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+ 0])));
+ in[1] = _mm256_add_ps(in[1], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+ 8])));
+ in[2] = _mm256_add_ps(in[2], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+16])));
+ in[3] = _mm256_add_ps(in[3], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+24])));
}
_mm256_store_ps(&d[n+ 0], in[0]);
_mm256_store_ps(&d[n+ 8], in[1]);
@@ -237,12 +237,13 @@ void dsp_sum_avx2(void *obj, float *r, const float *a, const float *b, uint32_t
inline static __m256 _mm256_mul_pz(__m256 ab, __m256 cd)
{
- __m256 aa, bb, dc, x1;
+ __m256 aa, bb, dc, x0, x1;
aa = _mm256_moveldup_ps(ab);
bb = _mm256_movehdup_ps(ab);
+ x0 = _mm256_mul_ps(aa, cd);
dc = _mm256_shuffle_ps(cd, cd, _MM_SHUFFLE(2,3,0,1));
x1 = _mm256_mul_ps(bb, dc);
- return _mm256_fmaddsub_ps(aa, cd, x1);
+ return _mm256_addsub_ps(x0, x1);
}
void dsp_fft_cmul_avx2(void *obj, void *fft,
@@ -307,10 +308,12 @@ void dsp_fft_cmuladd_avx2(void *obj, void *fft,
bb[1] = _mm256_load_ps(&b[2*i+8]); /* br2 bi2 br3 bi3 */
dd[0] = _mm256_mul_pz(aa[0], bb[0]);
dd[1] = _mm256_mul_pz(aa[1], bb[1]);
+ dd[0] = _mm256_mul_ps(dd[0], s);
+ dd[1] = _mm256_mul_ps(dd[1], s);
t[0] = _mm256_load_ps(&src[2*i]);
t[1] = _mm256_load_ps(&src[2*i+8]);
- t[0] = _mm256_fmadd_ps(dd[0], s, t[0]);
- t[1] = _mm256_fmadd_ps(dd[1], s, t[1]);
+ t[0] = _mm256_add_ps(t[0], dd[0]);
+ t[1] = _mm256_add_ps(t[1], dd[1]);
_mm256_store_ps(&dst[2*i], t[0]);
_mm256_store_ps(&dst[2*i+8], t[1]);
}
diff --git a/spa/plugins/filter-graph/audio-dsp.c b/spa/plugins/filter-graph/audio-dsp.c
index d0c4ef008..133b53db5 100644
--- a/spa/plugins/filter-graph/audio-dsp.c
+++ b/spa/plugins/filter-graph/audio-dsp.c
@@ -24,7 +24,7 @@ struct dsp_info {
static const struct dsp_info dsp_table[] =
{
#if defined (HAVE_AVX2)
- { SPA_CPU_FLAG_AVX2 | SPA_CPU_FLAG_FMA3,
+ { SPA_CPU_FLAG_AVX2,
.funcs.clear = dsp_clear_c,
.funcs.copy = dsp_copy_c,
.funcs.mix_gain = dsp_mix_gain_avx2,
diff --git a/spa/plugins/filter-graph/audio-plugin.h b/spa/plugins/filter-graph/audio-plugin.h
index fffdc0de5..9f9d1bce6 100644
--- a/spa/plugins/filter-graph/audio-plugin.h
+++ b/spa/plugins/filter-graph/audio-plugin.h
@@ -69,7 +69,6 @@ struct spa_fga_descriptor {
void (*connect_port) (void *instance, unsigned long port, void *data);
void (*control_changed) (void *instance);
- void (*control_sync) (void *instance);
void (*activate) (void *instance);
void (*deactivate) (void *instance);
diff --git a/spa/plugins/filter-graph/convolver.c b/spa/plugins/filter-graph/convolver.c
index 26ea5fa33..a077c6ec1 100644
--- a/spa/plugins/filter-graph/convolver.c
+++ b/spa/plugins/filter-graph/convolver.c
@@ -10,7 +10,7 @@
#include
-struct partition {
+struct convolver1 {
int blockSize;
int segSize;
int segCount;
@@ -27,6 +27,7 @@ struct partition {
float *pre_mult;
float *conv;
+ float *inputBuffer;
int inputBufferFill;
int current;
@@ -41,46 +42,48 @@ static int next_power_of_two(int val)
return r;
}
-static void partition_reset(struct spa_fga_dsp *dsp, struct partition *part)
+static void convolver1_reset(struct spa_fga_dsp *dsp, struct convolver1 *conv)
{
int i;
- for (i = 0; i < part->segCount; i++)
- spa_fga_dsp_fft_memclear(dsp, part->segments[i], part->fftComplexSize, false);
- spa_fga_dsp_fft_memclear(dsp, part->fft_buffer[0], part->segSize, true);
- spa_fga_dsp_fft_memclear(dsp, part->fft_buffer[1], part->segSize, true);
- spa_fga_dsp_fft_memclear(dsp, part->pre_mult, part->fftComplexSize, false);
- spa_fga_dsp_fft_memclear(dsp, part->conv, part->fftComplexSize, false);
- part->inputBufferFill = 0;
- part->current = 0;
+ for (i = 0; i < conv->segCount; i++)
+ spa_fga_dsp_fft_memclear(dsp, conv->segments[i], conv->fftComplexSize, false);
+ spa_fga_dsp_fft_memclear(dsp, conv->fft_buffer[0], conv->segSize, true);
+ spa_fga_dsp_fft_memclear(dsp, conv->fft_buffer[1], conv->segSize, true);
+ spa_fga_dsp_fft_memclear(dsp, conv->inputBuffer, conv->segSize, true);
+ spa_fga_dsp_fft_memclear(dsp, conv->pre_mult, conv->fftComplexSize, false);
+ spa_fga_dsp_fft_memclear(dsp, conv->conv, conv->fftComplexSize, false);
+ conv->inputBufferFill = 0;
+ conv->current = 0;
}
-static void partition_free(struct spa_fga_dsp *dsp, struct partition *part)
+static void convolver1_free(struct spa_fga_dsp *dsp, struct convolver1 *conv)
{
int i;
- for (i = 0; i < part->segCount; i++) {
- if (part->segments)
- spa_fga_dsp_fft_memfree(dsp, part->segments[i]);
- if (part->segmentsIr)
- spa_fga_dsp_fft_memfree(dsp, part->segmentsIr[i]);
+ for (i = 0; i < conv->segCount; i++) {
+ if (conv->segments)
+ spa_fga_dsp_fft_memfree(dsp, conv->segments[i]);
+ if (conv->segmentsIr)
+ spa_fga_dsp_fft_memfree(dsp, conv->segmentsIr[i]);
}
- if (part->fft)
- spa_fga_dsp_fft_free(dsp, part->fft);
- if (part->ifft)
- spa_fga_dsp_fft_free(dsp, part->ifft);
- if (part->fft_buffer[0])
- spa_fga_dsp_fft_memfree(dsp, part->fft_buffer[0]);
- if (part->fft_buffer[1])
- spa_fga_dsp_fft_memfree(dsp, part->fft_buffer[1]);
- free(part->segments);
- free(part->segmentsIr);
- spa_fga_dsp_fft_memfree(dsp, part->pre_mult);
- spa_fga_dsp_fft_memfree(dsp, part->conv);
- free(part);
+ if (conv->fft)
+ spa_fga_dsp_fft_free(dsp, conv->fft);
+ if (conv->ifft)
+ spa_fga_dsp_fft_free(dsp, conv->ifft);
+ if (conv->fft_buffer[0])
+ spa_fga_dsp_fft_memfree(dsp, conv->fft_buffer[0]);
+ if (conv->fft_buffer[1])
+ spa_fga_dsp_fft_memfree(dsp, conv->fft_buffer[1]);
+ free(conv->segments);
+ free(conv->segmentsIr);
+ spa_fga_dsp_fft_memfree(dsp, conv->pre_mult);
+ spa_fga_dsp_fft_memfree(dsp, conv->conv);
+ spa_fga_dsp_fft_memfree(dsp, conv->inputBuffer);
+ free(conv);
}
-static struct partition *partition_new(struct spa_fga_dsp *dsp, int block, const float *ir, int irlen)
+static struct convolver1 *convolver1_new(struct spa_fga_dsp *dsp, int block, const float *ir, int irlen)
{
- struct partition *part;
+ struct convolver1 *conv;
int i;
if (block == 0)
@@ -89,130 +92,134 @@ static struct partition *partition_new(struct spa_fga_dsp *dsp, int block, const
while (irlen > 0 && fabs(ir[irlen-1]) < 0.000001f)
irlen--;
- part = calloc(1, sizeof(*part));
- if (part == NULL)
+ conv = calloc(1, sizeof(*conv));
+ if (conv == NULL)
return NULL;
if (irlen == 0)
- return part;
+ return conv;
- part->blockSize = next_power_of_two(block);
- part->segSize = 2 * part->blockSize;
- part->segCount = (irlen + part->blockSize-1) / part->blockSize;
- part->fftComplexSize = (part->segSize / 2) + 1;
+ conv->blockSize = next_power_of_two(block);
+ conv->segSize = 2 * conv->blockSize;
+ conv->segCount = (irlen + conv->blockSize-1) / conv->blockSize;
+ conv->fftComplexSize = (conv->segSize / 2) + 1;
- part->fft = spa_fga_dsp_fft_new(dsp, part->segSize, true);
- if (part->fft == NULL)
+ conv->fft = spa_fga_dsp_fft_new(dsp, conv->segSize, true);
+ if (conv->fft == NULL)
goto error;
- part->ifft = spa_fga_dsp_fft_new(dsp, part->segSize, true);
- if (part->ifft == NULL)
+ conv->ifft = spa_fga_dsp_fft_new(dsp, conv->segSize, true);
+ if (conv->ifft == NULL)
goto error;
- part->fft_buffer[0] = spa_fga_dsp_fft_memalloc(dsp, part->segSize, true);
- part->fft_buffer[1] = spa_fga_dsp_fft_memalloc(dsp, part->segSize, true);
- if (part->fft_buffer[0] == NULL || part->fft_buffer[1] == NULL)
+ conv->fft_buffer[0] = spa_fga_dsp_fft_memalloc(dsp, conv->segSize, true);
+ conv->fft_buffer[1] = spa_fga_dsp_fft_memalloc(dsp, conv->segSize, true);
+ if (conv->fft_buffer[0] == NULL || conv->fft_buffer[1] == NULL)
goto error;
- part->segments = calloc(part->segCount, sizeof(float*));
- part->segmentsIr = calloc(part->segCount, sizeof(float*));
- if (part->segments == NULL || part->segmentsIr == NULL)
+ conv->segments = calloc(conv->segCount, sizeof(float*));
+ conv->segmentsIr = calloc(conv->segCount, sizeof(float*));
+ if (conv->segments == NULL || conv->segmentsIr == NULL)
goto error;
- for (i = 0; i < part->segCount; i++) {
- int left = irlen - (i * part->blockSize);
- int copy = SPA_MIN(part->blockSize, left);
+ for (i = 0; i < conv->segCount; i++) {
+ int left = irlen - (i * conv->blockSize);
+ int copy = SPA_MIN(conv->blockSize, left);
- part->segments[i] = spa_fga_dsp_fft_memalloc(dsp, part->fftComplexSize, false);
- part->segmentsIr[i] = spa_fga_dsp_fft_memalloc(dsp, part->fftComplexSize, false);
- if (part->segments[i] == NULL || part->segmentsIr[i] == NULL)
+ conv->segments[i] = spa_fga_dsp_fft_memalloc(dsp, conv->fftComplexSize, false);
+ conv->segmentsIr[i] = spa_fga_dsp_fft_memalloc(dsp, conv->fftComplexSize, false);
+ if (conv->segments[i] == NULL || conv->segmentsIr[i] == NULL)
goto error;
- spa_fga_dsp_copy(dsp, part->fft_buffer[0], &ir[i * part->blockSize], copy);
- if (copy < part->segSize)
- spa_fga_dsp_fft_memclear(dsp, part->fft_buffer[0] + copy, part->segSize - copy, true);
+ spa_fga_dsp_copy(dsp, conv->fft_buffer[0], &ir[i * conv->blockSize], copy);
+ if (copy < conv->segSize)
+ spa_fga_dsp_fft_memclear(dsp, conv->fft_buffer[0] + copy, conv->segSize - copy, true);
- spa_fga_dsp_fft_run(dsp, part->fft, 1, part->fft_buffer[0], part->segmentsIr[i]);
+ spa_fga_dsp_fft_run(dsp, conv->fft, 1, conv->fft_buffer[0], conv->segmentsIr[i]);
}
- part->pre_mult = spa_fga_dsp_fft_memalloc(dsp, part->fftComplexSize, false);
- part->conv = spa_fga_dsp_fft_memalloc(dsp, part->fftComplexSize, false);
- if (part->pre_mult == NULL || part->conv == NULL)
+ conv->pre_mult = spa_fga_dsp_fft_memalloc(dsp, conv->fftComplexSize, false);
+ conv->conv = spa_fga_dsp_fft_memalloc(dsp, conv->fftComplexSize, false);
+ conv->inputBuffer = spa_fga_dsp_fft_memalloc(dsp, conv->segSize, true);
+ if (conv->pre_mult == NULL || conv->conv == NULL || conv->inputBuffer == NULL)
goto error;
- part->scale = 1.0f / part->segSize;
- partition_reset(dsp, part);
+ conv->scale = 1.0f / conv->segSize;
+ convolver1_reset(dsp, conv);
- return part;
+ return conv;
error:
- partition_free(dsp, part);
+ convolver1_free(dsp, conv);
return NULL;
}
-static int partition_run(struct spa_fga_dsp *dsp, struct partition *part, const float *input, float *output, int len)
+static int convolver1_run(struct spa_fga_dsp *dsp, struct convolver1 *conv, const float *input, float *output, int len)
{
- int i;
+ int i, processed = 0;
- if (part == NULL || part->segCount == 0) {
+ if (conv == NULL || conv->segCount == 0) {
spa_fga_dsp_fft_memclear(dsp, output, len, true);
return len;
}
- int inputBufferFill = part->inputBufferFill;
+ int inputBufferFill = conv->inputBufferFill;
+ while (processed < len) {
+ const int processing = SPA_MIN(len - processed, conv->blockSize - inputBufferFill);
- spa_fga_dsp_fft_run(dsp, part->fft, 1, input, part->segments[part->current]);
+ spa_fga_dsp_copy(dsp, conv->inputBuffer + inputBufferFill, input + processed, processing);
+ if (inputBufferFill == 0 && processing < conv->blockSize)
+ spa_fga_dsp_fft_memclear(dsp, conv->inputBuffer + processing,
+ conv->blockSize - processing, true);
+ spa_fga_dsp_fft_run(dsp, conv->fft, 1, conv->inputBuffer, conv->segments[conv->current]);
- if (part->segCount > 1) {
- if (inputBufferFill == 0) {
- int indexAudio = part->current;
+ if (conv->segCount > 1) {
+ if (inputBufferFill == 0) {
+ int indexAudio = (conv->current + 1) % conv->segCount;
- if (++indexAudio == part->segCount)
- indexAudio = 0;
+ spa_fga_dsp_fft_cmul(dsp, conv->fft, conv->pre_mult,
+ conv->segmentsIr[1],
+ conv->segments[indexAudio],
+ conv->fftComplexSize, conv->scale);
- spa_fga_dsp_fft_cmul(dsp, part->fft, part->pre_mult,
- part->segmentsIr[1],
- part->segments[indexAudio],
- part->fftComplexSize, part->scale);
+ for (i = 2; i < conv->segCount; i++) {
+ indexAudio = (conv->current + i) % conv->segCount;
- for (i = 2; i < part->segCount; i++) {
- if (++indexAudio == part->segCount)
- indexAudio = 0;
-
- spa_fga_dsp_fft_cmuladd(dsp, part->fft,
- part->pre_mult,
- part->pre_mult,
- part->segmentsIr[i],
- part->segments[indexAudio],
- part->fftComplexSize, part->scale);
+ spa_fga_dsp_fft_cmuladd(dsp, conv->fft,
+ conv->pre_mult,
+ conv->pre_mult,
+ conv->segmentsIr[i],
+ conv->segments[indexAudio],
+ conv->fftComplexSize, conv->scale);
+ }
}
+ spa_fga_dsp_fft_cmuladd(dsp, conv->fft,
+ conv->conv,
+ conv->pre_mult,
+ conv->segments[conv->current],
+ conv->segmentsIr[0],
+ conv->fftComplexSize, conv->scale);
+ } else {
+ spa_fga_dsp_fft_cmul(dsp, conv->fft,
+ conv->conv,
+ conv->segments[conv->current],
+ conv->segmentsIr[0],
+ conv->fftComplexSize, conv->scale);
}
- spa_fga_dsp_fft_cmuladd(dsp, part->fft,
- part->conv,
- part->pre_mult,
- part->segments[part->current],
- part->segmentsIr[0],
- part->fftComplexSize, part->scale);
- } else {
- spa_fga_dsp_fft_cmul(dsp, part->fft,
- part->conv,
- part->segments[part->current],
- part->segmentsIr[0],
- part->fftComplexSize, part->scale);
+
+ spa_fga_dsp_fft_run(dsp, conv->ifft, -1, conv->conv, conv->fft_buffer[0]);
+
+ spa_fga_dsp_sum(dsp, output + processed, conv->fft_buffer[0] + inputBufferFill,
+ conv->fft_buffer[1] + conv->blockSize + inputBufferFill, processing);
+
+ inputBufferFill += processing;
+ if (inputBufferFill == conv->blockSize) {
+ inputBufferFill = 0;
+
+ SPA_SWAP(conv->fft_buffer[0], conv->fft_buffer[1]);
+
+ conv->current = (conv->current > 0) ? (conv->current - 1) : (conv->segCount - 1);
+ }
+
+ processed += processing;
}
-
- spa_fga_dsp_fft_run(dsp, part->ifft, -1, part->conv, part->fft_buffer[0]);
-
- spa_fga_dsp_sum(dsp, output, part->fft_buffer[0] + inputBufferFill,
- part->fft_buffer[1] + part->blockSize + inputBufferFill, len);
-
- inputBufferFill += len;
- if (inputBufferFill == part->blockSize) {
- inputBufferFill = 0;
-
- SPA_SWAP(part->fft_buffer[0], part->fft_buffer[1]);
-
- if (part->current == 0)
- part->current = part->segCount;
- part->current--;
- }
- part->inputBufferFill = inputBufferFill;
+ conv->inputBufferFill = inputBufferFill;
return len;
}
@@ -221,8 +228,11 @@ struct convolver
struct spa_fga_dsp *dsp;
int headBlockSize;
int tailBlockSize;
- struct partition *headPartition;
- struct partition *tailPartition;
+ struct convolver1 *headConvolver;
+ struct convolver1 *tailConvolver0;
+ float *tailOutput0;
+ float *tailPrecalculated0;
+ struct convolver1 *tailConvolver;
float *tailOutput;
float *tailPrecalculated;
float *tailInput;
@@ -233,10 +243,15 @@ void convolver_reset(struct convolver *conv)
{
struct spa_fga_dsp *dsp = conv->dsp;
- if (conv->headPartition)
- partition_reset(dsp, conv->headPartition);
- if (conv->tailPartition) {
- partition_reset(dsp, conv->tailPartition);
+ if (conv->headConvolver)
+ convolver1_reset(dsp, conv->headConvolver);
+ if (conv->tailConvolver0) {
+ convolver1_reset(dsp, conv->tailConvolver0);
+ spa_fga_dsp_fft_memclear(dsp, conv->tailOutput0, conv->tailBlockSize, true);
+ spa_fga_dsp_fft_memclear(dsp, conv->tailPrecalculated0, conv->tailBlockSize, true);
+ }
+ if (conv->tailConvolver) {
+ convolver1_reset(dsp, conv->tailConvolver);
spa_fga_dsp_fft_memclear(dsp, conv->tailOutput, conv->tailBlockSize, true);
spa_fga_dsp_fft_memclear(dsp, conv->tailPrecalculated, conv->tailBlockSize, true);
}
@@ -270,19 +285,34 @@ struct convolver *convolver_new(struct spa_fga_dsp *dsp, int head_block, int tai
conv->headBlockSize = next_power_of_two(head_block);
conv->tailBlockSize = next_power_of_two(tail_block);
- head_ir_len = SPA_MIN(irlen, 2 * conv->tailBlockSize);
- conv->headPartition = partition_new(dsp, conv->headBlockSize, ir, head_ir_len);
- if (conv->headPartition == NULL)
+ head_ir_len = SPA_MIN(irlen, conv->tailBlockSize);
+ conv->headConvolver = convolver1_new(dsp, conv->headBlockSize, ir, head_ir_len);
+ if (conv->headConvolver == NULL)
goto error;
+ if (irlen > conv->tailBlockSize) {
+ int conv1IrLen = SPA_MIN(irlen - conv->tailBlockSize, conv->tailBlockSize);
+ conv->tailConvolver0 = convolver1_new(dsp, conv->headBlockSize, ir + conv->tailBlockSize, conv1IrLen);
+ conv->tailOutput0 = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true);
+ conv->tailPrecalculated0 = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true);
+ if (conv->tailConvolver0 == NULL || conv->tailOutput0 == NULL ||
+ conv->tailPrecalculated0 == NULL)
+ goto error;
+ }
+
if (irlen > 2 * conv->tailBlockSize) {
int tailIrLen = irlen - (2 * conv->tailBlockSize);
- conv->tailPartition = partition_new(dsp, conv->tailBlockSize, ir + (2 * conv->tailBlockSize), tailIrLen);
+ conv->tailConvolver = convolver1_new(dsp, conv->tailBlockSize, ir + (2 * conv->tailBlockSize), tailIrLen);
conv->tailOutput = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true);
conv->tailPrecalculated = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true);
- conv->tailInput = spa_fga_dsp_fft_memalloc(dsp, 2 * conv->tailBlockSize, true);
- if (conv->tailPartition == NULL || conv->tailOutput == NULL ||
- conv->tailPrecalculated == NULL || conv->tailInput == NULL)
+ if (conv->tailConvolver == NULL || conv->tailOutput == NULL ||
+ conv->tailPrecalculated == NULL)
+ goto error;
+ }
+
+ if (conv->tailConvolver0 || conv->tailConvolver) {
+ conv->tailInput = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true);
+ if (conv->tailInput == NULL)
goto error;
}
@@ -298,10 +328,14 @@ void convolver_free(struct convolver *conv)
{
struct spa_fga_dsp *dsp = conv->dsp;
- if (conv->headPartition)
- partition_free(dsp, conv->headPartition);
- if (conv->tailPartition)
- partition_free(dsp, conv->tailPartition);
+ if (conv->headConvolver)
+ convolver1_free(dsp, conv->headConvolver);
+ if (conv->tailConvolver0)
+ convolver1_free(dsp, conv->tailConvolver0);
+ if (conv->tailConvolver)
+ convolver1_free(dsp, conv->tailConvolver);
+ spa_fga_dsp_fft_memfree(dsp, conv->tailOutput0);
+ spa_fga_dsp_fft_memfree(dsp, conv->tailPrecalculated0);
spa_fga_dsp_fft_memfree(dsp, conv->tailOutput);
spa_fga_dsp_fft_memfree(dsp, conv->tailPrecalculated);
spa_fga_dsp_fft_memfree(dsp, conv->tailInput);
@@ -310,37 +344,50 @@ void convolver_free(struct convolver *conv)
int convolver_run(struct convolver *conv, const float *input, float *output, int length)
{
- int processed = 0;
struct spa_fga_dsp *dsp = conv->dsp;
- while (processed < length) {
- int remaining = length - processed;
- int blockRemain = conv->tailInputFill % conv->headBlockSize;
- int processing = SPA_MIN(remaining, conv->headBlockSize - blockRemain);
+ convolver1_run(dsp, conv->headConvolver, input, output, length);
- spa_memcpy(conv->tailInput + conv->tailInputFill, input + processed, processing * sizeof(float));
- memset(conv->tailInput + conv->tailInputFill + processing, 0,
- (2 * conv->headBlockSize - processing) * sizeof(float));
+ if (conv->tailInput) {
+ int processed = 0;
- partition_run(dsp, conv->headPartition, conv->tailInput + conv->tailInputFill,
- &output[processed], processing);
+ while (processed < length) {
+ int remaining = length - processed;
+ int processing = SPA_MIN(remaining, conv->headBlockSize - (conv->tailInputFill % conv->headBlockSize));
- if (conv->tailPrecalculated)
- spa_fga_dsp_sum(dsp, &output[processed], &output[processed],
- &conv->tailPrecalculated[conv->tailInputFill],
- processing);
+ if (conv->tailPrecalculated0)
+ spa_fga_dsp_sum(dsp, &output[processed], &output[processed],
+ &conv->tailPrecalculated0[conv->tailInputFill],
+ processing);
+ if (conv->tailPrecalculated)
+ spa_fga_dsp_sum(dsp, &output[processed], &output[processed],
+ &conv->tailPrecalculated[conv->tailInputFill],
+ processing);
- conv->tailInputFill += processing;
+ spa_fga_dsp_copy(dsp, conv->tailInput + conv->tailInputFill, input + processed, processing);
+ conv->tailInputFill += processing;
- if (conv->tailInputFill == conv->tailBlockSize) {
- if (conv->tailPrecalculated) {
+ if (conv->tailPrecalculated0 && (conv->tailInputFill % conv->headBlockSize == 0)) {
+ int blockOffset = conv->tailInputFill - conv->headBlockSize;
+ convolver1_run(dsp, conv->tailConvolver0,
+ conv->tailInput + blockOffset,
+ conv->tailOutput0 + blockOffset,
+ conv->headBlockSize);
+ if (conv->tailInputFill == conv->tailBlockSize)
+ SPA_SWAP(conv->tailPrecalculated0, conv->tailOutput0);
+ }
+
+ if (conv->tailPrecalculated &&
+ conv->tailInputFill == conv->tailBlockSize) {
SPA_SWAP(conv->tailPrecalculated, conv->tailOutput);
- partition_run(dsp, conv->tailPartition, conv->tailInput,
+ convolver1_run(dsp, conv->tailConvolver, conv->tailInput,
conv->tailOutput, conv->tailBlockSize);
}
- conv->tailInputFill = 0;
+ if (conv->tailInputFill == conv->tailBlockSize)
+ conv->tailInputFill = 0;
+
+ processed += processing;
}
- processed += processing;
}
return 0;
}
diff --git a/spa/plugins/filter-graph/filter-graph.c b/spa/plugins/filter-graph/filter-graph.c
index 4b47d0c9f..05ba7b3fb 100644
--- a/spa/plugins/filter-graph/filter-graph.c
+++ b/spa/plugins/filter-graph/filter-graph.c
@@ -19,7 +19,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -222,7 +221,6 @@ struct impl {
struct spa_cpu *cpu;
struct spa_fga_dsp *dsp;
struct spa_plugin_loader *loader;
- struct spa_loop *data_loop;
uint64_t info_all;
struct spa_filter_graph_info info;
@@ -554,25 +552,19 @@ static int impl_get_props(void *object, struct spa_pod_builder *b, struct spa_po
struct descriptor *desc = node->desc;
const struct spa_fga_descriptor *d = desc->desc;
struct spa_fga_port *p = &d->ports[port->p];
- float v, min, max;
if (node->name[0] != '\0')
snprintf(name, sizeof(name), "%s:%s", node->name, p->name);
else
snprintf(name, sizeof(name), "%s", p->name);
- if (port->control_initialized)
- v = port->control_current;
- else
- get_ranges(impl, p, &v, &min, &max);
-
spa_pod_builder_string(b, name);
if (p->hint & SPA_FGA_HINT_BOOLEAN) {
- spa_pod_builder_bool(b, v <= 0.0f ? false : true);
+ spa_pod_builder_bool(b, port->control_data[0] <= 0.0f ? false : true);
} else if (p->hint & SPA_FGA_HINT_INTEGER) {
- spa_pod_builder_int(b, (int32_t)v);
+ spa_pod_builder_int(b, (int32_t)port->control_data[0]);
} else {
- spa_pod_builder_float(b, v);
+ spa_pod_builder_float(b, port->control_data[0]);
}
}
spa_pod_builder_pop(b, &f[1]);
@@ -706,46 +698,21 @@ static int impl_reset(void *object)
return 0;
}
-static int
-do_emit_node_control_sync(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
- size_t size, void *user_data)
+static void node_control_changed(struct node *node)
{
- struct impl *impl = user_data;
- struct graph *graph = &impl->graph;
- struct node *node;
- uint32_t i;
- spa_list_for_each(node, &graph->node_list, link) {
- const struct spa_fga_descriptor *d = node->desc->desc;
- if (!node->control_changed || d->control_sync == NULL)
- continue;
- for (i = 0; i < node->n_hndl; i++) {
- if (node->hndl[i] != NULL)
- d->control_sync(node->hndl[i]);
- }
- }
- return 0;
-}
-
-static void emit_node_control_changed(struct impl *impl)
-{
- struct graph *graph = &impl->graph;
- struct node *node;
+ const struct spa_fga_descriptor *d = node->desc->desc;
uint32_t i;
- spa_loop_locked(impl->data_loop, do_emit_node_control_sync, 1, NULL, 0, impl);
+ if (!node->control_changed)
+ return;
- spa_list_for_each(node, &graph->node_list, link) {
- const struct spa_fga_descriptor *d = node->desc->desc;
- if (!node->control_changed)
+ for (i = 0; i < node->n_hndl; i++) {
+ if (node->hndl[i] == NULL)
continue;
- if (d->control_changed != NULL) {
- for (i = 0; i < node->n_hndl; i++) {
- if (node->hndl[i] != NULL)
- d->control_changed(node->hndl[i]);
- }
- }
- node->control_changed = false;
+ if (d->control_changed)
+ d->control_changed(node->hndl[i]);
}
+ node->control_changed = false;
}
static int sync_volume(struct graph *graph, struct volume *vol)
@@ -859,7 +826,11 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru
spa_pod_dynamic_builder_clean(&b);
if (changed > 0) {
- emit_node_control_changed(impl);
+ struct node *node;
+
+ spa_list_for_each(node, &graph->node_list, link)
+ node_control_changed(node);
+
spa_filter_graph_emit_props_changed(&impl->hooks, SPA_DIRECTION_INPUT);
}
return 0;
@@ -1725,10 +1696,10 @@ static int impl_activate(void *object, const struct spa_dict *props)
for (i = 0; i < node->n_hndl; i++) {
if (d->activate)
d->activate(node->hndl[i]);
+ if (node->control_changed && d->control_changed)
+ d->control_changed(node->hndl[i]);
}
}
- emit_node_control_changed(impl);
-
/* calculate latency */
sort_reset(graph);
while ((node = sort_next_node(graph)) != NULL) {
@@ -2376,7 +2347,6 @@ impl_init(const struct spa_handle_factory *factory,
spa_log_topic_init(impl->log, &log_topic);
impl->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
- impl->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
impl->max_align = spa_cpu_get_max_align(impl->cpu);
impl->dsp = spa_fga_dsp_new(impl->cpu ? spa_cpu_get_flags(impl->cpu) : 0);
diff --git a/spa/plugins/filter-graph/meson.build b/spa/plugins/filter-graph/meson.build
index 20b90f4c4..94ee0bd25 100644
--- a/spa/plugins/filter-graph/meson.build
+++ b/spa/plugins/filter-graph/meson.build
@@ -18,16 +18,16 @@ if have_sse
simd_cargs += ['-DHAVE_SSE']
simd_dependencies += filter_graph_sse
endif
-if have_avx2 and have_fma
- filter_graph_avx2_fma = static_library('filter_graph_avx2_fma',
+if have_avx2
+ filter_graph_avx2 = static_library('filter_graph_avx2',
['audio-dsp-avx2.c' ],
include_directories : [configinc],
- c_args : [avx2_args, fma_args, '-O3', '-DHAVE_AVX2', '-DHAVE_FMA'],
+ c_args : [avx2_args, fma_args,'-O3', '-DHAVE_AVX2'],
dependencies : [ spa_dep ],
install : false
)
- simd_cargs += ['-DHAVE_AVX2', '-DHAVE_FMA']
- simd_dependencies += filter_graph_avx2_fma
+ simd_cargs += ['-DHAVE_AVX2']
+ simd_dependencies += filter_graph_avx2
endif
if have_neon
filter_graph_neon = static_library('filter_graph_neon',
diff --git a/spa/plugins/filter-graph/plugin_builtin.c b/spa/plugins/filter-graph/plugin_builtin.c
index b4882010c..3bcde30c9 100644
--- a/spa/plugins/filter-graph/plugin_builtin.c
+++ b/spa/plugins/filter-graph/plugin_builtin.c
@@ -21,7 +21,6 @@
#include
#include
#include
-#include
#include
#include
@@ -202,42 +201,42 @@ static struct spa_fga_port mixer_ports[] = {
{ .index = 9,
.name = "Gain 1",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 1.0f, .min = -10.0f, .max = 10.0f
+ .def = 1.0f, .min = 0.0f, .max = 10.0f
},
{ .index = 10,
.name = "Gain 2",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 1.0f, .min = -10.0f, .max = 10.0f
+ .def = 1.0f, .min = 0.0f, .max = 10.0f
},
{ .index = 11,
.name = "Gain 3",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 1.0f, .min = -10.0f, .max = 10.0f
+ .def = 1.0f, .min = 0.0f, .max = 10.0f
},
{ .index = 12,
.name = "Gain 4",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 1.0f, .min = -10.0f, .max = 10.0f
+ .def = 1.0f, .min = 0.0f, .max = 10.0f
},
{ .index = 13,
.name = "Gain 5",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 1.0f, .min = -10.0f, .max = 10.0f
+ .def = 1.0f, .min = 0.0f, .max = 10.0f
},
{ .index = 14,
.name = "Gain 6",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 1.0f, .min = -10.0f, .max = 10.0f
+ .def = 1.0f, .min = 0.0f, .max = 10.0f
},
{ .index = 15,
.name = "Gain 7",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 1.0f, .min = -10.0f, .max = 10.0f
+ .def = 1.0f, .min = 0.0f, .max = 10.0f
},
{ .index = 16,
.name = "Gain 8",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 1.0f, .min = -10.0f, .max = 10.0f
+ .def = 1.0f, .min = 0.0f, .max = 10.0f
},
};
@@ -543,12 +542,7 @@ static void bq_run(void *Instance, unsigned long samples)
struct biquad *bq = &impl->bq;
float *out = impl->port[0];
float *in = impl->port[1];
- spa_fga_dsp_biquad_run(impl->dsp, bq, 1, 0, &out, (const float **)&in, 1, samples);
-}
-static void bq_control_sync(void * Instance)
-{
- struct builtin *impl = Instance;
if (impl->type == BQ_NONE) {
float b0, b1, b2, a0, a1, a2;
b0 = impl->port[5][0];
@@ -568,6 +562,7 @@ static void bq_control_sync(void * Instance)
if (impl->freq != freq || impl->Q != Q || impl->gain != gain)
bq_freq_update(impl, impl->type, freq, Q, gain);
}
+ spa_fga_dsp_biquad_run(impl->dsp, bq, 1, 0, &out, (const float **)&in, 1, samples);
}
/** bq_lowpass */
@@ -579,7 +574,6 @@ static const struct spa_fga_descriptor bq_lowpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
- .control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@@ -594,7 +588,6 @@ static const struct spa_fga_descriptor bq_highpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
- .control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@@ -609,7 +602,6 @@ static const struct spa_fga_descriptor bq_bandpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
- .control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@@ -624,7 +616,6 @@ static const struct spa_fga_descriptor bq_lowshelf_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
- .control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@@ -639,7 +630,6 @@ static const struct spa_fga_descriptor bq_highshelf_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
- .control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@@ -654,7 +644,6 @@ static const struct spa_fga_descriptor bq_peaking_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
- .control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@@ -669,7 +658,6 @@ static const struct spa_fga_descriptor bq_notch_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
- .control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@@ -685,7 +673,6 @@ static const struct spa_fga_descriptor bq_allpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
- .control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@@ -700,7 +687,6 @@ static const struct spa_fga_descriptor bq_raw_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
- .control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@@ -1467,7 +1453,6 @@ static struct spa_fga_port clamp_ports[] = {
{ .index = 3,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 4,
.name = "Min",
@@ -1525,7 +1510,6 @@ static struct spa_fga_port linear_ports[] = {
{ .index = 3,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 4,
.name = "Mult",
@@ -1593,7 +1577,6 @@ static struct spa_fga_port recip_ports[] = {
{ .index = 3,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
};
@@ -1643,7 +1626,6 @@ static struct spa_fga_port exp_ports[] = {
{ .index = 3,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 4,
.name = "Base",
@@ -1701,7 +1683,6 @@ static struct spa_fga_port log_ports[] = {
{ .index = 3,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 4,
.name = "Base",
@@ -2491,12 +2472,10 @@ static struct spa_fga_port ramp_ports[] = {
{ .index = 1,
.name = "Start",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 2,
.name = "Stop",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 1.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 3,
.name = "Current",
@@ -2505,7 +2484,6 @@ static struct spa_fga_port ramp_ports[] = {
{ .index = 4,
.name = "Duration (s)",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 1.0f, .min = 0.0f, .max = FLT_MAX
},
};
@@ -2665,7 +2643,6 @@ static struct spa_fga_port debug_ports[] = {
{ .index = 2,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 3,
.name = "Notify",
@@ -3012,7 +2989,7 @@ static struct spa_fga_port noisegate_ports[] = {
{ .index = 2,
.name = "Level",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = NAN, .min = -FLT_MAX, .max = FLT_MAX
+ .def = NAN
},
{ .index = 3,
.name = "Open Threshold",
@@ -3253,7 +3230,6 @@ static struct spa_fga_port null_ports[] = {
{ .index = 1,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
};
diff --git a/spa/plugins/filter-graph/plugin_ebur128.c b/spa/plugins/filter-graph/plugin_ebur128.c
index 02fbcbe53..fb79de590 100644
--- a/spa/plugins/filter-graph/plugin_ebur128.c
+++ b/spa/plugins/filter-graph/plugin_ebur128.c
@@ -399,7 +399,6 @@ static struct spa_fga_port lufs2gain_ports[] = {
{ .index = 0,
.name = "LUFS",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- .def = 0.0f, .min = 0.0f, .max = FLT_MAX
},
{ .index = 1,
.name = "Gain",
diff --git a/spa/plugins/filter-graph/plugin_ffmpeg.c b/spa/plugins/filter-graph/plugin_ffmpeg.c
index 86001c0fd..4e4b9f4a0 100644
--- a/spa/plugins/filter-graph/plugin_ffmpeg.c
+++ b/spa/plugins/filter-graph/plugin_ffmpeg.c
@@ -243,7 +243,7 @@ static void ffmpeg_run(void *instance, unsigned long SampleCount)
delay);
for (j = 0; j < desc->layout[c].nb_channels; j++)
- spa_memcpy(i->data[d++], i->frame->data[j], SampleCount * sizeof(float));
+ memcpy(i->data[d++], i->frame->data[j], SampleCount * sizeof(float));
av_frame_unref(i->frame);
}
diff --git a/spa/plugins/filter-graph/plugin_ladspa.c b/spa/plugins/filter-graph/plugin_ladspa.c
index 54315861b..1aebafe35 100644
--- a/spa/plugins/filter-graph/plugin_ladspa.c
+++ b/spa/plugins/filter-graph/plugin_ladspa.c
@@ -233,49 +233,43 @@ static inline const char *split_walk(const char *str, const char *delimiter, siz
return s;
}
-static void make_search_paths(const char **path, const char **search_dirs)
-{
- const char *p;
-
- while ((p = strstr(*path, "../")) != NULL)
- *path = p + 3;
-
- *search_dirs = getenv("LADSPA_PATH");
- if (!*search_dirs)
- *search_dirs = "/usr/lib64/ladspa:/usr/lib/ladspa:" LIBDIR;
-}
-
-static int load_ladspa_plugin(struct plugin *impl, const char *path, const char *search_dirs)
+static int load_ladspa_plugin(struct plugin *impl, const char *path)
{
int res = -ENOENT;
- const char *p, *state = NULL;
- char filename[PATH_MAX];
- size_t len;
- /*
- * set the errno for the case when `ladspa_handle_load_by_path()`
- * is never called, which can only happen if the supplied
- * LADSPA_PATH contains too long paths
- */
- res = -ENAMETOOLONG;
+ if (path[0] != '/') {
+ const char *search_dirs, *p, *state = NULL;
+ char filename[PATH_MAX];
+ size_t len;
- while ((p = split_walk(search_dirs, ":", &len, &state))) {
- int namelen;
+ search_dirs = getenv("LADSPA_PATH");
+ if (!search_dirs)
+ search_dirs = "/usr/lib64/ladspa:/usr/lib/ladspa:" LIBDIR;
- if (len == 0 || len >= sizeof(filename))
- continue;
+ /*
+ * set the errno for the case when `ladspa_handle_load_by_path()`
+ * is never called, which can only happen if the supplied
+ * LADSPA_PATH contains too long paths
+ */
+ res = -ENAMETOOLONG;
+
+ while ((p = split_walk(search_dirs, ":", &len, &state))) {
+ int namelen;
+
+ if (len >= sizeof(filename))
+ continue;
- if (strncmp(path, p, len) == 0 && (path[len-1] == '/' || path[len] == '/'))
- namelen = snprintf(filename, sizeof(filename), "%s", path);
- else
namelen = snprintf(filename, sizeof(filename), "%.*s/%s.so", (int) len, p, path);
+ if (namelen < 0 || (size_t) namelen >= sizeof(filename))
+ continue;
- if (namelen < 0 || (size_t) namelen >= sizeof(filename))
- continue;
-
- res = ladspa_handle_load_by_path(impl, filename);
- if (res >= 0)
- break;
+ res = ladspa_handle_load_by_path(impl, filename);
+ if (res >= 0)
+ break;
+ }
+ }
+ else {
+ res = ladspa_handle_load_by_path(impl, path);
}
return res;
}
@@ -323,7 +317,7 @@ impl_init(const struct spa_handle_factory *factory,
struct plugin *impl;
uint32_t i;
int res;
- const char *path = NULL, *search_dirs;
+ const char *path = NULL;
handle->get_interface = impl_get_interface;
handle->clear = impl_clear;
@@ -341,11 +335,9 @@ impl_init(const struct spa_handle_factory *factory,
if (path == NULL)
return -EINVAL;
- make_search_paths(&path, &search_dirs);
-
- if ((res = load_ladspa_plugin(impl, path, search_dirs)) < 0) {
- spa_log_error(impl->log, "failed to load plugin '%s' in '%s': %s",
- path, search_dirs, spa_strerror(res));
+ if ((res = load_ladspa_plugin(impl, path)) < 0) {
+ spa_log_error(impl->log, "failed to load plugin '%s': %s",
+ path, spa_strerror(res));
return res;
}
diff --git a/spa/plugins/filter-graph/plugin_onnx.c b/spa/plugins/filter-graph/plugin_onnx.c
index 13fccee56..3ef12e963 100644
--- a/spa/plugins/filter-graph/plugin_onnx.c
+++ b/spa/plugins/filter-graph/plugin_onnx.c
@@ -593,10 +593,6 @@ static const struct spa_fga_descriptor *onnx_plugin_make_desc(void *plugin, cons
fp->flags |= SPA_FGA_PORT_OUTPUT;
fp->name = ti->data_name;
- fp->min = -FLT_MAX;
- fp->max = FLT_MAX;
- fp->def = 0.0f;
-
ti->data_index = desc->desc.n_ports;
desc->desc.n_ports++;
diff --git a/spa/plugins/libcamera/libcamera-device.cpp b/spa/plugins/libcamera/libcamera-device.cpp
index 6517860fc..2d6532f82 100644
--- a/spa/plugins/libcamera/libcamera-device.cpp
+++ b/spa/plugins/libcamera/libcamera-device.cpp
@@ -5,7 +5,6 @@
/* SPDX-License-Identifier: MIT */
#include
-#include
#include
#include
@@ -26,6 +25,7 @@
#include
#include
+#include
using namespace libcamera;
@@ -50,7 +50,7 @@ struct impl {
std::string device_id);
};
-std::span cameraDevice(const Camera& camera)
+const libcamera::Span cameraDevice(const Camera& camera)
{
if (auto devices = camera.properties().get(properties::SystemDevices))
return devices.value();
diff --git a/spa/plugins/meson.build b/spa/plugins/meson.build
index f774c1036..42aec7ed3 100644
--- a/spa/plugins/meson.build
+++ b/spa/plugins/meson.build
@@ -1,4 +1,4 @@
-if alsa_dep.found()
+if alsa_dep.found() and host_machine.system() == 'linux'
subdir('alsa')
endif
if get_option('avb').require(host_machine.system() == 'linux', error_message: 'AVB support is only available on Linux').allowed()
diff --git a/spa/plugins/support/cpu-x86.c b/spa/plugins/support/cpu-x86.c
index 0fb866671..c1c53855d 100644
--- a/spa/plugins/support/cpu-x86.c
+++ b/spa/plugins/support/cpu-x86.c
@@ -78,8 +78,6 @@ x86_init(struct impl *impl)
if ((ebx & AVX512_BITS) == AVX512_BITS)
flags |= SPA_CPU_FLAG_AVX512;
}
- if (max_level < 0x16)
- flags |= SPA_CPU_FLAG_SLOW_GATHER;
/* Check cpuid level of extended features. */
__cpuid (0x80000000, ext_level, ebx, ecx, edx);
diff --git a/spa/plugins/support/logger.c b/spa/plugins/support/logger.c
index 7d0b14c1f..6ea5f31b5 100644
--- a/spa/plugins/support/logger.c
+++ b/spa/plugins/support/logger.c
@@ -2,16 +2,12 @@
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
/* SPDX-License-Identifier: MIT */
-#include "config.h"
-
#include
-#include
#include
#include
#include
#include
#include
-#include
#include
#include
@@ -71,10 +67,16 @@ impl_log_logtv(void *object,
const char *fmt,
va_list args)
{
+#define RESERVED_LENGTH 24
+
struct impl *impl = object;
- char location[1024];
+ char timestamp[18] = {0};
+ char topicstr[32] = {0};
+ char filename[64] = {0};
+ char location[1000 + RESERVED_LENGTH], *p;
static const char * const levels[] = { "-", "E", "W", "I", "D", "T", "*T*" };
const char *prefix = "", *suffix = "";
+ int size, len;
bool do_trace;
if ((do_trace = (level == SPA_LOG_LEVEL_TRACE && impl->have_source)))
@@ -91,18 +93,8 @@ impl_log_logtv(void *object,
suffix = SPA_ANSI_RESET;
}
- struct spa_strbuf msg;
- spa_strbuf_init(&msg, location, sizeof(location));
-
- spa_strbuf_append(&msg, "%s[%s]", prefix, levels[level]);
-
-#ifdef HAVE_GETTID
- static thread_local pid_t tid;
- if (SPA_UNLIKELY(tid == 0))
- tid = gettid();
-
- spa_strbuf_append(&msg, "[%jd]", (intmax_t) tid);
-#endif
+ p = location;
+ len = sizeof(location) - RESERVED_LENGTH;
if (impl->local_timestamp) {
char buf[64];
@@ -112,52 +104,67 @@ impl_log_logtv(void *object,
clock_gettime(impl->clock_id, &now);
localtime_r(&now.tv_sec, &now_tm);
strftime(buf, sizeof(buf), "%H:%M:%S", &now_tm);
- spa_strbuf_append(&msg, "[%s.%06d]", buf,
+ spa_scnprintf(timestamp, sizeof(timestamp), "[%s.%06d]", buf,
(int)(now.tv_nsec / SPA_NSEC_PER_USEC));
} else if (impl->timestamp) {
struct timespec now;
clock_gettime(impl->clock_id, &now);
- spa_strbuf_append(&msg, "[%05jd.%06jd]",
+ spa_scnprintf(timestamp, sizeof(timestamp), "[%05jd.%06jd]",
(intmax_t) (now.tv_sec & 0x1FFFFFFF) % 100000, (intmax_t) now.tv_nsec / 1000);
}
if (topic && topic->topic)
- spa_strbuf_append(&msg, " %-12s | ", topic->topic);
+ spa_scnprintf(topicstr, sizeof(topicstr), " %-12s | ", topic->topic);
if (impl->line && line != 0) {
const char *s = strrchr(file, '/');
- spa_strbuf_append(&msg, "[%16.16s:%5i %s()]",
+ spa_scnprintf(filename, sizeof(filename), "[%16.16s:%5i %s()]",
s ? s + 1 : file, line, func);
}
- spa_strbuf_append(&msg, " ");
- spa_strbuf_appendv(&msg, fmt, args);
- spa_strbuf_append(&msg, "%s\n", suffix);
+ size = spa_scnprintf(p, len, "%s[%s]%s%s%s ", prefix, levels[level],
+ timestamp, topicstr, filename);
+ /*
+ * it is assumed that at this point `size` <= `len`,
+ * which is reasonable as long as file names and function names
+ * don't become very long
+ */
+ size += spa_vscnprintf(p + size, len - size, fmt, args);
- if (SPA_UNLIKELY(msg.pos >= msg.maxsize)) {
- static const char truncated_text[] = "... (truncated)";
- size_t suffix_length = strlen(suffix) + strlen(truncated_text) + 1 + 1;
+ /*
+ * `RESERVED_LENGTH` bytes are reserved for printing the suffix
+ * (at the moment it's "... (truncated)\x1B[0m\n" at its longest - 21 bytes),
+ * its length must be less than `RESERVED_LENGTH` (including the null byte),
+ * otherwise a stack buffer overrun could ensue
+ */
- spa_assert(msg.maxsize >= suffix_length);
- msg.pos = msg.maxsize - suffix_length;
-
- spa_strbuf_append(&msg, "%s%s\n", truncated_text, suffix);
- spa_assert(msg.pos < msg.maxsize);
+ /* if the message could not fit entirely... */
+ if (size >= len - 1) {
+ size = len - 1; /* index of the null byte */
+ len = sizeof(location);
+ size += spa_scnprintf(p + size, len - size, "... (truncated)");
}
+ else {
+ len = sizeof(location);
+ }
+
+ size += spa_scnprintf(p + size, len - size, "%s\n", suffix);
if (SPA_UNLIKELY(do_trace)) {
uint32_t index;
spa_ringbuffer_get_write_index(&impl->trace_rb, &index);
spa_ringbuffer_write_data(&impl->trace_rb, impl->trace_data, TRACE_BUFFER,
- index & (TRACE_BUFFER - 1), msg.buffer, msg.pos);
- spa_ringbuffer_write_update(&impl->trace_rb, index + msg.pos);
+ index & (TRACE_BUFFER - 1), location, size);
+ spa_ringbuffer_write_update(&impl->trace_rb, index + size);
if (spa_system_eventfd_write(impl->system, impl->source.fd, 1) < 0)
fprintf(impl->file, "error signaling eventfd: %s\n", strerror(errno));
} else
- fputs(msg.buffer, impl->file);
+ fputs(location, impl->file);
+
+#undef RESERVED_LENGTH
}
static SPA_PRINTF_FUNC(6,0) void
diff --git a/spa/plugins/support/loop.c b/spa/plugins/support/loop.c
index 5e35f6dcd..e5e49849f 100644
--- a/spa/plugins/support/loop.c
+++ b/spa/plugins/support/loop.c
@@ -462,12 +462,12 @@ again:
* this invoking thread but we need to serialize the flushing here with
* a mutex */
if (loop_thread == 0)
- spa_assert_se(pthread_mutex_lock(&impl->lock) == 0);
+ pthread_mutex_lock(&impl->lock);
flush_all_queues(impl);
if (loop_thread == 0)
- spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
+ pthread_mutex_unlock(&impl->lock);
res = item->res;
} else {
@@ -482,9 +482,9 @@ again:
recurse = impl->recurse;
while (impl->recurse > 0) {
impl->recurse--;
- spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
+ pthread_mutex_unlock(&impl->lock);
}
- spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
+ pthread_mutex_unlock(&impl->lock);
}
if ((res = spa_system_eventfd_read(impl->system, queue->ack_fd, &count)) < 0)
@@ -492,7 +492,7 @@ again:
queue, queue->ack_fd, spa_strerror(res));
for (i = 0; i < recurse; i++) {
- spa_assert_se(pthread_mutex_lock(&impl->lock) == 0);
+ pthread_mutex_lock(&impl->lock);
impl->recurse++;
}
@@ -569,14 +569,9 @@ static int loop_locked(void *object, spa_invoke_func_t func, uint32_t seq,
{
struct impl *impl = object;
int res;
-
- res = pthread_mutex_lock(&impl->lock);
- if (res)
- return -res;
-
+ pthread_mutex_lock(&impl->lock);
res = func(&impl->loop, false, seq, data, size, user_data);
- spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
-
+ pthread_mutex_unlock(&impl->lock);
return res;
}
@@ -603,7 +598,7 @@ static void loop_enter(void *object)
struct impl *impl = object;
pthread_t thread_id = pthread_self();
- spa_assert_se(pthread_mutex_lock(&impl->lock) == 0);
+ pthread_mutex_lock(&impl->lock);
if (impl->enter_count == 0) {
spa_return_if_fail(impl->thread == 0);
impl->thread = thread_id;
@@ -630,7 +625,7 @@ static void loop_leave(void *object)
impl->thread = 0;
flush_all_queues(impl);
}
- spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
+ pthread_mutex_unlock(&impl->lock);
}
static int loop_check(void *object)
@@ -649,7 +644,7 @@ static int loop_check(void *object)
/* we could take the lock, check if we actually locked it somewhere */
res = impl->recurse > 0 ? 1 : -EPERM;
- spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
+ pthread_mutex_unlock(&impl->lock);
return res;
}
static int loop_lock(void *object)
@@ -727,20 +722,13 @@ static int loop_accept(void *object)
}
struct cancellation_handler_data {
- struct impl *impl;
- const struct spa_poll_event *ep;
- volatile int ep_count;
- volatile int unlocked;
- volatile int locked;
+ struct spa_poll_event *ep;
+ int ep_count;
};
static void cancellation_handler(void *closure)
{
const struct cancellation_handler_data *data = closure;
- struct impl *impl = data->impl;
-
- if (data->unlocked && !data->locked)
- spa_assert_se(pthread_mutex_lock(&impl->lock) == 0);
for (int i = 0; i < data->ep_count; i++) {
struct spa_source *s = data->ep[i].data;
@@ -757,24 +745,20 @@ static int loop_iterate_cancel(void *object, int timeout)
struct spa_poll_event ep[MAX_EP], *e;
int i, nfds;
uint32_t remove_count;
- struct cancellation_handler_data cdata = { impl, ep, 0, 0, 0 };
-
- spa_return_val_if_fail(impl->enter_count > 0, -EPERM);
-
- pthread_cleanup_push(cancellation_handler, &cdata);
remove_count = impl->remove_count;
spa_loop_control_hook_before(&impl->hooks_list);
- spa_assert_se((cdata.unlocked = (pthread_mutex_unlock(&impl->lock) == 0)));
+ pthread_mutex_unlock(&impl->lock);
nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, SPA_N_ELEMENTS(ep), timeout);
- spa_assert_se((cdata.locked = (pthread_mutex_lock(&impl->lock) == 0)));
+ pthread_mutex_lock(&impl->lock);
spa_loop_control_hook_after(&impl->hooks_list);
if (remove_count != impl->remove_count)
nfds = 0;
- cdata.ep_count = nfds;
+ struct cancellation_handler_data cdata = { ep, nfds };
+ pthread_cleanup_push(cancellation_handler, &cdata);
/* first we set all the rmasks, then call the callbacks. The reason is that
* some callback might also want to look at other sources it manages and
@@ -810,15 +794,13 @@ static int loop_iterate(void *object, int timeout)
int i, nfds;
uint32_t remove_count;
- spa_return_val_if_fail(impl->enter_count > 0, -EPERM);
-
remove_count = impl->remove_count;
spa_loop_control_hook_before(&impl->hooks_list);
- spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
+ pthread_mutex_unlock(&impl->lock);
nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, SPA_N_ELEMENTS(ep), timeout);
- spa_assert_se(pthread_mutex_lock(&impl->lock) == 0);
+ pthread_mutex_lock(&impl->lock);
spa_loop_control_hook_after(&impl->hooks_list);
if (remove_count != impl->remove_count)
return 0;
diff --git a/spa/plugins/support/node-driver.c b/spa/plugins/support/node-driver.c
index f0585a711..fa9cf3426 100644
--- a/spa/plugins/support/node-driver.c
+++ b/spa/plugins/support/node-driver.c
@@ -91,6 +91,7 @@ struct impl {
struct spa_io_clock *clock;
struct spa_source timer_source;
+ struct itimerspec timerspec;
int clock_fd;
bool started;
@@ -181,16 +182,13 @@ static void set_timeout(struct impl *this, uint64_t next_time)
* returning -ECANCELED.)
* If timerfd is used with a non-realtime clock, the flag is ignored.
* (Note that the flag only works in combination with SPA_FD_TIMER_ABSTIME.) */
- struct itimerspec ts;
spa_log_trace(this->log, "set timeout %"PRIu64, next_time);
- ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
- ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
- ts.it_interval.tv_sec = 0;
- ts.it_interval.tv_nsec = 0;
+ this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
+ this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
spa_system_timerfd_settime(this->data_system,
this->timer_source.fd, SPA_FD_TIMER_ABSTIME |
- SPA_FD_TIMER_CANCEL_ON_SET, &ts, NULL);
+ SPA_FD_TIMER_CANCEL_ON_SET, &this->timerspec, NULL);
}
static inline uint64_t gettime_nsec(struct impl *this, clockid_t clock_id)
@@ -1045,6 +1043,10 @@ impl_init(const struct spa_handle_factory *factory,
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ this->timerspec.it_interval.tv_sec = 0;
+ this->timerspec.it_interval.tv_nsec = 0;
spa_loop_add_source(this->data_loop, &this->timer_source);
diff --git a/spa/plugins/support/null-audio-sink.c b/spa/plugins/support/null-audio-sink.c
index 610adf9ce..b804023b3 100644
--- a/spa/plugins/support/null-audio-sink.c
+++ b/spa/plugins/support/null-audio-sink.c
@@ -114,6 +114,7 @@ struct impl {
unsigned int started:1;
unsigned int following:1;
struct spa_source timer_source;
+ struct itimerspec timerspec;
uint64_t next_time;
};
@@ -178,15 +179,11 @@ static int impl_node_enum_params(void *object, int seq,
static void set_timeout(struct impl *this, uint64_t next_time)
{
- struct itimerspec ts;
-
spa_log_trace(this->log, "set timeout %"PRIu64, next_time);
- ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
- ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
- ts.it_interval.tv_sec = 0;
- ts.it_interval.tv_nsec = 0;
+ this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
+ this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
spa_system_timerfd_settime(this->data_system,
- this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
+ this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
}
static int set_timers(struct impl *this)
@@ -932,6 +929,10 @@ impl_init(const struct spa_handle_factory *factory,
SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ this->timerspec.it_interval.tv_sec = 0;
+ this->timerspec.it_interval.tv_nsec = 0;
spa_loop_add_source(this->data_loop, &this->timer_source);
diff --git a/spa/plugins/support/system.c b/spa/plugins/support/system.c
index cd9e1c6eb..767e0b43d 100644
--- a/spa/plugins/support/system.c
+++ b/spa/plugins/support/system.c
@@ -30,10 +30,6 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.system");
# define TFD_TIMER_CANCEL_ON_SET (1 << 1)
#endif
-SPA_STATIC_ASSERT(sizeof(struct spa_poll_event) == sizeof(struct epoll_event));
-SPA_STATIC_ASSERT(offsetof(struct spa_poll_event, events) == offsetof(struct epoll_event, events));
-SPA_STATIC_ASSERT(offsetof(struct spa_poll_event, data) == offsetof(struct epoll_event, data.ptr));
-
struct impl {
struct spa_handle handle;
struct spa_system system;
@@ -136,9 +132,16 @@ static int impl_pollfd_del(void *object, int pfd, int fd)
static int impl_pollfd_wait(void *object, int pfd,
struct spa_poll_event *ev, int n_ev, int timeout)
{
- int nfds;
- if (SPA_UNLIKELY((nfds = epoll_wait(pfd, (struct epoll_event*)ev, n_ev, timeout)) < 0))
+ struct epoll_event ep[n_ev];
+ int i, nfds;
+
+ if (SPA_UNLIKELY((nfds = epoll_wait(pfd, ep, n_ev, timeout)) < 0))
return -errno;
+
+ for (i = 0; i < nfds; i++) {
+ ev[i].events = ep[i].events;
+ ev[i].data = ep[i].data.ptr;
+ }
return nfds;
}
diff --git a/spa/plugins/test/fakesink.c b/spa/plugins/test/fakesink.c
index 31667a1de..71550300c 100644
--- a/spa/plugins/test/fakesink.c
+++ b/spa/plugins/test/fakesink.c
@@ -26,6 +26,10 @@
#define SPA_LOG_TOPIC_DEFAULT &log_topic
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.fakesink");
+struct props {
+ bool live;
+};
+
#define MAX_BUFFERS 16
#define MAX_PORTS 1
@@ -64,11 +68,13 @@ struct impl {
uint64_t info_all;
struct spa_node_info info;
struct spa_param_info params[1];
+ struct props props;
struct spa_hook_list hooks;
struct spa_callbacks callbacks;
struct spa_source timer_source;
+ struct itimerspec timerspec;
bool started;
uint64_t start_time;
@@ -81,6 +87,13 @@ struct impl {
#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS)
+#define DEFAULT_LIVE false
+
+static void reset_props(struct impl *this, struct props *props)
+{
+ props->live = DEFAULT_LIVE;
+}
+
static int impl_node_enum_params(void *object, int seq,
uint32_t id, uint32_t start, uint32_t num,
const struct spa_pod *filter)
@@ -103,6 +116,14 @@ static int impl_node_enum_params(void *object, int seq,
spa_pod_builder_init(&b, buffer, sizeof(buffer));
switch (id) {
+ case SPA_PARAM_Props:
+ if (result.index > 0)
+ return 0;
+
+ param = spa_pod_builder_add_object(&b,
+ SPA_TYPE_OBJECT_Props, id,
+ SPA_PROP_live, SPA_POD_Bool(this->props.live));
+ break;
default:
return -ENOENT;
}
@@ -130,7 +151,26 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
spa_return_val_if_fail(this != NULL, -EINVAL);
+
switch (id) {
+ case SPA_PARAM_Props:
+ {
+ struct port *port = &this->port;
+
+ if (param == NULL) {
+ reset_props(this, &this->props);
+ return 0;
+ }
+ spa_pod_parse_object(param,
+ SPA_TYPE_OBJECT_Props, NULL,
+ SPA_PROP_live, SPA_POD_OPT_Bool(&this->props.live));
+
+ if (this->props.live)
+ port->info.flags |= SPA_PORT_FLAG_LIVE;
+ else
+ port->info.flags &= ~SPA_PORT_FLAG_LIVE;
+ break;
+ }
default:
return -ENOENT;
}
@@ -139,15 +179,23 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
static void set_timer(struct impl *this, bool enabled)
{
- struct itimerspec ts = {0};
-
- if (enabled) {
- uint64_t next_time = this->start_time + this->elapsed_time;
- ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
- ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
+ if (this->callbacks.funcs || this->props.live) {
+ if (enabled) {
+ if (this->props.live) {
+ uint64_t next_time = this->start_time + this->elapsed_time;
+ this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
+ this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
+ } else {
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 1;
+ }
+ } else {
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ }
+ spa_system_timerfd_settime(this->data_system,
+ this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
}
-
- spa_system_timerfd_settime(this->data_system, this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
}
static inline int read_timer(struct impl *this)
@@ -155,12 +203,14 @@ static inline int read_timer(struct impl *this)
uint64_t expirations;
int res = 0;
- if ((res = spa_system_timerfd_read(this->data_system, this->timer_source.fd, &expirations)) < 0) {
- if (res != -EAGAIN)
- spa_log_error(this->log, "%p: timerfd error: %s",
- this, spa_strerror(res));
+ if (this->callbacks.funcs || this->props.live) {
+ if ((res = spa_system_timerfd_read(this->data_system,
+ this->timer_source.fd, &expirations)) < 0) {
+ if (res != -EAGAIN)
+ spa_log_error(this->log, "%p: timerfd error: %s",
+ this, spa_strerror(res));
+ }
}
-
return res;
}
@@ -248,7 +298,10 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
return 0;
clock_gettime(CLOCK_MONOTONIC, &now);
- this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
+ if (this->props.live)
+ this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
+ else
+ this->start_time = 0;
this->buffer_count = 0;
this->elapsed_time = 0;
@@ -598,8 +651,10 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
io->status = SPA_STATUS_OK;
}
-
- return SPA_STATUS_OK;
+ if (this->callbacks.funcs == NULL)
+ return consume_buffer(this);
+ else
+ return SPA_STATUS_OK;
}
static const struct spa_node_methods impl_node = {
@@ -703,6 +758,8 @@ impl_init(const struct spa_handle_factory *factory,
this->params[0] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
this->info.params = this->params;
this->info.n_params = 1;
+ reset_props(this, &this->props);
+
this->timer_source.func = on_input;
this->timer_source.data = this;
@@ -710,6 +767,10 @@ impl_init(const struct spa_handle_factory *factory,
SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ this->timerspec.it_interval.tv_sec = 0;
+ this->timerspec.it_interval.tv_nsec = 0;
if (this->data_loop)
spa_loop_add_source(this->data_loop, &this->timer_source);
@@ -718,7 +779,9 @@ impl_init(const struct spa_handle_factory *factory,
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
SPA_PORT_CHANGE_MASK_PARAMS;
port->info = SPA_PORT_INFO_INIT();
- port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_LIVE;
+ port->info.flags = SPA_PORT_FLAG_NO_REF;
+ if (this->props.live)
+ port->info.flags |= SPA_PORT_FLAG_LIVE;
port->params[0] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
port->params[1] = SPA_PARAM_INFO(SPA_PARAM_IO, 0);
port->params[2] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
diff --git a/spa/plugins/test/fakesrc.c b/spa/plugins/test/fakesrc.c
index db28d9c3c..28b37dab4 100644
--- a/spa/plugins/test/fakesrc.c
+++ b/spa/plugins/test/fakesrc.c
@@ -27,6 +27,7 @@
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.fakesrc");
struct props {
+ bool live;
uint32_t pattern;
};
@@ -74,6 +75,7 @@ struct impl {
struct spa_callbacks callbacks;
struct spa_source timer_source;
+ struct itimerspec timerspec;
bool started;
uint64_t start_time;
@@ -87,10 +89,12 @@ struct impl {
#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) < MAX_PORTS)
+#define DEFAULT_LIVE false
#define DEFAULT_PATTERN 0
static void reset_props(struct impl *this, struct props *props)
{
+ props->live = DEFAULT_LIVE;
props->pattern = DEFAULT_PATTERN;
}
@@ -125,6 +129,7 @@ static int impl_node_enum_params(void *object, int seq,
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, id,
+ SPA_PROP_live, SPA_POD_Bool(p->live),
SPA_PROP_patternType, SPA_POD_CHOICE_ENUM_Int(2, p->pattern, p->pattern));
break;
}
@@ -159,6 +164,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
case SPA_PARAM_Props:
{
struct props *p = &this->props;
+ struct port *port = &this->port;
if (param == NULL) {
reset_props(this, p);
@@ -166,7 +172,13 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
}
spa_pod_parse_object(param,
SPA_TYPE_OBJECT_Props, NULL,
+ SPA_PROP_live, SPA_POD_OPT_Bool(&p->live),
SPA_PROP_patternType, SPA_POD_OPT_Int(&p->pattern));
+
+ if (p->live)
+ port->info.flags |= SPA_PORT_FLAG_LIVE;
+ else
+ port->info.flags &= ~SPA_PORT_FLAG_LIVE;
break;
}
default:
@@ -182,15 +194,23 @@ static int fill_buffer(struct impl *this, struct buffer *b)
static void set_timer(struct impl *this, bool enabled)
{
- struct itimerspec ts = {0};
-
- if (enabled) {
- uint64_t next_time = this->start_time + this->elapsed_time;
- ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
- ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
+ if (this->callbacks.funcs || this->props.live) {
+ if (enabled) {
+ if (this->props.live) {
+ uint64_t next_time = this->start_time + this->elapsed_time;
+ this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
+ this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
+ } else {
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 1;
+ }
+ } else {
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ }
+ spa_system_timerfd_settime(this->data_system,
+ this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
}
-
- spa_system_timerfd_settime(this->data_system, this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
}
static inline int read_timer(struct impl *this)
@@ -198,12 +218,14 @@ static inline int read_timer(struct impl *this)
uint64_t expirations;
int res = 0;
- if ((res = spa_system_timerfd_read(this->data_system, this->timer_source.fd, &expirations)) < 0) {
- if (res != -EAGAIN)
- spa_log_error(this->log, "%p: timerfd error: %s",
- this, spa_strerror(res));
+ if (this->callbacks.funcs || this->props.live) {
+ if ((res = spa_system_timerfd_read(this->data_system,
+ this->timer_source.fd, &expirations)) < 0) {
+ if (res != -EAGAIN)
+ spa_log_error(this->log, "%p: timerfd error: %s",
+ this, spa_strerror(res));
+ }
}
-
return res;
}
@@ -289,7 +311,10 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
return 0;
clock_gettime(CLOCK_MONOTONIC, &now);
- this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
+ if (this->props.live)
+ this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
+ else
+ this->start_time = 0;
this->buffer_count = 0;
this->elapsed_time = 0;
@@ -657,7 +682,10 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
}
- return SPA_STATUS_OK;
+ if (this->callbacks.funcs == NULL)
+ return make_buffer(this);
+ else
+ return SPA_STATUS_OK;
}
static const struct spa_node_methods impl_node = {
@@ -769,6 +797,10 @@ impl_init(const struct spa_handle_factory *factory,
SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ this->timerspec.it_interval.tv_sec = 0;
+ this->timerspec.it_interval.tv_nsec = 0;
if (this->data_loop)
spa_loop_add_source(this->data_loop, &this->timer_source);
@@ -777,7 +809,9 @@ impl_init(const struct spa_handle_factory *factory,
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
SPA_PORT_CHANGE_MASK_PARAMS;
port->info = SPA_PORT_INFO_INIT();
- port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_LIVE;
+ port->info.flags = SPA_PORT_FLAG_NO_REF;
+ if (this->props.live)
+ port->info.flags |= SPA_PORT_FLAG_LIVE;
port->params[0] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
port->params[1] = SPA_PARAM_INFO(SPA_PARAM_IO, 0);
port->params[2] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
diff --git a/spa/plugins/videotestsrc/videotestsrc.c b/spa/plugins/videotestsrc/videotestsrc.c
index a8d7059c8..db5c90113 100644
--- a/spa/plugins/videotestsrc/videotestsrc.c
+++ b/spa/plugins/videotestsrc/videotestsrc.c
@@ -35,14 +35,17 @@ enum pattern {
PATTERN_SNOW,
};
+#define DEFAULT_LIVE true
#define DEFAULT_PATTERN PATTERN_SMPTE_SNOW
struct props {
+ bool live;
uint32_t pattern;
};
static void reset_props(struct props *props)
{
+ props->live = DEFAULT_LIVE;
props->pattern = DEFAULT_PATTERN;
}
@@ -94,7 +97,9 @@ struct impl {
struct spa_hook_list hooks;
struct spa_callbacks callbacks;
+ bool async;
struct spa_source *timer_source;
+ struct itimerspec timerspec;
bool started;
uint64_t start_time;
@@ -136,6 +141,13 @@ static int impl_node_enum_params(void *object, int seq,
switch (result.index) {
case 0:
+ param = spa_pod_builder_add_object(&b,
+ SPA_TYPE_OBJECT_PropInfo, id,
+ SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_live),
+ SPA_PROP_INFO_description, SPA_POD_String("Configure live mode of the source"),
+ SPA_PROP_INFO_type, SPA_POD_Bool(p->live));
+ break;
+ case 1:
spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(&b,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_patternType),
@@ -164,6 +176,7 @@ static int impl_node_enum_params(void *object, int seq,
case 0:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, id,
+ SPA_PROP_live, SPA_POD_Bool(p->live),
SPA_PROP_patternType, SPA_POD_Int(p->pattern));
break;
default:
@@ -218,6 +231,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
case SPA_PARAM_Props:
{
struct props *p = &this->props;
+ struct port *port = &this->port;
if (param == NULL) {
reset_props(p);
@@ -225,8 +239,13 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
}
spa_pod_parse_object(param,
SPA_TYPE_OBJECT_Props, NULL,
+ SPA_PROP_live, SPA_POD_OPT_Bool(&p->live),
SPA_PROP_patternType, SPA_POD_OPT_Int(&p->pattern));
+ if (p->live)
+ port->info.flags |= SPA_PORT_FLAG_LIVE;
+ else
+ port->info.flags &= ~SPA_PORT_FLAG_LIVE;
break;
}
default:
@@ -244,15 +263,22 @@ static int fill_buffer(struct impl *this, struct buffer *b)
static void set_timer(struct impl *this, bool enabled)
{
- struct timespec ts = {0};
-
- if (enabled) {
- uint64_t next_time = this->start_time + this->elapsed_time;
- ts.tv_sec = next_time / SPA_NSEC_PER_SEC;
- ts.tv_nsec = next_time % SPA_NSEC_PER_SEC;
+ if (this->async || this->props.live) {
+ if (enabled) {
+ if (this->props.live) {
+ uint64_t next_time = this->start_time + this->elapsed_time;
+ this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
+ this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
+ } else {
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 1;
+ }
+ } else {
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ }
+ spa_loop_utils_update_timer(this->loop_utils, this->timer_source, &this->timerspec.it_value, &this->timerspec.it_interval, true);
}
-
- spa_loop_utils_update_timer(this->loop_utils, this->timer_source, &ts, NULL, true);
}
static int make_buffer(struct impl *this)
@@ -332,7 +358,10 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
return 0;
clock_gettime(CLOCK_MONOTONIC, &now);
- this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
+ if (this->props.live)
+ this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
+ else
+ this->start_time = 0;
this->frame_count = 0;
this->elapsed_time = 0;
@@ -726,6 +755,9 @@ static inline void reuse_buffer(struct impl *this, struct port *port, uint32_t i
b->outstanding = false;
spa_list_append(&port->empty, &b->link);
+
+ if (!this->props.live)
+ set_timer(this, true);
}
static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
@@ -763,7 +795,10 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
}
- return SPA_STATUS_OK;
+ if (!this->props.live)
+ return make_buffer(this);
+ else
+ return SPA_STATUS_OK;
}
static const struct spa_node_methods impl_node = {
@@ -872,12 +907,18 @@ impl_init(const struct spa_handle_factory *factory,
reset_props(&this->props);
this->timer_source = spa_loop_utils_add_timer(this->loop_utils, on_output, this);
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ this->timerspec.it_interval.tv_sec = 0;
+ this->timerspec.it_interval.tv_nsec = 0;
port = &this->port;
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
SPA_PORT_CHANGE_MASK_PARAMS;
port->info = SPA_PORT_INFO_INIT();
- port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_LIVE;
+ port->info.flags = SPA_PORT_FLAG_NO_REF;
+ if (this->props.live)
+ port->info.flags |= SPA_PORT_FLAG_LIVE;
port->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
port->params[1] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
port->params[2] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
diff --git a/spa/plugins/vulkan/vulkan-blit-utils.c b/spa/plugins/vulkan/vulkan-blit-utils.c
index e0bd6cfe5..f8a60497e 100644
--- a/spa/plugins/vulkan/vulkan-blit-utils.c
+++ b/spa/plugins/vulkan/vulkan-blit-utils.c
@@ -12,10 +12,8 @@
#include
#include
#include
-#ifdef __linux__
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
#include
-#else
-#include
#endif
#include
#include
diff --git a/spa/plugins/vulkan/vulkan-compute-source.c b/spa/plugins/vulkan/vulkan-compute-source.c
index aa6f4a60f..1daf3b47c 100644
--- a/spa/plugins/vulkan/vulkan-compute-source.c
+++ b/spa/plugins/vulkan/vulkan-compute-source.c
@@ -33,6 +33,17 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.vulkan.compute-source");
#define FRAMES_TO_TIME(this,f) ((this->position->video.framerate.denom * (f) * SPA_NSEC_PER_SEC) / \
(this->position->video.framerate.num))
+#define DEFAULT_LIVE true
+
+struct props {
+ bool live;
+};
+
+static void reset_props(struct props *props)
+{
+ props->live = DEFAULT_LIVE;
+}
+
struct buffer {
uint32_t id;
#define BUFFER_FLAG_OUT (1<<0)
@@ -84,11 +95,14 @@ struct impl {
#define IDX_Props 1
#define N_NODE_PARAMS 2
struct spa_param_info params[N_NODE_PARAMS];
+ struct props props;
struct spa_hook_list hooks;
struct spa_callbacks callbacks;
+ bool async;
struct spa_source timer_source;
+ struct itimerspec timerspec;
bool started;
uint64_t start_time;
@@ -124,6 +138,38 @@ static int impl_node_enum_params(void *object, int seq,
spa_pod_builder_init(&b, buffer, sizeof(buffer));
switch (id) {
+ case SPA_PARAM_PropInfo:
+ {
+ struct props *p = &this->props;
+
+ switch (result.index) {
+ case 0:
+ param = spa_pod_builder_add_object(&b,
+ SPA_TYPE_OBJECT_PropInfo, id,
+ SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_live),
+ SPA_PROP_INFO_description, SPA_POD_String("Configure live mode of the source"),
+ SPA_PROP_INFO_type, SPA_POD_Bool(p->live));
+ break;
+ default:
+ return 0;
+ }
+ break;
+ }
+ case SPA_PARAM_Props:
+ {
+ struct props *p = &this->props;
+
+ switch (result.index) {
+ case 0:
+ param = spa_pod_builder_add_object(&b,
+ SPA_TYPE_OBJECT_Props, id,
+ SPA_PROP_live, SPA_POD_Bool(p->live));
+ break;
+ default:
+ return 0;
+ }
+ break;
+ }
default:
return -ENOENT;
}
@@ -168,6 +214,25 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
spa_return_val_if_fail(this != NULL, -EINVAL);
switch (id) {
+ case SPA_PARAM_Props:
+ {
+ struct props *p = &this->props;
+ struct port *port = &this->port;
+
+ if (param == NULL) {
+ reset_props(p);
+ return 0;
+ }
+ spa_pod_parse_object(param,
+ SPA_TYPE_OBJECT_Props, NULL,
+ SPA_PROP_live, SPA_POD_OPT_Bool(&p->live));
+
+ if (p->live)
+ port->info.flags |= SPA_PORT_FLAG_LIVE;
+ else
+ port->info.flags &= ~SPA_PORT_FLAG_LIVE;
+ break;
+ }
default:
return -ENOENT;
}
@@ -177,15 +242,23 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
static void set_timer(struct impl *this, bool enabled)
{
- struct itimerspec ts = {0};
-
- if (enabled) {
- uint64_t next_time = this->start_time + this->elapsed_time;
- ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
- ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
+ if (this->async || this->props.live) {
+ if (enabled) {
+ if (this->props.live) {
+ uint64_t next_time = this->start_time + this->elapsed_time;
+ this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
+ this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
+ } else {
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 1;
+ }
+ } else {
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ }
+ spa_system_timerfd_settime(this->data_system,
+ this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
}
-
- spa_system_timerfd_settime(this->data_system, this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
}
static int read_timer(struct impl *this)
@@ -193,12 +266,14 @@ static int read_timer(struct impl *this)
uint64_t expirations;
int res = 0;
- if ((res = spa_system_timerfd_read(this->data_system, this->timer_source.fd, &expirations)) < 0) {
- if (res != -EAGAIN)
- spa_log_error(this->log, "%p: timerfd error: %s",
- this, spa_strerror(res));
+ if (this->async || this->props.live) {
+ if ((res = spa_system_timerfd_read(this->data_system,
+ this->timer_source.fd, &expirations)) < 0) {
+ if (res != -EAGAIN)
+ spa_log_error(this->log, "%p: timerfd error: %s",
+ this, spa_strerror(res));
+ }
}
-
return res;
}
@@ -273,6 +348,9 @@ static inline void reuse_buffer(struct impl *this, struct port *port, uint32_t i
SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
spa_list_append(&port->empty, &b->link);
+
+ if (!this->props.live)
+ set_timer(this, true);
}
}
@@ -331,7 +409,10 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
return 0;
clock_gettime(CLOCK_MONOTONIC, &now);
- this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
+ if (this->props.live)
+ this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
+ else
+ this->start_time = 0;
this->frame_count = 0;
this->elapsed_time = 0;
@@ -793,7 +874,10 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
}
- return SPA_STATUS_OK;
+ if (!this->props.live)
+ return make_buffer(this);
+ else
+ return SPA_STATUS_OK;
}
static const struct spa_node_methods impl_node = {
@@ -901,6 +985,7 @@ impl_init(const struct spa_handle_factory *factory,
this->params[IDX_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
this->info.params = this->params;
this->info.n_params = N_NODE_PARAMS;
+ reset_props(&this->props);
this->timer_source.func = on_output;
this->timer_source.data = this;
@@ -908,6 +993,10 @@ impl_init(const struct spa_handle_factory *factory,
SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
+ this->timerspec.it_value.tv_sec = 0;
+ this->timerspec.it_value.tv_nsec = 0;
+ this->timerspec.it_interval.tv_sec = 0;
+ this->timerspec.it_interval.tv_nsec = 0;
if (this->data_loop)
spa_loop_add_source(this->data_loop, &this->timer_source);
@@ -917,7 +1006,9 @@ impl_init(const struct spa_handle_factory *factory,
SPA_PORT_CHANGE_MASK_PARAMS |
SPA_PORT_CHANGE_MASK_PROPS;
port->info = SPA_PORT_INFO_INIT();
- port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_LIVE;
+ port->info.flags = SPA_PORT_FLAG_NO_REF;
+ if (this->props.live)
+ port->info.flags |= SPA_PORT_FLAG_LIVE;
port->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
port->params[IDX_Meta] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
diff --git a/spa/plugins/vulkan/vulkan-compute-utils.c b/spa/plugins/vulkan/vulkan-compute-utils.c
index 503542483..49705bee8 100644
--- a/spa/plugins/vulkan/vulkan-compute-utils.c
+++ b/spa/plugins/vulkan/vulkan-compute-utils.c
@@ -11,10 +11,8 @@
#include
#include
#include
-#ifdef __linux__
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
#include
-#else
-#include
#endif
#include
#include
diff --git a/spa/plugins/vulkan/vulkan-utils.c b/spa/plugins/vulkan/vulkan-utils.c
index 88a230dd6..6a0f693dc 100644
--- a/spa/plugins/vulkan/vulkan-utils.c
+++ b/spa/plugins/vulkan/vulkan-utils.c
@@ -11,10 +11,8 @@
#include
#include
#include
-#ifdef __linux__
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
#include
-#else
-#include
#endif
#include
#include
diff --git a/spa/tests/benchmark-aec.c b/spa/tests/benchmark-aec.c
index 37d4a8375..3ac0fc62e 100644
--- a/spa/tests/benchmark-aec.c
+++ b/spa/tests/benchmark-aec.c
@@ -6,6 +6,7 @@
#include
#include
+#include
#include
#include
#include
diff --git a/spa/tools/spa-json-dump.c b/spa/tools/spa-json-dump.c
index ed81330d4..ee3b42da7 100644
--- a/spa/tools/spa-json-dump.c
+++ b/spa/tools/spa-json-dump.c
@@ -15,28 +15,27 @@
#include
#include
-#include
#include
#define DEFAULT_INDENT 2
struct data {
const char *filename;
-
- FILE *out;
- struct spa_json_builder builder;
+ FILE *file;
void *data;
size_t size;
+
+ int indent;
+ bool simple_string;
+ const char *comma;
+ const char *key_sep;
};
-#define OPTIONS "hNC:Ri:s"
+#define OPTIONS "hi:s"
static const struct option long_options[] = {
{ "help", no_argument, NULL, 'h'},
- { "no-colors", no_argument, NULL, 'N' },
- { "color", optional_argument, NULL, 'C' },
- { "raw", no_argument, NULL, 'R' },
{ "indent", required_argument, NULL, 'i' },
{ "spa", no_argument, NULL, 's' },
@@ -54,21 +53,72 @@ static void show_usage(struct data *d, const char *name, bool is_error)
" -h, --help Show this help\n"
"\n");
fprintf(fp,
- " -N, --no-colors disable color output\n"
- " -C, --color[=WHEN] whether to enable color support. WHEN is `never`, `always`, or `auto`\n"
- " -R, --raw force raw output\n"
" -i --indent set indent (default %d)\n"
" -s --spa use simplified SPA JSON\n"
"\n",
DEFAULT_INDENT);
}
-static int dump(struct data *d, struct spa_json *it, const char *key, const char *value, int len)
+#define REJECT "\"\\'=:,{}[]()#"
+
+static bool is_simple_string(const char *val, int len)
{
- struct spa_json_builder *b = &d->builder;
+ int i;
+ for (i = 0; i < len; i++) {
+ if (val[i] < 0x20 || strchr(REJECT, val[i]) != NULL)
+ return false;
+ }
+ return true;
+}
+
+static void encode_string(struct data *d, const char *val, int len)
+{
+ FILE *f = d->file;
+ int i;
+ if (d->simple_string && is_simple_string(val, len)) {
+ fprintf(f, "%.*s", len, val);
+ return;
+ }
+ fprintf(f, "\"");
+ for (i = 0; i < len; i++) {
+ char v = val[i];
+ switch (v) {
+ case '\n':
+ fprintf(f, "\\n");
+ break;
+ case '\r':
+ fprintf(f, "\\r");
+ break;
+ case '\b':
+ fprintf(f, "\\b");
+ break;
+ case '\t':
+ fprintf(f, "\\t");
+ break;
+ case '\f':
+ fprintf(f, "\\f");
+ break;
+ case '\\': case '"':
+ fprintf(f, "\\%c", v);
+ break;
+ default:
+ if (v > 0 && v < 0x20)
+ fprintf(f, "\\u%04x", v);
+ else
+ fprintf(f, "%c", v);
+ break;
+ }
+ }
+ fprintf(f, "\"");
+}
+
+static int dump(struct data *d, int indent, struct spa_json *it, const char *value, int len)
+{
+ FILE *file = d->file;
struct spa_json sub;
bool toplevel = false;
- int res;
+ int count = 0, res;
+ char key[1024];
if (!value) {
toplevel = true;
@@ -77,22 +127,29 @@ static int dump(struct data *d, struct spa_json *it, const char *key, const char
}
if (spa_json_is_array(value, len)) {
- spa_json_builder_object_push(b, key, "[");
+ fprintf(file, "[");
spa_json_enter(it, &sub);
while ((len = spa_json_next(&sub, &value)) > 0) {
- if ((res = dump(d, &sub, NULL, value, len)) < 0)
+ fprintf(file, "%s\n%*s", count++ > 0 ? d->comma : "",
+ indent+d->indent, "");
+ if ((res = dump(d, indent+d->indent, &sub, value, len)) < 0)
return res;
}
- spa_json_builder_pop(b, "]");
+ fprintf(file, "%s%*s]", count > 0 ? "\n" : "",
+ count > 0 ? indent : 0, "");
} else if (spa_json_is_object(value, len)) {
- char k[1024];
- spa_json_builder_object_push(b, key, "{");
+ fprintf(file, "{");
if (!toplevel)
spa_json_enter(it, &sub);
else
sub = *it;
- while ((len = spa_json_object_next(&sub, k, sizeof(k), &value)) > 0) {
- res = dump(d, &sub, k, value, len);
+ while ((len = spa_json_object_next(&sub, key, sizeof(key), &value)) > 0) {
+ fprintf(file, "%s\n%*s",
+ count++ > 0 ? d->comma : "",
+ indent+d->indent, "");
+ encode_string(d, key, strlen(key));
+ fprintf(file, "%s ", d->key_sep);
+ res = dump(d, indent+d->indent, &sub, value, len);
if (res < 0) {
if (toplevel)
*it = sub;
@@ -101,10 +158,18 @@ static int dump(struct data *d, struct spa_json *it, const char *key, const char
}
if (toplevel)
*it = sub;
- spa_json_builder_pop(b, "}");
+ fprintf(file, "%s%*s}", count > 0 ? "\n" : "",
+ count > 0 ? indent : 0, "");
+ } else if (spa_json_is_string(value, len) ||
+ spa_json_is_null(value, len) ||
+ spa_json_is_bool(value, len) ||
+ spa_json_is_int(value, len) ||
+ spa_json_is_float(value, len)) {
+ fprintf(file, "%.*s", len, value);
} else {
- spa_json_builder_add_simple(b, key, INT_MAX, 0, value, len);
+ encode_string(d, value, len);
}
+
if (spa_json_get_error(it, NULL, NULL))
return -EINVAL;
@@ -127,12 +192,12 @@ static int process_json(struct data *d)
len = 0;
}
- res = dump(d, &it, NULL, value, len);
+ res = dump(d, 0, &it, value, len);
if (spa_json_next(&it, &value) < 0)
res = -EINVAL;
- fprintf(d->builder.f, "\n");
- fflush(d->builder.f);
+ fprintf(d->file, "\n");
+ fflush(d->file);
if (res < 0) {
struct spa_error_location loc;
@@ -192,50 +257,30 @@ int main(int argc, char *argv[])
int c;
int longopt_index = 0;
int fd, res, exit_code = EXIT_FAILURE;
- int flags = 0, indent = -1;
struct data d;
struct stat sbuf;
- bool raw = false, colors = false;
spa_zero(d);
+ d.file = stdout;
d.filename = "-";
- d.out = stdout;
-
- if (getenv("NO_COLOR") == NULL && isatty(fileno(d.out)))
- colors = true;
- setlinebuf(d.out);
+ d.simple_string = false;
+ d.comma = ",";
+ d.key_sep = ":";
+ d.indent = DEFAULT_INDENT;
while ((c = getopt_long(argc, argv, OPTIONS, long_options, &longopt_index)) != -1) {
switch (c) {
case 'h' :
show_usage(&d, argv[0], false);
return 0;
- case 'N' :
- colors = false;
- break;
- case 'C' :
- if (optarg == NULL || !strcmp(optarg, "auto"))
- break; /* nothing to do, tty detection was done
- before parsing options */
- else if (!strcmp(optarg, "never"))
- colors = false;
- else if (!strcmp(optarg, "always"))
- colors = true;
- else {
- fprintf(stderr, "Unknown color: %s\n", optarg);
- show_usage(&d, argv[0], true);
- return -1;
- }
- break;
- case 'R':
- raw = true;
- break;
case 'i':
- indent = atoi(optarg);
+ d.indent = atoi(optarg);
break;
case 's':
- flags |= SPA_JSON_BUILDER_FLAG_SIMPLE;
+ d.simple_string = true;
+ d.comma = "";
+ d.key_sep = " =";
break;
default:
show_usage(&d, argv[0], true);
@@ -263,15 +308,6 @@ int main(int argc, char *argv[])
}
d.size = sbuf.st_size;
- if (!raw)
- flags |= SPA_JSON_BUILDER_FLAG_PRETTY;
- if (colors)
- flags |= SPA_JSON_BUILDER_FLAG_COLOR;
-
- spa_json_builder_file(&d.builder, d.out, flags);
- if (indent >= 0)
- d.builder.indent = indent;
-
res = process_json(&d);
if (res < 0)
exit_code = EXIT_FAILURE;
diff --git a/src/daemon/client.conf.in b/src/daemon/client.conf.in
index e0baeda92..46874af93 100644
--- a/src/daemon/client.conf.in
+++ b/src/daemon/client.conf.in
@@ -101,9 +101,6 @@ stream.properties = {
#channelmix.lfe-cutoff = 150
#channelmix.fc-cutoff = 12000
#channelmix.rear-delay = 12.0
- #channelmix.center-level = 0.707106781
- #channelmix.surround-level = 0.707106781
- #channelmix.lfe-level = 0.5
#channelmix.stereo-widen = 0.0
#channelmix.hilbert-taps = 0
#dither.noise = 0
diff --git a/src/daemon/filter-chain/source-rnnoise.conf b/src/daemon/filter-chain/source-rnnoise.conf
index 83142dd29..f3c2c71be 100644
--- a/src/daemon/filter-chain/source-rnnoise.conf
+++ b/src/daemon/filter-chain/source-rnnoise.conf
@@ -21,6 +21,7 @@ context.modules = [
# listed in the environment variable LADSPA_PATH or
# /usr/lib64/ladspa, /usr/lib/ladspa or the system library directory
# as a fallback.
+ # You might want to use an absolute path here to avoid problems.
plugin = "librnnoise_ladspa"
label = noise_suppressor_stereo
control = {
diff --git a/src/daemon/minimal.conf.in b/src/daemon/minimal.conf.in
index 7ab93e92b..82647e9ca 100644
--- a/src/daemon/minimal.conf.in
+++ b/src/daemon/minimal.conf.in
@@ -100,10 +100,6 @@ context.modules = [
}
flags = [ ifexists nofail ]
}
- # the graph scheduler
- { name = libpipewire-module-scheduler-v1
- condition = [ { module.scheduler-v1 = !false } ]
- }
# The native communication protocol.
{ name = libpipewire-module-protocol-native }
@@ -332,9 +328,6 @@ context.objects = [
#channelmix.fc-cutoff = 12000
#channelmix.rear-delay = 12.0
#channelmix.stereo-widen = 0.0
- #channelmix.center-level = 0.707106781
- #channelmix.surround-level = 0.707106781
- #channelmix.lfe-level = 0.5
#channelmix.hilbert-taps = 0
#channelmix.disable = false
#dither.noise = 0
diff --git a/src/daemon/pipewire-avb.conf.in b/src/daemon/pipewire-avb.conf.in
index e43015780..da158f212 100644
--- a/src/daemon/pipewire-avb.conf.in
+++ b/src/daemon/pipewire-avb.conf.in
@@ -60,9 +60,6 @@ stream.properties = {
#channelmix.fc-cutoff = 6000
#channelmix.rear-delay = 12.0
#channelmix.stereo-widen = 0.1
- #channelmix.center-level = 0.707106781
- #channelmix.surround-level = 0.707106781
- #channelmix.lfe-level = 0.5
#channelmix.hilbert-taps = 0
}
diff --git a/src/daemon/pipewire-pulse.conf.in b/src/daemon/pipewire-pulse.conf.in
index db646db0b..8c21e37df 100644
--- a/src/daemon/pipewire-pulse.conf.in
+++ b/src/daemon/pipewire-pulse.conf.in
@@ -88,9 +88,6 @@ stream.properties = {
#channelmix.fc-cutoff = 12000
#channelmix.rear-delay = 12.0
#channelmix.stereo-widen = 0.0
- #channelmix.center-level = 0.707106781
- #channelmix.surround-level = 0.707106781
- #channelmix.lfe-level = 0.5
#channelmix.hilbert-taps = 0
#dither.noise = 0
}
diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in
index a9142cede..c3eb7120f 100644
--- a/src/daemon/pipewire.conf.in
+++ b/src/daemon/pipewire.conf.in
@@ -121,10 +121,6 @@ context.modules = [
flags = [ ifexists nofail ]
condition = [ { module.rt = !false } ]
}
- # the graph scheduler
- { name = libpipewire-module-scheduler-v1
- condition = [ { module.scheduler-v1 = !false } ]
- }
# The native communication protocol.
{ name = libpipewire-module-protocol-native
diff --git a/src/examples/audio-dsp-filter.c b/src/examples/audio-dsp-filter.c
index 589646d61..8a43147a6 100644
--- a/src/examples/audio-dsp-filter.c
+++ b/src/examples/audio-dsp-filter.c
@@ -108,7 +108,6 @@ int main(int argc, char *argv[])
PW_KEY_MEDIA_TYPE, "Audio",
PW_KEY_MEDIA_CATEGORY, "Filter",
PW_KEY_MEDIA_ROLE, "DSP",
- PW_KEY_NODE_PASSIVE, "follow",
NULL),
&filter_events,
&data);
diff --git a/src/examples/audio-dsp-src.c b/src/examples/audio-dsp-src.c
index 0ef4a0e53..135d2e27e 100644
--- a/src/examples/audio-dsp-src.c
+++ b/src/examples/audio-dsp-src.c
@@ -18,6 +18,7 @@
#define M_PI_M2f (float)(M_PI+M_PI)
+#define DEFAULT_RATE 44100
#define DEFAULT_FREQ 440
#define DEFAULT_VOLUME 0.7f
@@ -60,9 +61,7 @@ static void on_process(void *userdata, struct spa_io_position *position)
return;
for (i = 0; i < n_samples; i++) {
- out_port->accumulator += M_PI_M2f * DEFAULT_FREQ *
- position->clock.rate.num / position->clock.rate.denom;
-
+ out_port->accumulator += M_PI_M2f * DEFAULT_FREQ / DEFAULT_RATE;
if (out_port->accumulator >= M_PI_M2f)
out_port->accumulator -= M_PI_M2f;
diff --git a/src/examples/video-src-alloc.c b/src/examples/video-src-alloc.c
index c89ce8d78..add9597ed 100644
--- a/src/examples/video-src-alloc.c
+++ b/src/examples/video-src-alloc.c
@@ -192,11 +192,7 @@ static void on_stream_state_changed(void *_data, enum pw_stream_state old, enum
interval.tv_sec = 0;
interval.tv_nsec = 40 * SPA_NSEC_PER_MSEC;
- printf("driving:%d lazy:%d\n",
- pw_stream_is_driving(data->stream),
- pw_stream_is_lazy(data->stream));
-
- if (pw_stream_is_driving(data->stream) != pw_stream_is_lazy(data->stream))
+ if (pw_stream_is_driving(data->stream))
pw_loop_update_timer(pw_thread_loop_get_loop(data->loop),
data->timer, &timeout, &interval, false);
break;
@@ -394,7 +390,6 @@ int main(int argc, char *argv[])
"video-src-alloc",
pw_properties_new(
PW_KEY_MEDIA_CLASS, "Video/Source",
- PW_KEY_NODE_SUPPORTS_REQUEST, "1",
NULL),
&stream_events,
&data);
diff --git a/src/examples/video-src-fixate.c b/src/examples/video-src-fixate.c
index b16d810ad..6d08074de 100644
--- a/src/examples/video-src-fixate.c
+++ b/src/examples/video-src-fixate.c
@@ -18,11 +18,7 @@
#include
#include
#include
-#ifdef __linux__
#include
-#else
-#include
-#endif
#include
#include
diff --git a/src/gst/gstpipewiresrc.c b/src/gst/gstpipewiresrc.c
index 3c57028b4..6ca9599d6 100644
--- a/src/gst/gstpipewiresrc.c
+++ b/src/gst/gstpipewiresrc.c
@@ -839,17 +839,6 @@ static GstBuffer *dequeue_buffer(GstPipeWireSrc *pwsrc)
video_size += d->chunk->size;
}
-
- /* If the buffer number is smaller than the plane number,
- * update the stride and offset for the remaining planes.
- */
- if (n_datas && n_datas < n_planes) {
- for (i = n_datas; i < n_planes; i++) {
- meta->stride[i] = gst_video_format_info_extrapolate_stride (info->finfo, i, b->buffer->datas[0].chunk->stride);
- meta->offset[i] = meta->offset[i-1] +
- meta->stride[i-1] * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, i-1, GST_VIDEO_INFO_HEIGHT(info));
- }
- }
}
if (b->buffer->n_datas != gst_buffer_n_memory(data->buf)) {
@@ -1085,6 +1074,10 @@ wait_negotiated (GstPipeWireSrc *this)
GST_DEBUG_OBJECT (this, "waiting for NEGOTIATED, now %s", pw_stream_state_as_string (state));
if (state == PW_STREAM_STATE_ERROR)
break;
+ if (this->flushing) {
+ state = PW_STREAM_STATE_ERROR;
+ break;
+ }
if (this->negotiated)
break;
diff --git a/src/modules/meson.build b/src/modules/meson.build
index 11b29a117..59f46ae13 100644
--- a/src/modules/meson.build
+++ b/src/modules/meson.build
@@ -46,7 +46,6 @@ module_sources = [
'module-vban-recv.c',
'module-vban-send.c',
'module-session-manager.c',
- 'module-scheduler-v1.c',
'module-zeroconf-discover.c',
'module-roc-source.c',
'module-roc-sink.c',
@@ -296,17 +295,6 @@ pipewire_module_protocol_native = shared_library('pipewire-module-protocol-nativ
dependencies : pipewire_module_protocol_deps,
)
-zeroconf_sources = []
-zeroconf_deps = []
-if avahi_dep.found()
- zeroconf_sources += [
- 'zeroconf-utils/zeroconf.c',
- 'zeroconf-utils/avahi-poll.c',
- ]
- zeroconf_deps += avahi_dep
- cdata.set('HAVE_AVAHI', true)
-endif
-
pipewire_module_protocol_pulse_deps = pipewire_module_protocol_deps
pipewire_module_protocol_pulse_sources = [
@@ -383,9 +371,10 @@ endif
if avahi_dep.found()
pipewire_module_protocol_pulse_sources += [
'module-protocol-pulse/modules/module-zeroconf-publish.c',
+ 'module-zeroconf-discover/avahi-poll.c',
]
- pipewire_module_protocol_pulse_sources += zeroconf_sources
- pipewire_module_protocol_pulse_deps += zeroconf_deps
+ pipewire_module_protocol_pulse_deps += avahi_dep
+ cdata.set('HAVE_AVAHI', true)
endif
if gsettings_gio_dep.found()
@@ -543,15 +532,6 @@ pipewire_module_adapter = shared_library('pipewire-module-adapter',
dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep],
)
-pipewire_module_scheduler_v1 = shared_library('pipewire-module-scheduler-v1',
- [ 'module-scheduler-v1.c' ],
- include_directories : [configinc],
- install : true,
- install_dir : modules_install_dir,
- install_rpath: modules_install_dir,
- dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep],
-)
-
pipewire_module_session_manager = shared_library('pipewire-module-session-manager',
[ 'module-session-manager.c',
'module-session-manager/client-endpoint/client-endpoint.c',
@@ -579,12 +559,12 @@ if build_module_zeroconf_discover
pipewire_module_zeroconf_discover = shared_library('pipewire-module-zeroconf-discover',
[ 'module-zeroconf-discover.c',
'module-protocol-pulse/format.c',
- zeroconf_sources ],
+ 'module-zeroconf-discover/avahi-poll.c' ],
include_directories : [configinc],
install : true,
install_dir : modules_install_dir,
install_rpath: modules_install_dir,
- dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, zeroconf_deps],
+ dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep],
)
endif
summary({'zeroconf-discover': build_module_zeroconf_discover}, bool_yn: true, section: 'Optional Modules')
@@ -609,12 +589,12 @@ build_module_raop_discover = avahi_dep.found()
if build_module_raop_discover
pipewire_module_raop_discover = shared_library('pipewire-module-raop-discover',
[ 'module-raop-discover.c',
- zeroconf_sources ],
+ 'module-zeroconf-discover/avahi-poll.c' ],
include_directories : [configinc],
install : true,
install_dir : modules_install_dir,
install_rpath: modules_install_dir,
- dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, zeroconf_deps],
+ dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep],
)
endif
summary({'raop-discover (needs Avahi)': build_module_raop_discover}, bool_yn: true, section: 'Optional Modules')
@@ -623,12 +603,12 @@ build_module_snapcast_discover = avahi_dep.found()
if build_module_snapcast_discover
pipewire_module_snapcast_discover = shared_library('pipewire-module-snapcast-discover',
[ 'module-snapcast-discover.c',
- zeroconf_sources ],
+ 'module-zeroconf-discover/avahi-poll.c' ],
include_directories : [configinc],
install : true,
install_dir : modules_install_dir,
install_rpath: modules_install_dir,
- dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, zeroconf_deps],
+ dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep],
)
endif
summary({'snapcast-discover (needs Avahi)': build_module_snapcast_discover}, bool_yn: true, section: 'Optional Modules')
@@ -671,13 +651,13 @@ pipewire_module_rtp_sink = shared_library('pipewire-module-rtp-sink',
build_module_rtp_session = avahi_dep.found()
if build_module_rtp_session
pipewire_module_rtp_session = shared_library('pipewire-module-rtp-session',
- [ 'module-rtp-session.c',
- zeroconf_sources ],
+ [ 'module-zeroconf-discover/avahi-poll.c',
+ 'module-rtp-session.c' ],
include_directories : [configinc],
install : true,
install_dir : modules_install_dir,
install_rpath: modules_install_dir,
- dependencies : [pipewire_module_rtp_common_dep, zeroconf_deps],
+ dependencies : [pipewire_module_rtp_common_dep, avahi_dep],
)
endif
@@ -710,35 +690,6 @@ pipewire_module_vban_recv = shared_library('pipewire-module-vban-recv',
dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep],
)
-pipewire_module_sendspin_sources = []
-pipewire_module_sendspin_deps = [ mathlib, dl_lib, rt_lib, pipewire_dep ]
-
-if avahi_dep.found()
- pipewire_module_sendspin_sources += zeroconf_sources
- pipewire_module_sendspin_deps += zeroconf_deps
-endif
-
-pipewire_module_sendspin_recv = shared_library('pipewire-module-sendspin-recv',
- [ 'module-sendspin-recv.c',
- 'module-sendspin/websocket.c',
- pipewire_module_sendspin_sources ],
- include_directories : [configinc],
- install : true,
- install_dir : modules_install_dir,
- install_rpath: modules_install_dir,
- dependencies : pipewire_module_sendspin_deps,
-)
-pipewire_module_sendspin_send = shared_library('pipewire-module-sendspin-send',
- [ 'module-sendspin-send.c',
- 'module-sendspin/websocket.c',
- pipewire_module_sendspin_sources ],
- include_directories : [configinc],
- install : true,
- install_dir : modules_install_dir,
- install_rpath: modules_install_dir,
- dependencies : pipewire_module_sendspin_deps,
-)
-
build_module_roc = roc_dep.found()
if build_module_roc
pipewire_module_roc_sink = shared_library('pipewire-module-roc-sink',
diff --git a/src/modules/module-adapter.c b/src/modules/module-adapter.c
index 3325602ac..9f5474a08 100644
--- a/src/modules/module-adapter.c
+++ b/src/modules/module-adapter.c
@@ -184,7 +184,7 @@ static void *create_object(void *_data,
if (properties == NULL)
goto error_properties;
- pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%u",
+ pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d",
pw_impl_factory_get_info(d->factory)->id);
linger = pw_properties_get_bool(properties, PW_KEY_OBJECT_LINGER, false);
@@ -196,7 +196,7 @@ static void *create_object(void *_data,
client = resource ? pw_resource_get_client(resource): NULL;
if (client && !linger) {
- pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%u",
+ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d",
pw_impl_client_get_info(client)->id);
}
@@ -343,7 +343,7 @@ static void module_registered(void *data)
char id[16];
int res;
- snprintf(id, sizeof(id), "%u", pw_impl_module_get_info(module)->id);
+ snprintf(id, sizeof(id), "%d", pw_impl_module_get_info(module)->id);
items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MODULE_ID, id);
pw_impl_factory_update_properties(factory, &SPA_DICT_INIT(items, 1));
diff --git a/src/modules/module-avb/acmp.c b/src/modules/module-avb/acmp.c
index 73a84ba89..eacfb6133 100644
--- a/src/modules/module-avb/acmp.c
+++ b/src/modules/module-avb/acmp.c
@@ -174,14 +174,13 @@ static int handle_connect_tx_command(struct acmp *acmp, uint64_t now, const void
return 0;
memcpy(buf, m, len);
- AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE);
-
stream = find_stream(server, SPA_DIRECTION_OUTPUT, ntohs(reply->talker_unique_id));
if (stream == NULL) {
status = AVB_ACMP_STATUS_TALKER_NO_STREAM_INDEX;
goto done;
}
+ AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE);
reply->stream_id = htobe64(stream->id);
stream_activate(stream, ntohs(reply->talker_unique_id), now);
@@ -252,14 +251,14 @@ static int handle_disconnect_tx_command(struct acmp *acmp, uint64_t now, const v
return 0;
memcpy(buf, m, len);
- AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_RESPONSE);
-
stream = find_stream(server, SPA_DIRECTION_OUTPUT, ntohs(reply->talker_unique_id));
if (stream == NULL) {
status = AVB_ACMP_STATUS_TALKER_NO_STREAM_INDEX;
goto done;
}
+ AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_RESPONSE);
+
stream_deactivate(stream, now);
done:
diff --git a/src/modules/module-avb/aecp-aem-cmds-resps/cmd-lock-entity.c b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-lock-entity.c
index 4b271b7fc..0149d633b 100644
--- a/src/modules/module-avb/aecp-aem-cmds-resps/cmd-lock-entity.c
+++ b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-lock-entity.c
@@ -71,41 +71,6 @@ static int handle_unsol_lock_entity_milanv12(struct aecp *aecp, struct descripto
}
-void handle_cmd_lock_entity_expired_milan_v12(struct aecp *aecp, int64_t now)
-{
- struct server *server = aecp->server;
- struct descriptor *desc;
- struct aecp_aem_entity_milan_state *entity_state;
- struct aecp_aem_lock_state *lock;
-
- desc = server_find_descriptor(server, AVB_AEM_DESC_ENTITY, 0);
- if (desc == NULL)
- return;
-
- entity_state = desc->ptr;
- lock = &entity_state->state.lock_state;
-
- if (!lock->is_locked)
- return;
-
- if (lock->base_info.expire_timeout >= now)
- return;
-
- pw_log_info("entity lock held by %" PRIx64 " expired after %lus, releasing",
- lock->locked_id, AECP_AEM_LOCK_ENTITY_EXPIRE_TIMEOUT_SECOND);
-
- lock->is_locked = false;
- lock->locked_id = 0;
- /*
- * No specific triggering controller (this is a timeout, not a command).
- * Setting controller_entity_id to 0 combined with internal=true ensures
- * reply_unsol_send notifies ALL registered controllers, including the
- * one whose lock just expired.
- */
- lock->base_info.controller_entity_id = 0;
- handle_unsol_lock_common(aecp, lock, true);
-}
-
/* LOCK_ENTITY */
/* Milan v1.2, Sec. 5.4.2.2; IEEE 1722.1-2021, Sec. 7.4.2*/
int handle_cmd_lock_entity_milan_v12(struct aecp *aecp, int64_t now, const void *m, int len)
@@ -134,7 +99,7 @@ int handle_cmd_lock_entity_milan_v12(struct aecp *aecp, int64_t now, const void
desc = server_find_descriptor(server, desc_type, desc_id);
if (desc == NULL)
- return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len);
+ return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len);
entity_state = desc->ptr;
lock = &entity_state->state.lock_state;
@@ -183,7 +148,7 @@ int handle_cmd_lock_entity_milan_v12(struct aecp *aecp, int64_t now, const void
// If the lock is taken again by device
if (ctrler_id == lock->locked_id) {
lock->base_info.expire_timeout +=
- AECP_AEM_LOCK_ENTITY_EXPIRE_TIMEOUT_SECOND * SPA_NSEC_PER_SEC;
+ AECP_AEM_LOCK_ENTITY_EXPIRE_TIMEOUT_SECOND;
lock->is_locked = true;
} else {
diff --git a/src/modules/module-avb/aecp-aem-cmds-resps/cmd-lock-entity.h b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-lock-entity.h
index c83bc464d..dee877a7f 100644
--- a/src/modules/module-avb/aecp-aem-cmds-resps/cmd-lock-entity.h
+++ b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-lock-entity.h
@@ -12,15 +12,6 @@
#include
-/**
- * @brief Checks whether the Milan entity lock has expired and releases it.
- *
- * Called once per second from the AECP periodic handler. If the lock is
- * active and its timeout (set at lock time) is earlier than @p now, the
- * lock is cleared.
- */
-void handle_cmd_lock_entity_expired_milan_v12(struct aecp *aecp, int64_t now);
-
/**
* @brief Command handling will generate the response for the lock command
*/
diff --git a/src/modules/module-avb/aecp-aem.c b/src/modules/module-avb/aecp-aem.c
index 78b3eb818..fccf6b178 100644
--- a/src/modules/module-avb/aecp-aem.c
+++ b/src/modules/module-avb/aecp-aem.c
@@ -5,7 +5,6 @@
#include "aecp-aem.h"
#include "aecp-aem-descriptors.h"
-#include "aecp-aem-state.h"
#include "aecp-aem-cmds-resps/cmd-resp-helpers.h"
#include "utils.h"
@@ -28,8 +27,7 @@ static int handle_acquire_entity_avb_legacy(struct aecp *aecp, int64_t now,
const void *m, int len)
{
struct server *server = aecp->server;
- const struct avb_ethernet_header *h = m;
- const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void);
+ const struct avb_packet_aecp_aem *p = m;
const struct avb_packet_aecp_aem_acquire *ae;
const struct descriptor *desc;
uint16_t desc_type, desc_id;
@@ -55,8 +53,7 @@ static int handle_lock_entity_avb_legacy(struct aecp *aecp, int64_t now,
const void *m, int len)
{
struct server *server = aecp->server;
- const struct avb_ethernet_header *h = m;
- const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void);
+ const struct avb_packet_aecp_aem *p = m;
const struct avb_packet_aecp_aem_acquire *ae;
const struct descriptor *desc;
uint16_t desc_type, desc_id;
@@ -371,44 +368,6 @@ static const struct {
},
};
-/**
- * \brief Stub that queries the AECP entity lock state.
- *
- * Returns true when the entity is currently locked by a *different* controller
- * than the one sending the command, meaning the command must be rejected with
- * ENTITY_LOCKED. Returns false in all other cases (not locked, lock expired,
- * or requester is the lock owner).
- *
- * Only Milan V1.2 entities maintain a lock state; legacy AVB entities always
- * return false (unlocked).
- */
-static bool check_locked(struct aecp *aecp, int64_t now,
- const struct avb_packet_aecp_aem *p)
-{
- struct server *server = aecp->server;
- const struct descriptor *desc;
- const struct aecp_aem_entity_milan_state *entity_state;
- const struct aecp_aem_lock_state *lock;
-
- if (server->avb_mode != AVB_MODE_MILAN_V12)
- return false;
-
- desc = server_find_descriptor(server, AVB_AEM_DESC_ENTITY, 0);
- if (desc == NULL)
- return false;
-
- entity_state = desc->ptr;
- lock = &entity_state->state.lock_state;
-
- /* A lock that has expired is treated as if the entity is unlocked. */
- if (lock->base_info.expire_timeout < now)
- return false;
-
- /* Locked by a different controller → reject. */
- return lock->is_locked &&
- (lock->locked_id != htobe64(p->aecp.controller_guid));
-}
-
int avb_aecp_aem_handle_command(struct aecp *aecp, const void *m, int len)
{
const struct avb_ethernet_header *h = m;
@@ -441,30 +400,9 @@ int avb_aecp_aem_handle_command(struct aecp *aecp, const void *m, int len)
now = SPA_TIMESPEC_TO_NSEC(&ts_now);
- /*
- * For write (non-readonly) commands, check whether the entity is locked
- * by a different controller before dispatching the handler. Readonly
- * commands are always allowed regardless of lock state.
- */
- if (!info->is_readonly && check_locked(aecp, now, p)) {
- pw_log_info("aem command %s rejected: entity locked",
- cmd_names[cmd_type]);
- return reply_entity_locked(aecp, m, len);
- }
-
return info->handle_command(aecp, now, m, len);
}
-void avb_aecp_aem_periodic(struct aecp *aecp, int64_t now)
-{
- struct server *server = aecp->server;
-
- if (server->avb_mode != AVB_MODE_MILAN_V12)
- return;
-
- handle_cmd_lock_entity_expired_milan_v12(aecp, now);
-}
-
int avb_aecp_aem_handle_response(struct aecp *aecp, const void *m, int len)
{
return 0;
diff --git a/src/modules/module-avb/aecp-aem.h b/src/modules/module-avb/aecp-aem.h
index e4e120452..507d0f868 100644
--- a/src/modules/module-avb/aecp-aem.h
+++ b/src/modules/module-avb/aecp-aem.h
@@ -249,6 +249,5 @@ struct avb_packet_aecp_aem {
int avb_aecp_aem_handle_command(struct aecp *aecp, const void *m, int len);
int avb_aecp_aem_handle_response(struct aecp *aecp, const void *m, int len);
-void avb_aecp_aem_periodic(struct aecp *aecp, int64_t now);
#endif /* AVB_AEM_H */
diff --git a/src/modules/module-avb/aecp.c b/src/modules/module-avb/aecp.c
index 6a15f87a9..6e6a1ba43 100644
--- a/src/modules/module-avb/aecp.c
+++ b/src/modules/module-avb/aecp.c
@@ -86,12 +86,6 @@ static int aecp_message(void *data, uint64_t now, const void *message, int len)
return info->handle(aecp, message, len);
}
-static void aecp_periodic(void *data, uint64_t now)
-{
- struct aecp *aecp = data;
- avb_aecp_aem_periodic(aecp, (int64_t)now);
-}
-
static void aecp_destroy(void *data)
{
struct aecp *aecp = data;
@@ -130,7 +124,6 @@ static const struct server_events server_events = {
AVB_VERSION_SERVER_EVENTS,
.destroy = aecp_destroy,
.message = aecp_message,
- .periodic = aecp_periodic,
.command = aecp_command
};
diff --git a/src/modules/module-avb/avb-transport-loopback.h b/src/modules/module-avb/avb-transport-loopback.h
deleted file mode 100644
index 18508b99a..000000000
--- a/src/modules/module-avb/avb-transport-loopback.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/* AVB support */
-/* SPDX-FileCopyrightText: Copyright © 2026 PipeWire contributors */
-/* SPDX-License-Identifier: MIT */
-
-#ifndef AVB_TRANSPORT_LOOPBACK_H
-#define AVB_TRANSPORT_LOOPBACK_H
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "internal.h"
-#include "packets.h"
-
-#define AVB_LOOPBACK_MAX_PACKETS 64
-#define AVB_LOOPBACK_MAX_PACKET_SIZE 2048
-
-struct avb_loopback_packet {
- uint8_t dest[6];
- uint16_t type;
- size_t size;
- uint8_t data[AVB_LOOPBACK_MAX_PACKET_SIZE];
-};
-
-struct avb_loopback_transport {
- struct avb_loopback_packet packets[AVB_LOOPBACK_MAX_PACKETS];
- int packet_count;
- int packet_read;
-};
-
-static inline int avb_loopback_setup(struct server *server)
-{
- struct avb_loopback_transport *t;
- static const uint8_t test_mac[6] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x01 };
-
- t = calloc(1, sizeof(*t));
- if (t == NULL)
- return -errno;
-
- server->transport_data = t;
-
- memcpy(server->mac_addr, test_mac, 6);
- server->ifindex = 1;
- server->entity_id = (uint64_t)server->mac_addr[0] << 56 |
- (uint64_t)server->mac_addr[1] << 48 |
- (uint64_t)server->mac_addr[2] << 40 |
- (uint64_t)0xff << 32 |
- (uint64_t)0xfe << 24 |
- (uint64_t)server->mac_addr[3] << 16 |
- (uint64_t)server->mac_addr[4] << 8 |
- (uint64_t)server->mac_addr[5];
-
- return 0;
-}
-
-static inline int avb_loopback_send_packet(struct server *server,
- const uint8_t dest[6], uint16_t type, void *data, size_t size)
-{
- struct avb_loopback_transport *t = server->transport_data;
- struct avb_loopback_packet *pkt;
- struct avb_ethernet_header *hdr = (struct avb_ethernet_header*)data;
-
- if (t->packet_count >= AVB_LOOPBACK_MAX_PACKETS)
- return -ENOSPC;
- if (size > AVB_LOOPBACK_MAX_PACKET_SIZE)
- return -EMSGSIZE;
-
- /* Fill in the ethernet header like the raw transport does */
- memcpy(hdr->dest, dest, 6);
- memcpy(hdr->src, server->mac_addr, 6);
- hdr->type = htons(type);
-
- pkt = &t->packets[t->packet_count % AVB_LOOPBACK_MAX_PACKETS];
- memcpy(pkt->dest, dest, 6);
- pkt->type = type;
- pkt->size = size;
- memcpy(pkt->data, data, size);
- t->packet_count++;
-
- return 0;
-}
-
-/**
- * Return a dummy fd for protocol handlers that create their own sockets.
- * Uses eventfd so pw_loop_add_io() has a valid fd to work with.
- */
-static inline int avb_loopback_make_socket(struct server *server,
- uint16_t type, const uint8_t mac[6])
-{
- int fd;
-
- fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- return fd;
-}
-
-static inline void avb_loopback_destroy(struct server *server)
-{
- free(server->transport_data);
- server->transport_data = NULL;
-}
-
-/**
- * Create a dummy stream socket using eventfd.
- * No AF_PACKET, no ioctls, no privileges needed.
- */
-static inline int avb_loopback_stream_setup_socket(struct server *server,
- struct stream *stream)
-{
- int fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- spa_zero(stream->sock_addr);
- stream->sock_addr.sll_family = AF_PACKET;
- stream->sock_addr.sll_halen = ETH_ALEN;
-
- return fd;
-}
-
-/**
- * No-op stream send — pretend the send succeeded.
- * Audio data is consumed from the ringbuffer but goes nowhere.
- */
-static inline ssize_t avb_loopback_stream_send(struct server *server,
- struct stream *stream, struct msghdr *msg, int flags)
-{
- ssize_t total = 0;
- for (size_t i = 0; i < msg->msg_iovlen; i++)
- total += msg->msg_iov[i].iov_len;
- return total;
-}
-
-static const struct avb_transport_ops avb_transport_loopback = {
- .setup = avb_loopback_setup,
- .send_packet = avb_loopback_send_packet,
- .make_socket = avb_loopback_make_socket,
- .destroy = avb_loopback_destroy,
- .stream_setup_socket = avb_loopback_stream_setup_socket,
- .stream_send = avb_loopback_stream_send,
-};
-
-/** Get the number of captured sent packets */
-static inline int avb_loopback_get_packet_count(struct server *server)
-{
- struct avb_loopback_transport *t = server->transport_data;
- return t->packet_count - t->packet_read;
-}
-
-/** Read the next captured sent packet, returns packet size or -1 */
-static inline int avb_loopback_get_packet(struct server *server,
- void *buf, size_t bufsize)
-{
- struct avb_loopback_transport *t = server->transport_data;
- struct avb_loopback_packet *pkt;
-
- if (t->packet_read >= t->packet_count)
- return -1;
-
- pkt = &t->packets[t->packet_read % AVB_LOOPBACK_MAX_PACKETS];
- t->packet_read++;
-
- if (pkt->size > bufsize)
- return -1;
-
- memcpy(buf, pkt->data, pkt->size);
- return pkt->size;
-}
-
-/** Clear all captured packets */
-static inline void avb_loopback_clear_packets(struct server *server)
-{
- struct avb_loopback_transport *t = server->transport_data;
- t->packet_count = 0;
- t->packet_read = 0;
-}
-
-#endif /* AVB_TRANSPORT_LOOPBACK_H */
diff --git a/src/modules/module-avb/avdecc.c b/src/modules/module-avb/avdecc.c
index 8729d6421..7cd7e8e7a 100644
--- a/src/modules/module-avb/avdecc.c
+++ b/src/modules/module-avb/avdecc.c
@@ -84,7 +84,7 @@ static void on_socket_data(void *data, int fd, uint32_t mask)
}
}
-static int raw_send_packet(struct server *server, const uint8_t dest[6],
+int avb_server_send_packet(struct server *server, const uint8_t dest[6],
uint16_t type, void *data, size_t size)
{
struct avb_ethernet_header *hdr = (struct avb_ethernet_header*)data;
@@ -101,12 +101,6 @@ static int raw_send_packet(struct server *server, const uint8_t dest[6],
return res;
}
-int avb_server_send_packet(struct server *server, const uint8_t dest[6],
- uint16_t type, void *data, size_t size)
-{
- return server->transport->send_packet(server, dest, type, data, size);
-}
-
static int load_filter(int fd, uint16_t eth, const uint8_t dest[6], const uint8_t mac[6])
{
struct sock_fprog filter;
@@ -142,7 +136,7 @@ static int load_filter(int fd, uint16_t eth, const uint8_t dest[6], const uint8_
return 0;
}
-static int raw_make_socket(struct server *server, uint16_t type, const uint8_t mac[6])
+int avb_server_make_socket(struct server *server, uint16_t type, const uint8_t mac[6])
{
int fd, res;
struct ifreq req;
@@ -215,20 +209,13 @@ error_close:
return res;
}
-int avb_server_make_socket(struct server *server, uint16_t type, const uint8_t mac[6])
-{
- if (server->transport && server->transport->make_socket)
- return server->transport->make_socket(server, type, mac);
- return raw_make_socket(server, type, mac);
-}
-
-static int raw_transport_setup(struct server *server)
+static int setup_socket(struct server *server)
{
struct impl *impl = server->impl;
int fd, res;
static const uint8_t bmac[6] = AVB_BROADCAST_MAC;
- fd = raw_make_socket(server, AVB_TSN_ETH, bmac);
+ fd = avb_server_make_socket(server, AVB_TSN_ETH, bmac);
if (fd < 0)
return fd;
@@ -257,119 +244,6 @@ error_no_source:
return res;
}
-static int raw_stream_setup_socket(struct server *server, struct stream *stream)
-{
- int fd, res;
- char buf[128];
- struct ifreq req;
-
- fd = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(ETH_P_ALL));
- if (fd < 0) {
- pw_log_error("socket() failed: %m");
- return -errno;
- }
-
- spa_zero(req);
- snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", server->ifname);
- res = ioctl(fd, SIOCGIFINDEX, &req);
- if (res < 0) {
- pw_log_error("SIOCGIFINDEX %s failed: %m", server->ifname);
- res = -errno;
- goto error_close;
- }
-
- spa_zero(stream->sock_addr);
- stream->sock_addr.sll_family = AF_PACKET;
- stream->sock_addr.sll_protocol = htons(ETH_P_TSN);
- stream->sock_addr.sll_ifindex = req.ifr_ifindex;
-
- if (stream->direction == SPA_DIRECTION_OUTPUT) {
- struct sock_txtime txtime_cfg;
-
- res = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &stream->prio,
- sizeof(stream->prio));
- if (res < 0) {
- pw_log_error("setsockopt(SO_PRIORITY %d) failed: %m", stream->prio);
- res = -errno;
- goto error_close;
- }
-
- txtime_cfg.clockid = CLOCK_TAI;
- txtime_cfg.flags = 0;
- res = setsockopt(fd, SOL_SOCKET, SO_TXTIME, &txtime_cfg,
- sizeof(txtime_cfg));
- if (res < 0) {
- pw_log_error("setsockopt(SO_TXTIME) failed: %m");
- res = -errno;
- goto error_close;
- }
- } else {
- struct packet_mreq mreq;
-
- res = bind(fd, (struct sockaddr *) &stream->sock_addr, sizeof(stream->sock_addr));
- if (res < 0) {
- pw_log_error("bind() failed: %m");
- res = -errno;
- goto error_close;
- }
-
- spa_zero(mreq);
- mreq.mr_ifindex = req.ifr_ifindex;
- mreq.mr_type = PACKET_MR_MULTICAST;
- mreq.mr_alen = ETH_ALEN;
- memcpy(&mreq.mr_address, stream->addr, ETH_ALEN);
- res = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
- &mreq, sizeof(struct packet_mreq));
-
- pw_log_info("join %s", avb_utils_format_addr(buf, 128, stream->addr));
-
- if (res < 0) {
- pw_log_error("setsockopt(ADD_MEMBERSHIP) failed: %m");
- res = -errno;
- goto error_close;
- }
- }
- return fd;
-
-error_close:
- close(fd);
- return res;
-}
-
-static ssize_t raw_stream_send(struct server *server, struct stream *stream,
- struct msghdr *msg, int flags)
-{
- return sendmsg(stream->source->fd, msg, flags);
-}
-
-int avb_server_stream_setup_socket(struct server *server, struct stream *stream)
-{
- return server->transport->stream_setup_socket(server, stream);
-}
-
-ssize_t avb_server_stream_send(struct server *server, struct stream *stream,
- struct msghdr *msg, int flags)
-{
- return server->transport->stream_send(server, stream, msg, flags);
-}
-
-static void raw_transport_destroy(struct server *server)
-{
- struct impl *impl = server->impl;
- if (server->source)
- pw_loop_destroy_source(impl->loop, server->source);
- server->source = NULL;
-}
-
-const struct avb_transport_ops avb_transport_raw = {
- .setup = raw_transport_setup,
- .send_packet = raw_send_packet,
- .make_socket = raw_make_socket,
- .destroy = raw_transport_destroy,
- .stream_setup_socket = raw_stream_setup_socket,
- .stream_send = raw_stream_send,
-};
-
struct server *avdecc_server_new(struct impl *impl, struct spa_dict *props)
{
struct server *server;
@@ -392,14 +266,10 @@ struct server *avdecc_server_new(struct impl *impl, struct spa_dict *props)
spa_hook_list_init(&server->listener_list);
spa_list_init(&server->descriptors);
- spa_list_init(&server->streams);
server->debug_messages = false;
- if (server->transport == NULL)
- server->transport = &avb_transport_raw;
-
- if ((res = server->transport->setup(server)) < 0)
+ if ((res = setup_socket(server)) < 0)
goto error_free;
@@ -445,10 +315,12 @@ void avdecc_server_add_listener(struct server *server, struct spa_hook *listener
void avdecc_server_free(struct server *server)
{
+ struct impl *impl = server->impl;
+
server_destroy_descriptors(server);
spa_list_remove(&server->link);
- if (server->transport)
- server->transport->destroy(server);
+ if (server->source)
+ pw_loop_destroy_source(impl->loop, server->source);
pw_timer_queue_cancel(&server->timer);
spa_hook_list_clean(&server->listener_list);
free(server->ifname);
diff --git a/src/modules/module-avb/es-builder.c b/src/modules/module-avb/es-builder.c
index 6234e1b92..adcc6b930 100644
--- a/src/modules/module-avb/es-builder.c
+++ b/src/modules/module-avb/es-builder.c
@@ -50,7 +50,7 @@ static void *es_builder_desc_entity_milan_v12(struct server *server,
&entity_state);
if (!ptr_alloc) {
- pw_log_error("Error during allocation\n");
+ pw_log_error("Error durring allocation\n");
spa_assert(0);
}
diff --git a/src/modules/module-avb/internal.h b/src/modules/module-avb/internal.h
index bb4961674..82ced2f21 100644
--- a/src/modules/module-avb/internal.h
+++ b/src/modules/module-avb/internal.h
@@ -5,8 +5,6 @@
#ifndef AVB_INTERNAL_H
#define AVB_INTERNAL_H
-#include
-
#include
#ifdef __cplusplus
@@ -19,22 +17,6 @@ struct avb_mrp;
#define AVB_TSN_ETH 0x22f0
#define AVB_BROADCAST_MAC { 0x91, 0xe0, 0xf0, 0x01, 0x00, 0x00 };
-struct stream;
-
-struct avb_transport_ops {
- int (*setup)(struct server *server);
- int (*send_packet)(struct server *server, const uint8_t dest[6],
- uint16_t type, void *data, size_t size);
- int (*make_socket)(struct server *server, uint16_t type,
- const uint8_t mac[6]);
- void (*destroy)(struct server *server);
-
- /* stream data plane ops */
- int (*stream_setup_socket)(struct server *server, struct stream *stream);
- ssize_t (*stream_send)(struct server *server, struct stream *stream,
- struct msghdr *msg, int flags);
-};
-
struct impl {
struct pw_loop *loop;
struct pw_timer_queue *timer_queue;
@@ -95,16 +77,12 @@ struct server {
uint64_t entity_id;
int ifindex;
- const struct avb_transport_ops *transport;
- void *transport_data;
-
struct spa_source *source;
struct pw_timer timer;
struct spa_hook_list listener_list;
struct spa_list descriptors;
- struct spa_list streams;
unsigned debug_messages:1;
@@ -124,6 +102,7 @@ static inline void server_destroy_descriptors(struct server *server)
struct descriptor *d, *t;
spa_list_for_each_safe(d, t, &server->descriptors, link) {
+ free(d->ptr);
spa_list_remove(&d->link);
free(d);
}
@@ -166,17 +145,11 @@ void avdecc_server_free(struct server *server);
void avdecc_server_add_listener(struct server *server, struct spa_hook *listener,
const struct server_events *events, void *data);
-extern const struct avb_transport_ops avb_transport_raw;
-
int avb_server_make_socket(struct server *server, uint16_t type, const uint8_t mac[6]);
int avb_server_send_packet(struct server *server, const uint8_t dest[6],
uint16_t type, void *data, size_t size);
-int avb_server_stream_setup_socket(struct server *server, struct stream *stream);
-ssize_t avb_server_stream_send(struct server *server, struct stream *stream,
- struct msghdr *msg, int flags);
-
struct aecp {
struct server *server;
struct spa_hook server_listener;
diff --git a/src/modules/module-avb/mrp.c b/src/modules/module-avb/mrp.c
index c6505d41b..73d5275ca 100644
--- a/src/modules/module-avb/mrp.c
+++ b/src/modules/module-avb/mrp.c
@@ -302,8 +302,6 @@ const char *avb_mrp_notify_name(uint8_t notify)
const char *avb_mrp_send_name(uint8_t send)
{
switch(send) {
- case 0:
- return "none";
case AVB_MRP_SEND_NEW:
return "new";
case AVB_MRP_SEND_JOININ:
diff --git a/src/modules/module-avb/mrp.h b/src/modules/module-avb/mrp.h
index 399343267..78683412c 100644
--- a/src/modules/module-avb/mrp.h
+++ b/src/modules/module-avb/mrp.h
@@ -88,13 +88,13 @@ struct avb_packet_mrp_footer {
#define AVB_MRP_ATTRIBUTE_EVENT_LV 5
#define AVB_MRP_ATTRIBUTE_EVENT_LVA 6
-#define AVB_MRP_SEND_NEW 1
-#define AVB_MRP_SEND_JOININ 2
-#define AVB_MRP_SEND_IN 3
-#define AVB_MRP_SEND_JOINMT 4
-#define AVB_MRP_SEND_MT 5
-#define AVB_MRP_SEND_LV 6
-#define AVB_MRP_SEND_LVA 7
+#define AVB_MRP_SEND_NEW 0
+#define AVB_MRP_SEND_JOININ 1
+#define AVB_MRP_SEND_IN 2
+#define AVB_MRP_SEND_JOINMT 3
+#define AVB_MRP_SEND_MT 4
+#define AVB_MRP_SEND_LV 5
+#define AVB_MRP_SEND_LVA 6
#define AVB_MRP_NOTIFY_NEW 1
#define AVB_MRP_NOTIFY_JOIN 2
diff --git a/src/modules/module-avb/msrp.c b/src/modules/module-avb/msrp.c
index 611ddd537..92d1e65b4 100644
--- a/src/modules/module-avb/msrp.c
+++ b/src/modules/module-avb/msrp.c
@@ -91,7 +91,7 @@ static int encode_talker(struct msrp *msrp, struct attr *a, void *m)
*t = a->attr.attr.talker;
ev = SPA_PTROFF(t, sizeof(*t), uint8_t);
- *ev = (a->attr.mrp->pending_send - 1) * 6 * 6;
+ *ev = a->attr.mrp->pending_send * 6 * 6;
f = SPA_PTROFF(ev, sizeof(*ev), struct avb_packet_mrp_footer);
f->end_mark = 0;
@@ -170,7 +170,7 @@ static int encode_listener(struct msrp *msrp, struct attr *a, void *m)
*l = a->attr.attr.listener;
ev = SPA_PTROFF(l, sizeof(*l), uint8_t);
- *ev = (a->attr.mrp->pending_send - 1) * 6 * 6;
+ *ev = a->attr.mrp->pending_send * 6 * 6;
ev = SPA_PTROFF(ev, sizeof(*ev), uint8_t);
*ev = a->attr.param * 4 * 4 * 4;
@@ -226,7 +226,7 @@ static int encode_domain(struct msrp *msrp, struct attr *a, void *m)
*d = a->attr.attr.domain;
ev = SPA_PTROFF(d, sizeof(*d), uint8_t);
- *ev = (a->attr.mrp->pending_send - 1) * 36;
+ *ev = a->attr.mrp->pending_send * 36;
f = SPA_PTROFF(ev, sizeof(*ev), struct avb_packet_mrp_footer);
f->end_mark = 0;
@@ -332,8 +332,7 @@ static void msrp_notify(void *data, uint64_t now, uint8_t notify)
{
struct attr *a = data;
struct msrp *msrp = a->msrp;
- if (dispatch[a->attr.type].notify)
- dispatch[a->attr.type].notify(msrp, now, a, notify);
+ return dispatch[a->attr.type].notify(msrp, now, a, notify);
}
static const struct avb_mrp_attribute_events mrp_attr_events = {
diff --git a/src/modules/module-avb/mvrp.c b/src/modules/module-avb/mvrp.c
index e2e501a9e..20862c2ae 100644
--- a/src/modules/module-avb/mvrp.c
+++ b/src/modules/module-avb/mvrp.c
@@ -84,7 +84,7 @@ static int encode_vid(struct mvrp *mvrp, struct attr *a, void *m)
*d = a->attr.attr.vid;
ev = SPA_PTROFF(d, sizeof(*d), uint8_t);
- *ev = (a->attr.mrp->pending_send - 1) * 36;
+ *ev = a->attr.mrp->pending_send * 36;
f = SPA_PTROFF(ev, sizeof(*ev), struct avb_packet_mrp_footer);
f->end_mark = 0;
@@ -171,8 +171,7 @@ static void mvrp_notify(void *data, uint64_t now, uint8_t notify)
{
struct attr *a = data;
struct mvrp *mvrp = a->mvrp;
- if (dispatch[a->attr.type].notify)
- dispatch[a->attr.type].notify(mvrp, now, a, notify);
+ return dispatch[a->attr.type].notify(mvrp, now, a, notify);
}
static const struct avb_mrp_attribute_events mrp_attr_events = {
diff --git a/src/modules/module-avb/stream.c b/src/modules/module-avb/stream.c
index 26a3a795b..f7101bdf0 100644
--- a/src/modules/module-avb/stream.c
+++ b/src/modules/module-avb/stream.c
@@ -116,10 +116,9 @@ static int flush_write(struct stream *stream, uint64_t current_time)
p->timestamp = ptime;
p->dbc = dbc;
- n = avb_server_stream_send(stream->server, stream,
- &stream->msg, MSG_NOSIGNAL);
+ n = sendmsg(stream->source->fd, &stream->msg, MSG_NOSIGNAL);
if (n < 0 || n != (ssize_t)stream->pdu_size) {
- pw_log_error("stream send failed %zd != %zd: %m",
+ pw_log_error("sendmsg() failed %zd != %zd: %m",
n, stream->pdu_size);
}
txtime += stream->pdu_period;
@@ -332,8 +331,6 @@ struct stream *server_create_stream(struct server *server, struct stream *stream
stream->talker_attr->attr.talker.rank = AVB_MSRP_RANK_DEFAULT;
stream->talker_attr->attr.talker.accumulated_latency = htonl(95);
- spa_list_append(&server->streams, &stream->link);
-
return stream;
error_free_stream:
@@ -351,7 +348,82 @@ void stream_destroy(struct stream *stream)
static int setup_socket(struct stream *stream)
{
- return avb_server_stream_setup_socket(stream->server, stream);
+ struct server *server = stream->server;
+ int fd, res;
+ char buf[128];
+ struct ifreq req;
+
+ fd = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(ETH_P_ALL));
+ if (fd < 0) {
+ pw_log_error("socket() failed: %m");
+ return -errno;
+ }
+
+ spa_zero(req);
+ snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", server->ifname);
+ res = ioctl(fd, SIOCGIFINDEX, &req);
+ if (res < 0) {
+ pw_log_error("SIOCGIFINDEX %s failed: %m", server->ifname);
+ res = -errno;
+ goto error_close;
+ }
+
+ spa_zero(stream->sock_addr);
+ stream->sock_addr.sll_family = AF_PACKET;
+ stream->sock_addr.sll_protocol = htons(ETH_P_TSN);
+ stream->sock_addr.sll_ifindex = req.ifr_ifindex;
+
+ if (stream->direction == SPA_DIRECTION_OUTPUT) {
+ struct sock_txtime txtime_cfg;
+
+ res = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &stream->prio,
+ sizeof(stream->prio));
+ if (res < 0) {
+ pw_log_error("setsockopt(SO_PRIORITY %d) failed: %m", stream->prio);
+ res = -errno;
+ goto error_close;
+ }
+
+ txtime_cfg.clockid = CLOCK_TAI;
+ txtime_cfg.flags = 0;
+ res = setsockopt(fd, SOL_SOCKET, SO_TXTIME, &txtime_cfg,
+ sizeof(txtime_cfg));
+ if (res < 0) {
+ pw_log_error("setsockopt(SO_TXTIME) failed: %m");
+ res = -errno;
+ goto error_close;
+ }
+ } else {
+ struct packet_mreq mreq;
+
+ res = bind(fd, (struct sockaddr *) &stream->sock_addr, sizeof(stream->sock_addr));
+ if (res < 0) {
+ pw_log_error("bind() failed: %m");
+ res = -errno;
+ goto error_close;
+ }
+
+ spa_zero(mreq);
+ mreq.mr_ifindex = req.ifr_ifindex;
+ mreq.mr_type = PACKET_MR_MULTICAST;
+ mreq.mr_alen = ETH_ALEN;
+ memcpy(&mreq.mr_address, stream->addr, ETH_ALEN);
+ res = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
+ &mreq, sizeof(struct packet_mreq));
+
+ pw_log_info("join %s", avb_utils_format_addr(buf, 128, stream->addr));
+
+ if (res < 0) {
+ pw_log_error("setsockopt(ADD_MEMBERSHIP) failed: %m");
+ res = -errno;
+ goto error_close;
+ }
+ }
+ return fd;
+
+error_close:
+ close(fd);
+ return res;
}
static void handle_iec61883_packet(struct stream *stream,
@@ -476,24 +548,3 @@ int stream_deactivate(struct stream *stream, uint64_t now)
}
return 0;
}
-
-int stream_activate_virtual(struct stream *stream, uint16_t index)
-{
- struct server *server = stream->server;
- int fd;
-
- if (stream->source == NULL) {
- fd = setup_socket(stream);
- if (fd < 0)
- return fd;
-
- stream->source = pw_loop_add_io(server->impl->loop, fd,
- SPA_IO_IN, true, on_socket_data, stream);
- if (stream->source == NULL) {
- close(fd);
- return -errno;
- }
- }
- pw_stream_set_active(stream->stream, true);
- return 0;
-}
diff --git a/src/modules/module-avb/stream.h b/src/modules/module-avb/stream.h
index 4cc02ddd3..f650cc216 100644
--- a/src/modules/module-avb/stream.h
+++ b/src/modules/module-avb/stream.h
@@ -78,6 +78,5 @@ void stream_destroy(struct stream *stream);
int stream_activate(struct stream *stream, uint16_t index, uint64_t now);
int stream_deactivate(struct stream *stream, uint64_t now);
-int stream_activate_virtual(struct stream *stream, uint16_t index);
#endif /* AVB_STREAM_H */
diff --git a/src/modules/module-client-device.c b/src/modules/module-client-device.c
index 658276eb9..c02a4058f 100644
--- a/src/modules/module-client-device.c
+++ b/src/modules/module-client-device.c
@@ -135,9 +135,9 @@ static void *create_object(void *_data,
goto error_properties;
}
- pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%u",
+ pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d",
pw_global_get_id(pw_impl_factory_get_global(factory)));
- pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%u",
+ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d",
pw_global_get_id(pw_impl_client_get_global(client)));
result = pw_client_device_new(device_resource, properties);
diff --git a/src/modules/module-client-device/resource-device.c b/src/modules/module-client-device/resource-device.c
index 8b3ddea99..ca00bfef9 100644
--- a/src/modules/module-client-device/resource-device.c
+++ b/src/modules/module-client-device/resource-device.c
@@ -108,7 +108,7 @@ struct pw_impl_device *pw_client_device_new(struct pw_resource *resource,
if (properties == NULL)
return NULL;
- pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%u",
+ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d",
pw_impl_client_get_info(client)->id);
device = pw_context_create_device(context, properties, sizeof(struct impl));
diff --git a/src/modules/module-client-node/client-node.c b/src/modules/module-client-node/client-node.c
index 1e104429c..9740e1c89 100644
--- a/src/modules/module-client-node/client-node.c
+++ b/src/modules/module-client-node/client-node.c
@@ -92,6 +92,7 @@ struct impl {
struct spa_node node;
+ struct spa_log *log;
struct spa_loop *data_loop;
struct spa_system *data_system;
@@ -282,7 +283,7 @@ static int clear_buffers(struct impl *impl, struct mix *mix)
for (i = 0; i < mix->n_buffers; i++) {
struct buffer *b = &mix->buffers[i];
- pw_log_debug("%p: clear buffer %d", impl, i);
+ spa_log_debug(impl->log, "%p: clear buffer %d", impl, i);
clear_buffer(impl, &b->buffer);
pw_memblock_unref(b->mem);
}
@@ -299,7 +300,7 @@ static void free_mix(struct port *p, struct mix *mix)
if (mix->n_buffers) {
/* this shouldn't happen */
- pw_log_warn("%p: mix port-id:%u freeing leaked buffers", impl, mix->mix_id - 1u);
+ spa_log_warn(impl->log, "%p: mix port-id:%u freeing leaked buffers", impl, mix->mix_id - 1u);
}
clear_buffers(impl, mix);
@@ -330,13 +331,11 @@ static int impl_node_enum_params(void *object, int seq,
struct spa_pod_dynamic_builder b;
struct spa_result_node_params result;
uint32_t count = 0;
+ bool found = false;
spa_return_val_if_fail(impl != NULL, -EINVAL);
spa_return_val_if_fail(num != 0, -EINVAL);
- if (!pw_param_info_find(impl->this.node->info.params, impl->this.node->info.n_params, id))
- return -ENOENT;
-
result.id = id;
result.next = 0;
@@ -352,6 +351,8 @@ static int impl_node_enum_params(void *object, int seq,
if (param == NULL || !spa_pod_is_object_id(param, id))
continue;
+ found = true;
+
if (result.index < start)
continue;
@@ -366,8 +367,7 @@ static int impl_node_enum_params(void *object, int seq,
if (count == num)
break;
}
-
- return 0;
+ return found ? 0 : -ENOENT;
}
static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
@@ -509,7 +509,7 @@ do_update_port(struct impl *impl,
const struct spa_port_info *info)
{
if (change_mask & PW_CLIENT_NODE_PORT_UPDATE_PARAMS) {
- pw_log_debug("%p: port %u update %d params", impl, port->id, n_params);
+ spa_log_debug(impl->log, "%p: port %u update %d params", impl, port->id, n_params);
update_params(&port->params, n_params, params);
}
@@ -543,7 +543,7 @@ static int mix_clear_cb(void *item, void *data)
static void
clear_port(struct impl *impl, struct port *port)
{
- pw_log_debug("%p: clear port %p", impl, port);
+ spa_log_debug(impl->log, "%p: clear port %p", impl, port);
do_update_port(impl, port,
PW_CLIENT_NODE_PORT_UPDATE_PARAMS |
@@ -599,6 +599,7 @@ node_port_enum_params(struct impl *impl, int seq,
struct spa_pod_dynamic_builder b;
struct spa_result_node_params result;
uint32_t count = 0;
+ bool found = false;
spa_return_val_if_fail(impl != NULL, -EINVAL);
spa_return_val_if_fail(num != 0, -EINVAL);
@@ -609,9 +610,6 @@ node_port_enum_params(struct impl *impl, int seq,
pw_log_debug("%p: seq:%d port %d.%d id:%u start:%u num:%u n_params:%d",
impl, seq, direction, port_id, id, start, num, port->params.n_params);
- if (!pw_param_info_find(port->port->info.params, port->port->info.n_params, id))
- return -ENOENT;
-
result.id = id;
result.next = 0;
@@ -627,6 +625,8 @@ node_port_enum_params(struct impl *impl, int seq,
if (param == NULL || !spa_pod_is_object_id(param, id))
continue;
+ found = true;
+
if (result.index < start)
continue;
@@ -641,7 +641,7 @@ node_port_enum_params(struct impl *impl, int seq,
if (count == num)
break;
}
- return 0;
+ return found ? 0 : -ENOENT;
}
static int
@@ -782,7 +782,7 @@ do_port_use_buffers(struct impl *impl,
if (n_buffers > MAX_BUFFERS)
return -ENOSPC;
- pw_log_debug("%p: %s port %d.%d use buffers %p %u flags:%08x", impl,
+ spa_log_debug(impl->log, "%p: %s port %d.%d use buffers %p %u flags:%08x", impl,
direction == SPA_DIRECTION_INPUT ? "input" : "output",
port_id, mix_id, buffers, n_buffers, flags);
@@ -852,7 +852,7 @@ do_port_use_buffers(struct impl *impl,
mb[i].mem_id = m->id;
mb[i].offset = SPA_PTRDIFF(baseptr, mem->map->ptr);
mb[i].size = SPA_PTRDIFF(endptr, baseptr);
- pw_log_debug("%p: buffer %d %d %d %d", impl, i, mb[i].mem_id,
+ spa_log_debug(impl->log, "%p: buffer %d %d %d %d", impl, i, mb[i].mem_id,
mb[i].offset, mb[i].size);
b->buffer.n_metas = SPA_MIN(buffers[i]->n_metas, MAX_METAS);
@@ -885,7 +885,7 @@ do_port_use_buffers(struct impl *impl,
if (d->flags & SPA_DATA_FLAG_WRITABLE)
flags |= PW_MEMBLOCK_FLAG_WRITABLE;
- pw_log_debug("mem %d type:%d fd:%d", j, d->type, (int)d->fd);
+ spa_log_debug(impl->log, "mem %d type:%d fd:%d", j, d->type, (int)d->fd);
m = pw_mempool_import(impl->client_pool,
flags, d->type, d->fd);
if (m == NULL)
@@ -896,14 +896,14 @@ do_port_use_buffers(struct impl *impl,
break;
}
case SPA_DATA_MemPtr:
- pw_log_debug("mem %d %zd", j, SPA_PTRDIFF(d->data, baseptr));
+ spa_log_debug(impl->log, "mem %d %zd", j, SPA_PTRDIFF(d->data, baseptr));
b->datas[j].data = SPA_INT_TO_PTR(SPA_PTRDIFF(d->data, baseptr));
SPA_FLAG_CLEAR(b->datas[j].flags, SPA_DATA_FLAG_MAPPABLE);
break;
default:
b->datas[j].type = SPA_ID_INVALID;
b->datas[j].data = NULL;
- pw_log_error("invalid memory type %d", d->type);
+ spa_log_error(impl->log, "invalid memory type %d", d->type);
break;
}
}
@@ -939,7 +939,7 @@ impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
spa_return_val_if_fail(impl != NULL, -EINVAL);
spa_return_val_if_fail(CHECK_PORT(impl, SPA_DIRECTION_OUTPUT, port_id), -EINVAL);
- pw_log_trace_fp("reuse buffer %d", buffer_id);
+ spa_log_trace_fp(impl->log, "reuse buffer %d", buffer_id);
return -ENOTSUP;
}
@@ -952,7 +952,7 @@ static int impl_node_process(void *object)
/* this should not be called, we call the exported node
* directly */
- pw_log_warn("exported node activation");
+ spa_log_warn(impl->log, "exported node activation");
spa_system_clock_gettime(impl->data_system, CLOCK_MONOTONIC, &ts);
n->rt.target.activation->status = PW_NODE_ACTIVATION_TRIGGERED;
n->rt.target.activation->signal_time = SPA_TIMESPEC_TO_NSEC(&ts);
@@ -1013,7 +1013,7 @@ client_node_port_update(void *data,
struct port *port;
bool remove;
- pw_log_debug("%p: got port update change:%08x params:%d",
+ spa_log_debug(impl->log, "%p: got port update change:%08x params:%d",
impl, change_mask, n_params);
remove = (change_mask == 0);
@@ -1051,7 +1051,7 @@ client_node_port_update(void *data,
static int client_node_set_active(void *data, bool active)
{
struct impl *impl = data;
- pw_log_debug("%p: active:%d", impl, active);
+ spa_log_debug(impl->log, "%p: active:%d", impl, active);
return pw_impl_node_set_active(impl->this.node, active);
}
@@ -1074,7 +1074,7 @@ static int client_node_port_buffers(void *data,
struct mix *mix;
uint32_t i, j;
- pw_log_debug("%p: %s port %d.%d buffers %p %u", impl,
+ spa_log_debug(impl->log, "%p: %s port %d.%d buffers %p %u", impl,
direction == SPA_DIRECTION_INPUT ? "input" : "output",
port_id, mix_id, buffers, n_buffers);
@@ -1102,7 +1102,7 @@ static int client_node_port_buffers(void *data,
oldbuf = b->outbuf;
newbuf = buffers[i];
- pw_log_debug("buffer %d n_datas:%d", i, newbuf->n_datas);
+ spa_log_debug(impl->log, "buffer %d n_datas:%d", i, newbuf->n_datas);
for (j = 0; j < b->buffer.n_datas; j++) {
struct spa_chunk *oldchunk = oldbuf->datas[j].chunk;
@@ -1111,7 +1111,7 @@ static int client_node_port_buffers(void *data,
if (d->type == SPA_DATA_MemFd &&
!SPA_FLAG_IS_SET(flags, SPA_DATA_FLAG_MAPPABLE)) {
- pw_log_debug("buffer:%d data:%d has non mappable MemFd, "
+ spa_log_debug(impl->log, "buffer:%d data:%d has non mappable MemFd, "
"fixing to ensure backwards compatibility.",
i, j);
flags |= SPA_DATA_FLAG_MAPPABLE;
@@ -1126,7 +1126,7 @@ static int client_node_port_buffers(void *data,
b->datas[j].flags = flags;
b->datas[j].fd = d->fd;
- pw_log_debug(" data %d type:%d fl:%08x fd:%d, offs:%d max:%d",
+ spa_log_debug(impl->log, " data %d type:%d fl:%08x fd:%d, offs:%d max:%d",
j, d->type, flags, (int) d->fd, d->mapoffset,
d->maxsize);
}
@@ -1153,7 +1153,7 @@ static void node_on_data_fd_events(struct spa_source *source)
struct impl *impl = source->data;
if (SPA_UNLIKELY(source->rmask & (SPA_IO_ERR | SPA_IO_HUP))) {
- pw_log_warn("%p: got error", impl);
+ spa_log_warn(impl->log, "%p: got error", impl);
return;
}
if (SPA_LIKELY(source->rmask & SPA_IO_IN)) {
@@ -1170,10 +1170,10 @@ static void node_on_data_fd_events(struct spa_source *source)
if (impl->resource && impl->resource->version < 5) {
struct pw_node_activation *a = node->rt.target.activation;
int status = a->state[0].status;
- pw_log_trace_fp("%p: got ready %d", impl, status);
+ spa_log_trace_fp(impl->log, "%p: got ready %d", impl, status);
spa_node_call_ready(&impl->callbacks, status);
} else {
- pw_log_trace_fp("%p: got complete", impl);
+ spa_log_trace_fp(impl->log, "%p: got complete", impl);
pw_impl_node_rt_emit_complete(node);
}
}
@@ -1764,7 +1764,7 @@ struct pw_impl_client_node *pw_impl_client_node_new(struct pw_resource *resource
goto error_exit_free;
}
- pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%u", client->global->id);
+ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d", client->global->id);
this = &impl->this;
@@ -1774,6 +1774,7 @@ struct pw_impl_client_node *pw_impl_client_node_new(struct pw_resource *resource
pw_log_debug("%p: new", &impl->node);
impl_init(impl, NULL);
+ impl->log = pw_log_get();
impl->resource = resource;
impl->client = client;
impl->client_pool = pw_impl_client_get_mempool(client);
diff --git a/src/modules/module-ffado-driver.c b/src/modules/module-ffado-driver.c
index c98b0efcd..05b9218ce 100644
--- a/src/modules/module-ffado-driver.c
+++ b/src/modules/module-ffado-driver.c
@@ -345,26 +345,34 @@ static void midi_to_ffado(struct port *p, float *src, uint32_t n_samples)
p->event_pos = 0;
while (spa_pod_parser_get_control_body(&parser, &c, &c_body) >= 0) {
- uint32_t j, size = c.value.size;
- const uint8_t *data = c_body;
+ uint8_t data[16];
+ int j, size;
+ size_t c_size = c.value.size;
+ uint64_t state = 0;
- if (c.type != SPA_CONTROL_Midi)
+ if (c.type != SPA_CONTROL_UMP)
continue;
if (index < c.offset)
index = SPA_ROUND_UP_N(c.offset, 8);
- for (j = 0; j < size; j++) {
- if (index >= n_samples) {
- /* keep events that don't fit for the next cycle */
- if (p->event_pos < sizeof(p->event_buffer))
- p->event_buffer[p->event_pos++] = data[j];
+ while (c_size > 0) {
+ size = spa_ump_to_midi((const uint32_t**)&c_body, &c_size, data, sizeof(data), &state);
+ if (size <= 0)
+ break;
+
+ for (j = 0; j < size; j++) {
+ if (index >= n_samples) {
+ /* keep events that don't fit for the next cycle */
+ if (p->event_pos < sizeof(p->event_buffer))
+ p->event_buffer[p->event_pos++] = data[j];
+ else
+ unhandled++;
+ }
else
- unhandled++;
+ dst[index] = 0x01000000 | (uint32_t) data[j];
+ index += 8;
}
- else
- dst[index] = 0x01000000 | (uint32_t) data[j];
- index += 8;
}
}
if (unhandled > 0)
@@ -489,8 +497,16 @@ static void ffado_to_midi(struct port *p, float *dst, uint32_t *src, uint32_t si
continue;
if (process_byte(p, i, data & 0xff, &frame, &bytes, &size)) {
- spa_pod_builder_control(&b, frame, SPA_CONTROL_Midi);
- spa_pod_builder_bytes(&b, bytes, size);
+ uint64_t state = 0;
+ while (size > 0) {
+ uint32_t ev[4];
+ int ev_size = spa_ump_from_midi(&bytes, &size, ev, sizeof(ev), 0, &state);
+ if (ev_size <= 0)
+ break;
+
+ spa_pod_builder_control(&b, frame, SPA_CONTROL_UMP);
+ spa_pod_builder_bytes(&b, ev, ev_size);
+ }
}
}
spa_pod_builder_pop(&b, &f);
diff --git a/src/modules/module-filter-chain.c b/src/modules/module-filter-chain.c
index c369810fe..64a92c5ac 100644
--- a/src/modules/module-filter-chain.c
+++ b/src/modules/module-filter-chain.c
@@ -128,7 +128,7 @@ extern struct spa_handle_factory spa_filter_graph_factory;
* # an example ladspa plugin
* type = ladspa
* name = pitch
- * plugin = "ladspa-rubberband"
+ * plugin = "/usr/lib64/ladspa/ladspa-rubberband.so"
* label = "rubberband-r3-pitchshifter-mono"
* control = {
* # controls are using the ladspa port names as seen in analyseplugin
@@ -398,10 +398,10 @@ extern struct spa_handle_factory spa_filter_graph_factory;
* between 64 and 256. When not specified, this value is
* computed automatically from the number of samples in the file.
* - `tailsize` specifies the size of the tail blocks to use in the FFT.
- * - `gain` the overall gain to apply to the IR file. Default 1.0
+ * - `gain` the overall gain to apply to the IR file.
* - `delay` The extra delay to add to the IR. A float number will be interpreted as seconds,
* and integer as samples. Using the delay in seconds is independent of the graph
- * and IR rate and is recommended. Default 0
+ * and IR rate and is recommended.
* - `filename` The IR to load or create. Possible values are:
* - `/hilbert` creates a [hilbert function](https://en.wikipedia.org/wiki/Hilbert_transform)
* that can be used to phase shift the signal by +/-90 degrees. The
@@ -1651,7 +1651,7 @@ static const struct pw_stream_events out_stream_events = {
static int setup_streams(struct impl *impl)
{
- int res = 0;
+ int res;
uint32_t i, n_params, *offs, flags;
struct pw_array offsets;
const struct spa_pod **params = NULL;
@@ -1702,19 +1702,6 @@ static int setup_streams(struct impl *impl)
spa_process_latency_build(&b.b,
SPA_PARAM_ProcessLatency, &impl->process_latency);
-
- if (impl->capture || impl->playback) {
- if ((offs = pw_array_add(&offsets, sizeof(uint32_t))) != NULL)
- *offs = b.b.state.offset;
-
- if (impl->capture)
- spa_format_audio_raw_build(&b.b,
- SPA_PARAM_EnumFormat, &impl->capture_info);
- else
- spa_format_audio_raw_build(&b.b,
- SPA_PARAM_EnumFormat, &impl->playback_info);
- }
-
n_params = pw_array_get_len(&offsets, uint32_t);
if (n_params == 0) {
res = -ENOMEM;
@@ -1730,6 +1717,8 @@ static int setup_streams(struct impl *impl)
params[i] = spa_pod_builder_deref(&b.b, offs[i]);
if (impl->capture) {
+ params[n_params++] = spa_format_audio_raw_build(&b.b,
+ SPA_PARAM_EnumFormat, &impl->capture_info);
flags = PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS;
@@ -1750,9 +1739,8 @@ static int setup_streams(struct impl *impl)
spa_pod_dynamic_builder_init(&b, NULL, 0, 4096);
}
if (impl->playback) {
- if (n_params == 0)
- params[n_params++] = spa_format_audio_raw_build(&b.b,
- SPA_PARAM_EnumFormat, &impl->playback_info);
+ params[n_params++] = spa_format_audio_raw_build(&b.b,
+ SPA_PARAM_EnumFormat, &impl->playback_info);
flags = PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
diff --git a/src/modules/module-jack-tunnel.c b/src/modules/module-jack-tunnel.c
index bee37da4f..0c0cee034 100644
--- a/src/modules/module-jack-tunnel.c
+++ b/src/modules/module-jack-tunnel.c
@@ -50,6 +50,7 @@
*
* - `jack.library`: the libjack to load, by default libjack.so.0 is searched in
* LIBJACK_PATH directories and then some standard library paths.
+ * Can be an absolute path.
* - `jack.server`: the name of the JACK server to tunnel to.
* - `jack.client-name`: the name of the JACK client.
* - `jack.connect`: if jack ports should be connected automatically. Can also be
@@ -242,16 +243,13 @@ static inline void do_volume(float *dst, const float *src, struct volume *vol, u
}
}
-static inline bool fix_midi_event(const uint8_t *data, size_t size, uint8_t dst[3])
+static inline void fix_midi_event(uint8_t *data, size_t size)
{
/* fixup NoteOn with vel 0 */
if (size > 2 && (data[0] & 0xF0) == 0x90 && data[2] == 0x00) {
- dst[0] = 0x80 + (data[0] & 0x0F);
- dst[1] = data[1];
- dst[2] = 0x40;
- return true;
+ data[0] = 0x80 + (data[0] & 0x0F);
+ data[2] = 0x40;
}
- return false;
}
static void midi_to_jack(struct impl *impl, float *dst, float *src, uint32_t n_samples)
@@ -262,6 +260,9 @@ static void midi_to_jack(struct impl *impl, float *dst, float *src, uint32_t n_s
struct spa_pod_control c;
const void *seq_body, *c_body;
int res;
+ bool in_sysex = false;
+ uint8_t tmp[n_samples * 4];
+ size_t tmp_size = 0;
jack.midi_clear_buffer(dst);
if (src == NULL)
@@ -273,20 +274,36 @@ static void midi_to_jack(struct impl *impl, float *dst, float *src, uint32_t n_s
return;
while (spa_pod_parser_get_control_body(&parser, &c, &c_body) >= 0) {
- uint32_t size = c.value.size;
- const uint8_t *data = c_body;
- uint8_t tmp[3];
+ int size;
+ size_t c_size = c.value.size;
+ uint64_t state = 0;
- if (c.type != SPA_CONTROL_Midi)
+ if (c.type != SPA_CONTROL_UMP)
continue;
- if (impl->fix_midi && fix_midi_event(data, size, tmp)) {
- data = tmp;
- size = 3;
+ while (c_size > 0) {
+ size = spa_ump_to_midi((const uint32_t**)&c_body, &c_size,
+ &tmp[tmp_size], sizeof(tmp) - tmp_size, &state);
+ if (size <= 0)
+ break;
+
+ if (impl->fix_midi)
+ fix_midi_event(&tmp[tmp_size], size);
+
+ if (!in_sysex && tmp[tmp_size] == 0xf0)
+ in_sysex = true;
+
+ tmp_size += size;
+ if (in_sysex && tmp[tmp_size-1] == 0xf7)
+ in_sysex = false;
+
+ if (!in_sysex) {
+ if ((res = jack.midi_event_write(dst, c.offset, tmp, tmp_size)) < 0)
+ pw_log_warn("midi %p: can't write event: %s", dst,
+ spa_strerror(res));
+ tmp_size = 0;
+ }
}
- if ((res = jack.midi_event_write(dst, c.offset, data, size)) < 0)
- pw_log_warn("midi %p: can't write event: %s", dst,
- spa_strerror(res));
}
}
@@ -302,11 +319,19 @@ static void jack_to_midi(float *dst, float *src, uint32_t size)
spa_pod_builder_push_sequence(&b, &f, 0);
for (i = 0; i < count; i++) {
jack_midi_event_t ev;
+ uint64_t state = 0;
jack.midi_event_get(&ev, src, i);
- spa_pod_builder_control(&b, ev.time, SPA_CONTROL_Midi);
- spa_pod_builder_bytes(&b, ev.buffer, ev.size);
+ while (ev.size > 0) {
+ uint32_t ump[4];
+ int ump_size = spa_ump_from_midi(&ev.buffer, &ev.size, ump, sizeof(ump), 0, &state);
+ if (ump_size <= 0)
+ break;
+
+ spa_pod_builder_control(&b, ev.time, SPA_CONTROL_UMP);
+ spa_pod_builder_bytes(&b, ump, ump_size);
+ }
}
spa_pod_builder_pop(&b, &f);
}
diff --git a/src/modules/module-jack-tunnel/weakjack.h b/src/modules/module-jack-tunnel/weakjack.h
index 6a3d9bc67..6d5b5503e 100644
--- a/src/modules/module-jack-tunnel/weakjack.h
+++ b/src/modules/module-jack-tunnel/weakjack.h
@@ -158,36 +158,34 @@ static inline int weakjack_load_by_path(struct weakjack *jack, const char *path)
static inline int weakjack_load(struct weakjack *jack, const char *lib)
{
int res = -ENOENT;
- const char *search_dirs, *p, *state = NULL;
- char path[PATH_MAX];
- size_t len;
- while ((p = strstr(lib, "../")) != NULL)
- lib = p + 3;
+ if (lib[0] != '/') {
+ const char *search_dirs, *p, *state = NULL;
+ char path[PATH_MAX];
+ size_t len;
- search_dirs = getenv("LIBJACK_PATH");
- if (!search_dirs)
- search_dirs = PREFIX "/lib64/:" PREFIX "/lib/:"
- "/usr/lib64/:/usr/lib/:" LIBDIR;
+ search_dirs = getenv("LIBJACK_PATH");
+ if (!search_dirs)
+ search_dirs = PREFIX "/lib64/:" PREFIX "/lib/:"
+ "/usr/lib64/:/usr/lib/:" LIBDIR;
- res = -ENAMETOOLONG;
+ while ((p = pw_split_walk(search_dirs, ":", &len, &state))) {
+ int pathlen;
- while ((p = pw_split_walk(search_dirs, ":", &len, &state))) {
- int pathlen;
-
- if (len == 0 || len >= sizeof(path))
- continue;
-
- if (strncmp(lib, p, len) == 0 && (lib[len-1] == '/' || lib[len] == '/'))
- pathlen = snprintf(path, sizeof(path), "%s", lib);
- else
+ if (len >= sizeof(path)) {
+ res = -ENAMETOOLONG;
+ continue;
+ }
pathlen = snprintf(path, sizeof(path), "%.*s/%s", (int) len, p, lib);
-
- if (pathlen < 0 || (size_t) pathlen >= sizeof(path))
- continue;
-
- if ((res = weakjack_load_by_path(jack, path)) == 0)
- break;
+ if (pathlen < 0 || (size_t) pathlen >= sizeof(path)) {
+ res = -ENAMETOOLONG;
+ continue;
+ }
+ if ((res = weakjack_load_by_path(jack, path)) == 0)
+ break;
+ }
+ } else {
+ res = weakjack_load_by_path(jack, lib);
}
return res;
}
diff --git a/src/modules/module-link-factory.c b/src/modules/module-link-factory.c
index 1f01078e8..d54df2758 100644
--- a/src/modules/module-link-factory.c
+++ b/src/modules/module-link-factory.c
@@ -485,12 +485,12 @@ static void *create_object(void *_data,
linger = pw_properties_get_bool(properties, PW_KEY_OBJECT_LINGER, false);
- pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%u",
+ pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d",
pw_impl_factory_get_info(d->factory)->id);
client = resource ? pw_resource_get_client(resource) : NULL;
if (client && !linger)
- pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%u",
+ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d",
pw_impl_client_get_info(client)->id);
if (!d->allow_passive)
diff --git a/src/modules/module-metadata.c b/src/modules/module-metadata.c
index 84b98a7ce..79d80d1b4 100644
--- a/src/modules/module-metadata.c
+++ b/src/modules/module-metadata.c
@@ -203,9 +203,9 @@ static void *create_object(void *_data,
if (properties == NULL)
return NULL;
- pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%u",
+ pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d",
pw_impl_factory_get_info(data->factory)->id);
- pw_properties_setf(properties, PW_KEY_MODULE_ID, "%u",
+ pw_properties_setf(properties, PW_KEY_MODULE_ID, "%d",
pw_impl_module_get_info(data->module)->id);
if (pw_properties_get(properties, PW_KEY_METADATA_NAME) == NULL)
@@ -218,7 +218,7 @@ static void *create_object(void *_data,
goto error_resource;
}
- pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%u",
+ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d",
pw_impl_client_get_info(client)->id);
result = pw_metadata_new(context, metadata_resource, properties);
diff --git a/src/modules/module-netjack2/peer.c b/src/modules/module-netjack2/peer.c
index 7547cc5b2..eacc1c95b 100644
--- a/src/modules/module-netjack2/peer.c
+++ b/src/modules/module-netjack2/peer.c
@@ -273,18 +273,39 @@ static inline void *n2j_midi_buffer_reserve(struct nj2_midi_buffer *buf,
}
static inline void n2j_midi_buffer_write(struct nj2_midi_buffer *buf,
- uint32_t offset, const void *data, uint32_t size, bool fix)
+ uint32_t offset, void *data, uint32_t size)
{
- uint8_t *ptr = n2j_midi_buffer_reserve(buf, offset, size);
- if (ptr != NULL) {
+ void *ptr = n2j_midi_buffer_reserve(buf, offset, size);
+ if (ptr != NULL)
memcpy(ptr, data, size);
- if (fix)
- fix_midi_event(ptr, size);
- }
else
buf->lost_events++;
}
+static inline void n2j_midi_buffer_append(struct nj2_midi_buffer *buf,
+ void *data, uint32_t size)
+{
+ struct nj2_midi_event *ev;
+ uint32_t old_size;
+ uint8_t *old_ptr, *new_ptr;
+
+ ev = &buf->event[--buf->event_count];
+ old_size = ev->size;
+ if (old_size <= MIDI_INLINE_MAX) {
+ old_ptr = ev->buffer;
+ } else {
+ buf->write_pos -= old_size;
+ old_ptr = SPA_PTROFF(buf, ev->offset, void);
+ }
+ new_ptr = n2j_midi_buffer_reserve(buf, ev->time, old_size + size);
+ if (new_ptr == NULL) {
+ buf->lost_events++;
+ } else {
+ memmove(new_ptr, old_ptr, old_size);
+ memcpy(new_ptr+old_size, data, size);
+ }
+}
+
static void midi_to_netjack2(struct netjack2_peer *peer,
struct nj2_midi_buffer *buf, float *src, uint32_t n_samples)
{
@@ -293,6 +314,7 @@ static void midi_to_netjack2(struct netjack2_peer *peer,
struct spa_pod_sequence seq;
struct spa_pod_control c;
const void *seq_body, *c_body;
+ bool in_sysex = false;
buf->magic = MIDI_BUFFER_MAGIC;
buf->buffer_size = peer->params.period_size * sizeof(float);
@@ -310,17 +332,39 @@ static void midi_to_netjack2(struct netjack2_peer *peer,
return;
while (spa_pod_parser_get_control_body(&parser, &c, &c_body) >= 0) {
- uint32_t size = c.value.size;
- const uint8_t *data = c_body;
+ int size;
+ uint8_t data[16];
+ bool was_sysex = in_sysex;
+ size_t c_size = c.value.size;
+ uint64_t state = 0;
- if (c.type != SPA_CONTROL_Midi)
+ if (c.type != SPA_CONTROL_UMP)
continue;
- if (c.offset >= n_samples) {
- buf->lost_events++;
- continue;
+ while (c_size > 0) {
+ size = spa_ump_to_midi((const uint32_t**)&c_body, &c_size, data, sizeof(data), &state);
+ if (size <= 0)
+ break;
+
+ if (c.offset >= n_samples) {
+ buf->lost_events++;
+ continue;
+ }
+
+ if (!in_sysex && data[0] == 0xf0)
+ in_sysex = true;
+
+ if (!in_sysex && peer->fix_midi)
+ fix_midi_event(data, size);
+
+ if (in_sysex && data[size-1] == 0xf7)
+ in_sysex = false;
+
+ if (was_sysex)
+ n2j_midi_buffer_append(buf, data, size);
+ else
+ n2j_midi_buffer_write(buf, c.offset, data, size);
}
- n2j_midi_buffer_write(buf, c.offset, data, size, peer->fix_midi);
}
if (buf->write_pos > 0)
memmove(SPA_PTROFF(buf, sizeof(*buf) + buf->event_count * sizeof(struct nj2_midi_event), void),
@@ -351,6 +395,8 @@ static inline void netjack2_to_midi(float *dst, uint32_t size, struct nj2_midi_b
for (i = 0; i < buf->event_count; i++) {
struct nj2_midi_event *ev = &buf->event[i];
uint8_t *data;
+ size_t s;
+ uint64_t state = 0;
if (ev->size <= MIDI_INLINE_MAX)
data = ev->buffer;
@@ -359,8 +405,17 @@ static inline void netjack2_to_midi(float *dst, uint32_t size, struct nj2_midi_b
else
continue;
- spa_pod_builder_control(&b, ev->time, SPA_CONTROL_Midi);
- spa_pod_builder_bytes(&b, data, ev->size);
+ s = ev->size;
+ while (s > 0) {
+ uint32_t ump[4];
+ int ump_size = spa_ump_from_midi(&data, &s, ump, sizeof(ump), 0, &state);
+ if (ump_size <= 0) {
+ pw_log_warn("invalid MIDI received: %s", spa_strerror(ump_size));
+ break;
+ }
+ spa_pod_builder_control(&b, ev->time, SPA_CONTROL_UMP);
+ spa_pod_builder_bytes(&b, ump, ump_size);
+ }
}
spa_pod_builder_pop(&b, &f);
}
diff --git a/src/modules/module-profiler.c b/src/modules/module-profiler.c
index 557ca2649..b49629ba9 100644
--- a/src/modules/module-profiler.c
+++ b/src/modules/module-profiler.c
@@ -166,7 +166,7 @@ static void do_flush_event(void *data, uint64_t count)
avail = spa_ringbuffer_get_read_index(&n->buffer, &idx);
- pw_log_trace("%p: node:%p avail %d", n, impl, avail);
+ pw_log_trace("%p: avail %d", impl, avail);
if (avail > 0) {
size_t size = total + avail + sizeof(struct spa_pod_struct);
@@ -559,7 +559,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
free(impl);
return -errno;
}
- pw_properties_setf(impl->properties, PW_KEY_OBJECT_ID, "%u", pw_global_get_id(impl->global));
+ pw_properties_setf(impl->properties, PW_KEY_OBJECT_ID, "%d", pw_global_get_id(impl->global));
pw_properties_setf(impl->properties, PW_KEY_OBJECT_SERIAL, "%"PRIu64,
pw_global_get_serial(impl->global));
diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c
index 2a0b2f438..98a43829b 100644
--- a/src/modules/module-protocol-native.c
+++ b/src/modules/module-protocol-native.c
@@ -663,7 +663,7 @@ static struct client_data *client_new(struct server *s, int fd)
}
#endif
- pw_properties_setf(props, PW_KEY_MODULE_ID, "%u", d->module->global->id);
+ pw_properties_setf(props, PW_KEY_MODULE_ID, "%d", d->module->global->id);
client = pw_context_create_client(s->this.core,
protocol, props, sizeof(struct client_data));
diff --git a/src/modules/module-protocol-native/connection.c b/src/modules/module-protocol-native/connection.c
index 15c739428..b376a0f69 100644
--- a/src/modules/module-protocol-native/connection.c
+++ b/src/modules/module-protocol-native/connection.c
@@ -536,7 +536,7 @@ static int prepare_packet(struct pw_protocol_native_connection *conn, struct buf
size -= impl->hdr_size;
buf->msg.fds = &buf->fds[buf->fds_offset];
- if (buf->msg.n_fds + buf->fds_offset > buf->n_fds)
+ if (buf->msg.n_fds + buf->fds_offset > MAX_FDS)
return -EPROTO;
if (size < len)
diff --git a/src/modules/module-protocol-native/test-connection.c b/src/modules/module-protocol-native/test-connection.c
index 0c82d2da8..eabcbbd67 100644
--- a/src/modules/module-protocol-native/test-connection.c
+++ b/src/modules/module-protocol-native/test-connection.c
@@ -3,7 +3,6 @@
/* SPDX-License-Identifier: MIT */
#include
-#include
#include
#include
@@ -166,87 +165,6 @@ static void test_reentering(struct pw_protocol_native_connection *in,
}
}
-/*
- * Test that a packet claiming more FDs in its header than were actually
- * sent via SCM_RIGHTS is rejected. Without the n_fds validation this
- * would cause the receiver to read uninitialised / stale FD values.
- */
-static void test_spoofed_fds(struct pw_protocol_native_connection *in,
- struct pw_protocol_native_connection *out)
-{
- const struct pw_protocol_native_message *msg;
- int res;
-
- /*
- * First, send a valid message through the normal API so that the
- * receiver's version handshake happens (it switches to HDR_SIZE=16
- * on the first message). Use a message with 0 FDs.
- */
- {
- struct spa_pod_builder *b;
- struct pw_protocol_native_message *wmsg;
-
- b = pw_protocol_native_connection_begin(out, 0, 1, &wmsg);
- spa_assert_se(b != NULL);
- spa_pod_builder_add_struct(b, SPA_POD_Int(0));
- pw_protocol_native_connection_end(out, b);
- pw_protocol_native_connection_flush(out);
-
- /* Consume it on the reading side */
- res = pw_protocol_native_connection_get_next(in, &msg);
- spa_assert_se(res == 1);
- }
-
- /*
- * Now craft a raw packet on the wire that claims n_fds=5 in the
- * header but send 0 actual FDs via SCM_RIGHTS.
- *
- * v3 header layout (16 bytes / 4 uint32s):
- * p[0] = id
- * p[1] = (opcode << 24) | (payload_size & 0xffffff)
- * p[2] = seq
- * p[3] = n_fds
- *
- * We need a minimal valid SPA pod as payload.
- */
- {
- /* Build a tiny SPA pod: struct { Int(0) } */
- uint8_t payload[32];
- struct spa_pod_builder pb;
-
- spa_pod_builder_init(&pb, payload, sizeof(payload));
- spa_pod_builder_add_struct(&pb, SPA_POD_Int(0));
-
- uint32_t payload_size = pb.state.offset;
- uint32_t header[4];
-
- spa_assert_se(payload_size <= sizeof(payload));
-
- header[0] = 1; /* id */
- header[1] = (5u << 24) | (payload_size & 0xffffff); /* opcode=5, size */
- header[2] = 0; /* seq */
- header[3] = 5; /* SPOOFED: claim 5 fds, send 0 */
-
- struct iovec iov[2];
- struct msghdr mh = { 0 };
-
- iov[0].iov_base = header;
- iov[0].iov_len = sizeof(header);
- iov[1].iov_base = payload;
- iov[1].iov_len = payload_size;
- mh.msg_iov = iov;
- mh.msg_iovlen = 2;
- /* No msg_control — 0 FDs via SCM_RIGHTS */
-
- ssize_t sent = sendmsg(out->fd, &mh, MSG_NOSIGNAL);
- spa_assert_se(sent == (ssize_t)(sizeof(header) + payload_size));
- }
-
- /* The receiver must reject this packet */
- res = pw_protocol_native_connection_get_next(in, &msg);
- spa_assert_se(res == -EPROTO);
-}
-
int main(int argc, char *argv[])
{
struct pw_main_loop *loop;
@@ -280,26 +198,6 @@ int main(int argc, char *argv[])
pw_protocol_native_connection_destroy(in);
pw_protocol_native_connection_destroy(out);
-
- /* test_spoofed_fds needs its own connection pair */
- {
- int fds2[2];
- struct pw_protocol_native_connection *in2, *out2;
-
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds2) < 0)
- spa_assert_not_reached();
-
- in2 = pw_protocol_native_connection_new(context, fds2[0]);
- spa_assert_se(in2 != NULL);
- out2 = pw_protocol_native_connection_new(context, fds2[1]);
- spa_assert_se(out2 != NULL);
-
- test_spoofed_fds(in2, out2);
-
- pw_protocol_native_connection_destroy(in2);
- pw_protocol_native_connection_destroy(out2);
- }
-
pw_context_destroy(context);
pw_main_loop_destroy(loop);
diff --git a/src/modules/module-protocol-pulse/modules/module-zeroconf-publish.c b/src/modules/module-protocol-pulse/modules/module-zeroconf-publish.c
index ffb5aa0fe..d4425a3fe 100644
--- a/src/modules/module-protocol-pulse/modules/module-zeroconf-publish.c
+++ b/src/modules/module-protocol-pulse/modules/module-zeroconf-publish.c
@@ -15,7 +15,14 @@
#include "../module.h"
#include "../pulse-server.h"
#include "../server.h"
-#include "../../zeroconf-utils/zeroconf.h"
+#include "../../module-zeroconf-discover/avahi-poll.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
/** \page page_pulse_module_zeroconf_publish Zeroconf Publish
*
@@ -45,17 +52,32 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
#define SERVICE_DATA_ID "module-zeroconf-publish.service"
+enum service_subtype {
+ SUBTYPE_HARDWARE,
+ SUBTYPE_VIRTUAL,
+ SUBTYPE_MONITOR
+};
+
struct service {
struct spa_list link;
struct module_zeroconf_publish_data *userdata;
+ AvahiEntryGroup *entry_group;
+ AvahiStringList *txt;
struct server *server;
+ const char *service_type;
+ enum service_subtype subtype;
+
+ char *name;
+ bool is_sink;
+
struct sample_spec ss;
struct channel_map cm;
struct pw_properties *props;
+ char service_name[AVAHI_LABEL_MAX];
unsigned published:1;
};
@@ -69,8 +91,8 @@ struct module_zeroconf_publish_data {
struct spa_hook manager_listener;
struct spa_hook impl_listener;
- struct pw_zeroconf *zeroconf;
- struct spa_hook zeroconf_listener;
+ AvahiPoll *avahi_poll;
+ AvahiClient *client;
/* lists of services */
struct spa_list pending;
@@ -94,43 +116,47 @@ static const struct pw_core_events core_events = {
.error = on_core_error,
};
-static void unpublish_service(struct service *s)
+static void get_service_name(struct pw_manager_object *o, char *buf, size_t length)
{
- const char *device;
+ const char *hn, *un, *n;
- spa_list_remove(&s->link);
- spa_list_append(&s->userdata->pending, &s->link);
- s->published = false;
- s->server = NULL;
+ hn = pw_get_host_name();
+ un = pw_get_user_name();
+ n = pw_properties_get(o->props, PW_KEY_NODE_DESCRIPTION);
- device = pw_properties_get(s->props, "device");
-
- pw_log_info("unpublished service: %s", device);
-
- pw_zeroconf_set_announce(s->userdata->zeroconf, s, NULL);
-}
-
-static void unpublish_all_services(struct module_zeroconf_publish_data *d)
-{
- struct service *s;
- spa_list_consume(s, &d->published, link)
- unpublish_service(s);
+ snprintf(buf, length, "%s@%s: %s", un, hn, n);
}
static void service_free(struct service *s)
{
pw_log_debug("service %p: free", s);
- if (s->published)
- unpublish_service(s);
+ if (s->entry_group)
+ avahi_entry_group_free(s->entry_group);
+
+ if (s->name)
+ free(s->name);
pw_properties_free(s->props);
+ avahi_string_list_free(s->txt);
spa_list_remove(&s->link);
- /* no need to free, the service is added as custom
- * data on the object */
}
-#define PA_CHANNEL_MAP_SNPRINT_MAX (CHANNELS_MAX * 32)
+static void unpublish_service(struct service *s)
+{
+ spa_list_remove(&s->link);
+ spa_list_append(&s->userdata->pending, &s->link);
+ s->published = false;
+ s->server = NULL;
+}
+
+static void unpublish_all_services(struct module_zeroconf_publish_data *d)
+{
+ struct service *s;
+
+ spa_list_consume(s, &d->published, link)
+ unpublish_service(s);
+}
static char* channel_map_snprint(char *s, size_t l, const struct channel_map *map)
{
@@ -162,39 +188,6 @@ static char* channel_map_snprint(char *s, size_t l, const struct channel_map *ma
return s;
}
-static void txt_record_server_data(struct pw_core_info *info, struct pw_properties *props)
-{
- struct utsname u;
-
- spa_assert(info);
-
- pw_properties_set(props, "server-version", PACKAGE_NAME" "PACKAGE_VERSION);
- pw_properties_set(props, "user-name", pw_get_user_name());
- pw_properties_set(props, "fqdn", pw_get_host_name());
- pw_properties_setf(props, "cookie", "0x%08x", info->cookie);
- if (uname(&u) >= 0)
- pw_properties_setf(props, "uname", "%s %s %s", u.sysname, u.machine, u.release);
-}
-
-static void fill_service_txt(const struct service *s, const struct pw_properties *props)
-{
- static const struct mapping {
- const char *pw_key, *txt_key;
- } mappings[] = {
- { PW_KEY_NODE_DESCRIPTION, "description" },
- { PW_KEY_DEVICE_VENDOR_NAME, "vendor-name" },
- { PW_KEY_DEVICE_PRODUCT_NAME, "product-name" },
- { PW_KEY_DEVICE_CLASS, "class" },
- { PW_KEY_DEVICE_FORM_FACTOR, "form-factor" },
- { PW_KEY_DEVICE_ICON_NAME, "icon-name" },
- };
- SPA_FOR_EACH_ELEMENT_VAR(mappings, m) {
- const char *value = pw_properties_get(props, m->pw_key);
- if (value != NULL)
- pw_properties_set(s->props, m->txt_key, value);
- }
-}
-
static void fill_service_data(struct module_zeroconf_publish_data *d, struct service *s,
struct pw_manager_object *o)
{
@@ -207,10 +200,6 @@ static void fill_service_data(struct module_zeroconf_publish_data *d, struct ser
struct card_info card_info = CARD_INFO_INIT;
struct device_info dev_info;
uint32_t flags = 0;
- const char *service_type, *subtype, *subtype_service[2];
- uint32_t n_subtype = 0;
- char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
-
if (info == NULL || info->props == NULL)
return;
@@ -239,49 +228,19 @@ static void fill_service_data(struct module_zeroconf_publish_data *d, struct ser
s->ss = dev_info.ss;
s->cm = dev_info.map;
-
- s->props = pw_properties_new(NULL, NULL);
-
- txt_record_server_data(s->userdata->manager->info, s->props);
+ s->name = strdup(name);
+ s->props = pw_properties_copy(o->props);
if (is_sink) {
- service_type = SERVICE_TYPE_SINK;
- if (flags & SINK_HARDWARE) {
- subtype = "hardware";
- subtype_service[n_subtype++] = SERVICE_SUBTYPE_SINK_HARDWARE;
- } else {
- subtype = "virtual";
- subtype_service[n_subtype++] = SERVICE_SUBTYPE_SINK_VIRTUAL;
- }
+ s->is_sink = true;
+ s->service_type = SERVICE_TYPE_SINK;
+ s->subtype = flags & SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
} else if (is_source) {
- service_type = SERVICE_TYPE_SOURCE;
- if (flags & SOURCE_HARDWARE) {
- subtype = "hardware";
- subtype_service[n_subtype++] = SERVICE_SUBTYPE_SOURCE_HARDWARE;
- } else {
- subtype = "virtual";
- subtype_service[n_subtype++] = SERVICE_SUBTYPE_SOURCE_VIRTUAL;
- }
- subtype_service[n_subtype++] = SERVICE_SUBTYPE_SOURCE_NON_MONITOR;
+ s->is_sink = false;
+ s->service_type = SERVICE_TYPE_SOURCE;
+ s->subtype = flags & SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
} else
spa_assert_not_reached();
-
- pw_properties_set(s->props, "device", name);
- pw_properties_setf(s->props, "rate", "%u", s->ss.rate);
- pw_properties_setf(s->props, "channels", "%u", s->ss.channels);
- pw_properties_set(s->props, "format", format_id2paname(s->ss.format));
- pw_properties_set(s->props, "channel_map", channel_map_snprint(cm, sizeof(cm), &s->cm));
- pw_properties_set(s->props, "subtype", subtype);
-
- pw_properties_setf(s->props, PW_KEY_ZEROCONF_NAME, "%s@%s: %s",
- pw_get_user_name(), pw_get_host_name(), desc);
- pw_properties_set(s->props, PW_KEY_ZEROCONF_TYPE, service_type);
- pw_properties_setf(s->props, PW_KEY_ZEROCONF_SUBTYPES, "[ %s%s%s ]",
- n_subtype > 0 ? subtype_service[0] : "",
- n_subtype > 1 ? ", " : "",
- n_subtype > 1 ? subtype_service[1] : "");
-
- fill_service_txt(s, o->props);
}
static struct service *create_service(struct module_zeroconf_publish_data *d, struct pw_manager_object *o)
@@ -293,6 +252,8 @@ static struct service *create_service(struct module_zeroconf_publish_data *d, st
return NULL;
s->userdata = d;
+ s->entry_group = NULL;
+ get_service_name(o, s->service_name, sizeof(s->service_name));
spa_list_append(&d->pending, &s->link);
fill_service_data(d, s, o);
@@ -302,6 +263,127 @@ static struct service *create_service(struct module_zeroconf_publish_data *d, st
return s;
}
+static AvahiStringList* txt_record_server_data(struct pw_core_info *info, AvahiStringList *l)
+{
+ const char *t;
+ struct utsname u;
+
+ spa_assert(info);
+
+ l = avahi_string_list_add_pair(l, "server-version", PACKAGE_NAME" "PACKAGE_VERSION);
+
+ t = pw_get_user_name();
+ l = avahi_string_list_add_pair(l, "user-name", t);
+
+ if (uname(&u) >= 0) {
+ char sysname[sizeof(u.sysname) + sizeof(u.machine) + sizeof(u.release)];
+
+ snprintf(sysname, sizeof(sysname), "%s %s %s", u.sysname, u.machine, u.release);
+ l = avahi_string_list_add_pair(l, "uname", sysname);
+ }
+
+ t = pw_get_host_name();
+ l = avahi_string_list_add_pair(l, "fqdn", t);
+ l = avahi_string_list_add_printf(l, "cookie=0x%08x", info->cookie);
+
+ return l;
+}
+
+static void clear_entry_group(struct service *s)
+{
+ if (s->entry_group == NULL)
+ return;
+
+ avahi_entry_group_free(s->entry_group);
+ s->entry_group = NULL;
+}
+
+static void publish_service(struct service *s);
+
+static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata)
+{
+ struct service *s = userdata;
+
+ spa_assert(s);
+ if (!s->published) {
+ pw_log_info("cancel unpublished service: %s", s->service_name);
+ clear_entry_group(s);
+ return;
+ }
+
+ switch (state) {
+ case AVAHI_ENTRY_GROUP_ESTABLISHED:
+ pw_log_info("established service: %s", s->service_name);
+ break;
+ case AVAHI_ENTRY_GROUP_COLLISION:
+ {
+ char *t;
+
+ t = avahi_alternative_service_name(s->service_name);
+ pw_log_info("service name collision: renaming '%s' to '%s'", s->service_name, t);
+ snprintf(s->service_name, sizeof(s->service_name), "%s", t);
+ avahi_free(t);
+
+ unpublish_service(s);
+ publish_service(s);
+ break;
+ }
+ case AVAHI_ENTRY_GROUP_FAILURE:
+ pw_log_error("failed to establish service '%s': %s",
+ s->service_name,
+ avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
+ unpublish_service(s);
+ clear_entry_group(s);
+ break;
+
+ case AVAHI_ENTRY_GROUP_UNCOMMITED:
+ case AVAHI_ENTRY_GROUP_REGISTERING:
+ break;
+ }
+}
+
+#define PA_CHANNEL_MAP_SNPRINT_MAX (CHANNELS_MAX * 32)
+
+static AvahiStringList *get_service_txt(const struct service *s)
+{
+ static const char * const subtype_text[] = {
+ [SUBTYPE_HARDWARE] = "hardware",
+ [SUBTYPE_VIRTUAL] = "virtual",
+ [SUBTYPE_MONITOR] = "monitor"
+ };
+
+ static const struct mapping {
+ const char *pw_key, *txt_key;
+ } mappings[] = {
+ { PW_KEY_NODE_DESCRIPTION, "description" },
+ { PW_KEY_DEVICE_VENDOR_NAME, "vendor-name" },
+ { PW_KEY_DEVICE_PRODUCT_NAME, "product-name" },
+ { PW_KEY_DEVICE_CLASS, "class" },
+ { PW_KEY_DEVICE_FORM_FACTOR, "form-factor" },
+ { PW_KEY_DEVICE_ICON_NAME, "icon-name" },
+ };
+
+ char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ AvahiStringList *txt = NULL;
+
+ txt = txt_record_server_data(s->userdata->manager->info, txt);
+
+ txt = avahi_string_list_add_pair(txt, "device", s->name);
+ txt = avahi_string_list_add_printf(txt, "rate=%u", s->ss.rate);
+ txt = avahi_string_list_add_printf(txt, "channels=%u", s->ss.channels);
+ txt = avahi_string_list_add_pair(txt, "format", format_id2paname(s->ss.format));
+ txt = avahi_string_list_add_pair(txt, "channel_map", channel_map_snprint(cm, sizeof(cm), &s->cm));
+ txt = avahi_string_list_add_pair(txt, "subtype", subtype_text[s->subtype]);
+
+ SPA_FOR_EACH_ELEMENT_VAR(mappings, m) {
+ const char *value = pw_properties_get(s->props, m->pw_key);
+ if (value != NULL)
+ txt = avahi_string_list_add_pair(txt, m->txt_key, value);
+ }
+
+ return txt;
+}
+
static struct server *find_server(struct service *s, int *proto, uint16_t *port)
{
struct module_zeroconf_publish_data *d = s->userdata;
@@ -310,47 +392,109 @@ static struct server *find_server(struct service *s, int *proto, uint16_t *port)
spa_list_for_each(server, &impl->servers, link) {
if (server->addr.ss_family == AF_INET) {
- *proto = 4;
+ *proto = AVAHI_PROTO_INET;
*port = ntohs(((struct sockaddr_in*) &server->addr)->sin_port);
return server;
} else if (server->addr.ss_family == AF_INET6) {
- *proto = 6;
+ *proto = AVAHI_PROTO_INET6;
*port = ntohs(((struct sockaddr_in6*) &server->addr)->sin6_port);
return server;
}
}
+
return NULL;
}
static void publish_service(struct service *s)
{
struct module_zeroconf_publish_data *d = s->userdata;
- int proto, res;
+ int proto;
uint16_t port;
- struct server *server = find_server(s, &proto, &port);
- const char *device;
+ struct server *server = find_server(s, &proto, &port);
if (!server)
return;
- device = pw_properties_get(s->props, "device");
-
pw_log_debug("found server:%p proto:%d port:%d", server, proto, port);
- pw_properties_setf(s->props, PW_KEY_ZEROCONF_PROTO, "%d", proto);
- pw_properties_setf(s->props, PW_KEY_ZEROCONF_PORT, "%d", port);
-
- if ((res = pw_zeroconf_set_announce(s->userdata->zeroconf, s, &s->props->dict)) < 0) {
- pw_log_error("failed to announce service %s: %s", device, spa_strerror(res));
+ if (!d->client || avahi_client_get_state(d->client) != AVAHI_CLIENT_S_RUNNING)
return;
+
+ s->published = true;
+ if (!s->entry_group) {
+ s->entry_group = avahi_entry_group_new(d->client, service_entry_group_callback, s);
+ if (s->entry_group == NULL) {
+ pw_log_error("avahi_entry_group_new(): %s",
+ avahi_strerror(avahi_client_errno(d->client)));
+ goto error;
+ }
+ } else {
+ avahi_entry_group_reset(s->entry_group);
+ }
+
+ if (s->txt == NULL)
+ s->txt = get_service_txt(s);
+
+ if (avahi_entry_group_add_service_strlst(
+ s->entry_group,
+ AVAHI_IF_UNSPEC, proto,
+ 0,
+ s->service_name,
+ s->service_type,
+ NULL,
+ NULL,
+ port,
+ s->txt) < 0) {
+ pw_log_error("avahi_entry_group_add_service_strlst(): %s",
+ avahi_strerror(avahi_client_errno(d->client)));
+ goto error;
+ }
+
+ if (avahi_entry_group_add_service_subtype(
+ s->entry_group,
+ AVAHI_IF_UNSPEC, proto,
+ 0,
+ s->service_name,
+ s->service_type,
+ NULL,
+ s->is_sink ? (s->subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SINK_HARDWARE : SERVICE_SUBTYPE_SINK_VIRTUAL) :
+ (s->subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SOURCE_HARDWARE : (s->subtype == SUBTYPE_VIRTUAL ? SERVICE_SUBTYPE_SOURCE_VIRTUAL : SERVICE_SUBTYPE_SOURCE_MONITOR))) < 0) {
+
+ pw_log_error("avahi_entry_group_add_service_subtype(): %s",
+ avahi_strerror(avahi_client_errno(d->client)));
+ goto error;
+ }
+
+ if (!s->is_sink && s->subtype != SUBTYPE_MONITOR) {
+ if (avahi_entry_group_add_service_subtype(
+ s->entry_group,
+ AVAHI_IF_UNSPEC, proto,
+ 0,
+ s->service_name,
+ SERVICE_TYPE_SOURCE,
+ NULL,
+ SERVICE_SUBTYPE_SOURCE_NON_MONITOR) < 0) {
+ pw_log_error("avahi_entry_group_add_service_subtype(): %s",
+ avahi_strerror(avahi_client_errno(d->client)));
+ goto error;
+ }
+ }
+
+ if (avahi_entry_group_commit(s->entry_group) < 0) {
+ pw_log_error("avahi_entry_group_commit(): %s",
+ avahi_strerror(avahi_client_errno(d->client)));
+ goto error;
}
spa_list_remove(&s->link);
spa_list_append(&d->published, &s->link);
- s->published = true;
s->server = server;
- pw_log_info("published service: %s", device);
+ pw_log_info("created service: %s", s->service_name);
+ return;
+
+error:
+ s->published = false;
return;
}
@@ -362,6 +506,62 @@ static void publish_pending(struct module_zeroconf_publish_data *data)
publish_service(s);
}
+static void clear_pending_entry_groups(struct module_zeroconf_publish_data *data)
+{
+ struct service *s;
+
+ spa_list_for_each(s, &data->pending, link)
+ clear_entry_group(s);
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, void *d)
+{
+ struct module_zeroconf_publish_data *data = d;
+
+ spa_assert(c);
+ spa_assert(data);
+
+ data->client = c;
+
+ switch (state) {
+ case AVAHI_CLIENT_S_RUNNING:
+ pw_log_info("the avahi daemon is up and running");
+ publish_pending(data);
+ break;
+ case AVAHI_CLIENT_S_COLLISION:
+ pw_log_error("host name collision");
+ unpublish_all_services(d);
+ break;
+ case AVAHI_CLIENT_FAILURE:
+ {
+ int err = avahi_client_errno(data->client);
+
+ pw_log_error("avahi client failure: %s", avahi_strerror(err));
+
+ unpublish_all_services(data);
+ clear_pending_entry_groups(data);
+ avahi_client_free(data->client);
+ data->client = NULL;
+
+ if (err == AVAHI_ERR_DISCONNECTED) {
+ data->client = avahi_client_new(data->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, data, &err);
+ if (data->client == NULL)
+ pw_log_error("failed to create avahi client: %s", avahi_strerror(err));
+ }
+
+ if (data->client == NULL)
+ module_schedule_unload(data->module);
+
+ break;
+ }
+ case AVAHI_CLIENT_CONNECTING:
+ pw_log_info("connecting to the avahi daemon...");
+ break;
+ default:
+ break;
+ }
+}
+
static void manager_removed(void *d, struct pw_manager_object *o)
{
if (!pw_manager_object_is_sink(o) && !pw_manager_object_is_source(o))
@@ -424,6 +624,7 @@ static void impl_server_stopped(void *data, struct server *server)
if (s->server == server)
unpublish_service(s);
}
+
publish_pending(d);
}
@@ -433,18 +634,10 @@ static const struct impl_events impl_events = {
.server_stopped = impl_server_stopped,
};
-static void on_zeroconf_error(void *data, int err, const char *message)
-{
- pw_log_error("got zeroconf error %d: %s", err, message);
-}
-static const struct pw_zeroconf_events zeroconf_events = {
- PW_VERSION_ZEROCONF_EVENTS,
- .error = on_zeroconf_error,
-};
-
static int module_zeroconf_publish_load(struct module *module)
{
struct module_zeroconf_publish_data *data = module->user_data;
+ int error;
data->core = pw_context_connect(module->impl->context, NULL, 0);
if (data->core == NULL) {
@@ -456,22 +649,24 @@ static int module_zeroconf_publish_load(struct module *module)
&data->core_listener,
&core_events, data);
+ data->avahi_poll = pw_avahi_poll_new(module->impl->context);
+
+ data->client = avahi_client_new(data->avahi_poll, AVAHI_CLIENT_NO_FAIL,
+ client_callback, data, &error);
+ if (!data->client) {
+ pw_log_error("failed to create avahi client: %s", avahi_strerror(error));
+ return -errno;
+ }
+
data->manager = pw_manager_new(data->core);
if (data->manager == NULL) {
pw_log_error("failed to create pipewire manager: %m");
return -errno;
}
+
pw_manager_add_listener(data->manager, &data->manager_listener,
&manager_events, data);
- data->zeroconf = pw_zeroconf_new(module->impl->context, NULL);
- if (!data->zeroconf) {
- pw_log_error("failed to create zeroconf: %m");
- return -errno;
- }
- pw_zeroconf_add_listener(data->zeroconf, &data->zeroconf_listener,
- &zeroconf_events, data);
-
impl_add_listener(module->impl, &data->impl_listener, &impl_events, data);
return 0;
@@ -489,18 +684,22 @@ static int module_zeroconf_publish_unload(struct module *module)
spa_list_consume(s, &d->pending, link)
service_free(s);
- if (d->zeroconf) {
- spa_hook_remove(&d->zeroconf_listener);
- pw_zeroconf_destroy(d->zeroconf);
- }
+ if (d->client)
+ avahi_client_free(d->client);
+
+ if (d->avahi_poll)
+ pw_avahi_poll_free(d->avahi_poll);
+
if (d->manager != NULL) {
spa_hook_remove(&d->manager_listener);
pw_manager_destroy(d->manager);
}
+
if (d->core != NULL) {
spa_hook_remove(&d->core_listener);
pw_core_disconnect(d->core);
}
+
return 0;
}
diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c
index 38d967777..59610ef57 100644
--- a/src/modules/module-protocol-pulse/pulse-server.c
+++ b/src/modules/module-protocol-pulse/pulse-server.c
@@ -1258,6 +1258,8 @@ static void stream_param_changed(void *data, uint32_t id, const struct spa_pod *
pw_stream_set_control(stream->stream,
SPA_PROP_mute, 1, &val, 0);
}
+ if (stream->corked)
+ stream_set_paused(stream, true, "cork after create");
/* if peer exists, reply immediately, otherwise reply when the link is created */
peer = find_linked(stream->client->manager, stream->id, stream->direction);
@@ -1619,7 +1621,7 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui
struct pw_manager_object *o;
bool is_monitor;
- props = pw_properties_new(NULL, NULL);
+ props = pw_properties_copy(client->props);
if (props == NULL)
goto error_errno;
@@ -1779,7 +1781,6 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui
goto error_errno;
stream->corked = corked;
- stream->is_paused = corked;
stream->adjust_latency = adjust_latency;
stream->early_requests = early_requests;
stream->volume = volume;
@@ -1804,8 +1805,6 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui
flags = 0;
if (no_move)
flags |= PW_STREAM_FLAG_DONT_RECONNECT;
- if (corked)
- flags |= PW_STREAM_FLAG_INACTIVE;
if (sink_name != NULL) {
if (o != NULL)
@@ -1908,7 +1907,7 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint
struct pw_manager_object *o;
bool is_monitor = false;
- props = pw_properties_new(NULL, NULL);
+ props = pw_properties_copy(client->props);
if (props == NULL)
goto error_errno;
@@ -2060,7 +2059,6 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint
goto error_errno;
stream->corked = corked;
- stream->is_paused = corked;
stream->adjust_latency = adjust_latency;
stream->early_requests = early_requests;
stream->volume = volume;
@@ -2088,8 +2086,6 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint
flags = 0;
if (no_move)
flags |= PW_STREAM_FLAG_DONT_RECONNECT;
- if (corked)
- flags |= PW_STREAM_FLAG_INACTIVE;
if (direct_on_input_idx != SPA_ID_INVALID) {
dont_inhibit_auto_suspend = false;
@@ -2302,7 +2298,7 @@ static int do_create_upload_stream(struct client *client, uint32_t command, uint
struct message *reply;
int res;
- if ((props = pw_properties_new(NULL, NULL)) == NULL)
+ if ((props = pw_properties_copy(client->props)) == NULL)
goto error_errno;
if ((res = message_get(m,
@@ -4072,45 +4068,6 @@ static const char *get_media_name(struct pw_node_info *info)
return media_name;
}
-static int fill_node_info_proplist(struct message *m, const struct spa_dict *node_props,
- const struct pw_manager_object *client)
-{
- struct pw_client_info *client_info = client ? client->info : NULL;
- uint32_t n_items, n;
- struct spa_dict dict, *client_props = NULL;
- const struct spa_dict_item *it;
- struct spa_dict_item *items, *it2;
-
- n_items = node_props->n_items;
- if (client_info && client_info->props) {
- client_props = client_info->props;
- n_items += client_props->n_items;
- }
-
- dict.n_items = n = 0;
- dict.items = items = alloca(n_items * sizeof(struct spa_dict_item));
-
- spa_dict_for_each(it, node_props)
- items[n++] = *it;
- dict.n_items = n;
-
- if (client_props) {
- spa_dict_for_each(it, client_props) {
- if (spa_streq(it->key, PW_KEY_OBJECT_ID) ||
- spa_streq(it->key, PW_KEY_OBJECT_SERIAL))
- continue;
-
- if ((it2 = (struct spa_dict_item*)spa_dict_lookup_item(&dict, it->key)))
- it2->value = it->value;
- else
- items[n++] = *it;
- }
- dict.n_items = n;
- }
- message_put(m, TAG_PROPLIST, &dict, TAG_INVALID);
- return 0;
-}
-
static int fill_sink_input_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
@@ -4171,16 +4128,10 @@ static int fill_sink_input_info(struct client *client, struct message *m,
message_put(m,
TAG_BOOLEAN, dev_info.volume_info.mute, /* muted */
TAG_INVALID);
- if (client->version >= 13) {
- int res;
- struct pw_manager_object *c = NULL;
- if (client_id != SPA_ID_INVALID) {
- struct selector sel = { .id = client_id, .type = pw_manager_object_is_client, };
- c = select_object(manager, &sel);
- }
- if ((res = fill_node_info_proplist(m, info->props, c)) < 0)
- return res;
- }
+ if (client->version >= 13)
+ message_put(m,
+ TAG_PROPLIST, info->props,
+ TAG_INVALID);
if (client->version >= 19)
message_put(m,
TAG_BOOLEAN, corked, /* corked */
@@ -4256,16 +4207,10 @@ static int fill_source_output_info(struct client *client, struct message *m,
TAG_STRING, "PipeWire", /* resample method */
TAG_STRING, "PipeWire", /* driver */
TAG_INVALID);
- if (client->version >= 13) {
- int res;
- struct pw_manager_object *c = NULL;
- if (client_id != SPA_ID_INVALID) {
- struct selector sel = { .id = client_id, .type = pw_manager_object_is_client, };
- c = select_object(manager, &sel);
- }
- if ((res = fill_node_info_proplist(m, info->props, c)) < 0)
- return res;
- }
+ if (client->version >= 13)
+ message_put(m,
+ TAG_PROPLIST, info->props,
+ TAG_INVALID);
if (client->version >= 19)
message_put(m,
TAG_BOOLEAN, corked, /* corked */
@@ -4754,6 +4699,7 @@ static int do_set_profile(struct client *client, uint32_t command, uint32_t tag,
static int do_set_default(struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
struct pw_manager *manager = client->manager;
+ struct pw_manager_object *o;
const char *name, *str;
int res;
bool sink = command == COMMAND_SET_DEFAULT_SINK;
@@ -4770,10 +4716,10 @@ static int do_set_default(struct client *client, uint32_t command, uint32_t tag,
if (spa_streq(name, "@NONE@"))
name = NULL;
+ if (name != NULL && (o = find_device(client, SPA_ID_INVALID, name, sink, NULL)) == NULL)
+ return -ENOENT;
+
if (name != NULL) {
- struct pw_manager_object *o;
- if ((o = find_device(client, SPA_ID_INVALID, name, sink, NULL)) == NULL)
- return -ENOENT;
if (o->props && (str = pw_properties_get(o->props, PW_KEY_NODE_NAME)) != NULL)
name = str;
else if (spa_strendswith(name, ".monitor"))
diff --git a/src/modules/module-raop-discover.c b/src/modules/module-raop-discover.c
index 3675b003e..436972ac0 100644
--- a/src/modules/module-raop-discover.c
+++ b/src/modules/module-raop-discover.c
@@ -19,8 +19,12 @@
#include
#include
-#include "zeroconf-utils/zeroconf.h"
+#include
+#include
+#include
+
#include "module-protocol-pulse/format.h"
+#include "module-zeroconf-discover/avahi-poll.h"
/** \page page_module_raop_discover RAOP Discover
*
@@ -125,20 +129,29 @@ struct impl {
struct pw_properties *properties;
- struct pw_zeroconf *zeroconf;
- struct spa_hook zeroconf_listener;
+ AvahiPoll *avahi_poll;
+ AvahiClient *client;
+ AvahiServiceBrowser *sink_browser;
struct spa_list tunnel_list;
};
+struct tunnel_info {
+ const char *name;
+};
+
+#define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ })
+
struct tunnel {
struct spa_list link;
- char *name;
+ struct tunnel_info info;
struct pw_impl_module *module;
struct spa_hook module_listener;
};
-static struct tunnel *tunnel_new(struct impl *impl, const char *name)
+static int start_client(struct impl *impl);
+
+static struct tunnel *make_tunnel(struct impl *impl, const struct tunnel_info *info)
{
struct tunnel *t;
@@ -146,28 +159,28 @@ static struct tunnel *tunnel_new(struct impl *impl, const char *name)
if (t == NULL)
return NULL;
- t->name = strdup(name);
+ t->info.name = strdup(info->name);
spa_list_append(&impl->tunnel_list, &t->link);
return t;
}
-static struct tunnel *find_tunnel(struct impl *impl, const char *name)
+static struct tunnel *find_tunnel(struct impl *impl, const struct tunnel_info *info)
{
struct tunnel *t;
spa_list_for_each(t, &impl->tunnel_list, link) {
- if (spa_streq(t->name, name))
+ if (spa_streq(t->info.name, info->name))
return t;
}
return NULL;
}
-static void tunnel_free(struct tunnel *t)
+static void free_tunnel(struct tunnel *t)
{
spa_list_remove(&t->link);
if (t->module)
pw_impl_module_destroy(t->module);
- free(t->name);
+ free((char *) t->info.name);
free(t);
}
@@ -176,9 +189,14 @@ static void impl_free(struct impl *impl)
struct tunnel *t;
spa_list_consume(t, &impl->tunnel_list, link)
- tunnel_free(t);
- if (impl->zeroconf)
- pw_zeroconf_destroy(impl->zeroconf);
+ free_tunnel(t);
+
+ if (impl->sink_browser)
+ avahi_service_browser_free(impl->sink_browser);
+ if (impl->client)
+ avahi_client_free(impl->client);
+ if (impl->avahi_poll)
+ pw_avahi_poll_free(impl->avahi_poll);
pw_properties_free(impl->properties);
free(impl);
}
@@ -206,6 +224,75 @@ static bool str_in_list(const char *haystack, const char *delimiters, const char
return false;
}
+static void pw_properties_from_avahi_string(const char *key, const char *value,
+ struct pw_properties *props)
+{
+ if (spa_streq(key, "device")) {
+ pw_properties_set(props, "raop.device", value);
+ }
+ else if (spa_streq(key, "tp")) {
+ /* transport protocol, "UDP", "TCP", "UDP,TCP" */
+ if (str_in_list(value, ",", "UDP"))
+ value = "udp";
+ else if (str_in_list(value, ",", "TCP"))
+ value = "tcp";
+ pw_properties_set(props, "raop.transport", value);
+ } else if (spa_streq(key, "et")) {
+ /* RAOP encryption types:
+ * 0 = none,
+ * 1 = RSA,
+ * 3 = FairPlay,
+ * 4 = MFiSAP (/auth-setup),
+ * 5 = FairPlay SAPv2.5 */
+ if (str_in_list(value, ",", "5"))
+ value = "fp_sap25";
+ else if (str_in_list(value, ",", "4"))
+ value = "auth_setup";
+ else if (str_in_list(value, ",", "1"))
+ value = "RSA";
+ else
+ value = "none";
+ pw_properties_set(props, "raop.encryption.type", value);
+ } else if (spa_streq(key, "cn")) {
+ /* Supported audio codecs:
+ * 0 = PCM,
+ * 1 = ALAC,
+ * 2 = AAC,
+ * 3 = AAC ELD. */
+ if (str_in_list(value, ",", "0"))
+ value = "PCM";
+ else if (str_in_list(value, ",", "1"))
+ value = "ALAC";
+ else if (str_in_list(value, ",", "2"))
+ value = "AAC";
+ else if (str_in_list(value, ",", "3"))
+ value = "AAC-ELD";
+ else
+ value = "unknown";
+ pw_properties_set(props, "raop.audio.codec", value);
+ } else if (spa_streq(key, "ch")) {
+ /* Number of channels */
+ pw_properties_set(props, PW_KEY_AUDIO_CHANNELS, value);
+ } else if (spa_streq(key, "ss")) {
+ /* Sample size */
+ if (spa_streq(value, "16"))
+ value = "S16";
+ else if (spa_streq(value, "24"))
+ value = "S24";
+ else if (spa_streq(value, "32"))
+ value = "S32";
+ else
+ value = "UNKNOWN";
+ pw_properties_set(props, PW_KEY_AUDIO_FORMAT, value);
+ } else if (spa_streq(key, "sr")) {
+ /* Sample rate */
+ pw_properties_set(props, PW_KEY_AUDIO_RATE, value);
+ } else if (spa_streq(key, "am")) {
+ /* Device model */
+ pw_properties_set(props, "device.model", value);
+ }
+}
+
static void submodule_destroy(void *data)
{
struct tunnel *t = data;
@@ -277,124 +364,76 @@ static int rule_matched(void *data, const char *location, const char *action,
return res;
}
-static void pw_properties_from_zeroconf(const char *key, const char *value,
- struct pw_properties *props)
+static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiResolverEvent event, const char *name, const char *type, const char *domain,
+ const char *host_name, const AvahiAddress *a, uint16_t port, AvahiStringList *txt,
+ AvahiLookupResultFlags flags, void *userdata)
{
- if (spa_streq(key, PW_KEY_ZEROCONF_IFINDEX)) {
- pw_properties_set(props, "raop.ifindex", value);
- }
- else if (spa_streq(key, PW_KEY_ZEROCONF_ADDRESS)) {
- pw_properties_set(props, "raop.ip", value);
- }
- else if (spa_streq(key, PW_KEY_ZEROCONF_PORT)) {
- pw_properties_set(props, "raop.port", value);
- }
- else if (spa_streq(key, PW_KEY_ZEROCONF_NAME)) {
- pw_properties_set(props, "raop.name", value);
- }
- else if (spa_streq(key, PW_KEY_ZEROCONF_HOSTNAME)) {
- pw_properties_set(props, "raop.hostname", value);
- }
- else if (spa_streq(key, PW_KEY_ZEROCONF_DOMAIN)) {
- pw_properties_set(props, "raop.domain", value);
- }
- else if (spa_streq(key, "device")) {
- pw_properties_set(props, "raop.device", value);
- }
- else if (spa_streq(key, "tp")) {
- /* transport protocol, "UDP", "TCP", "UDP,TCP" */
- if (str_in_list(value, ",", "UDP"))
- value = "udp";
- else if (str_in_list(value, ",", "TCP"))
- value = "tcp";
- pw_properties_set(props, "raop.transport", value);
- } else if (spa_streq(key, "et")) {
- /* RAOP encryption types:
- * 0 = none,
- * 1 = RSA,
- * 3 = FairPlay,
- * 4 = MFiSAP (/auth-setup),
- * 5 = FairPlay SAPv2.5 */
- if (str_in_list(value, ",", "5"))
- value = "fp_sap25";
- else if (str_in_list(value, ",", "4"))
- value = "auth_setup";
- else if (str_in_list(value, ",", "1"))
- value = "RSA";
- else
- value = "none";
- pw_properties_set(props, "raop.encryption.type", value);
- } else if (spa_streq(key, "cn")) {
- /* Supported audio codecs:
- * 0 = PCM,
- * 1 = ALAC,
- * 2 = AAC,
- * 3 = AAC ELD. */
- if (str_in_list(value, ",", "0"))
- value = "PCM";
- else if (str_in_list(value, ",", "1"))
- value = "ALAC";
- else if (str_in_list(value, ",", "2"))
- value = "AAC";
- else if (str_in_list(value, ",", "3"))
- value = "AAC-ELD";
- else
- value = "unknown";
- pw_properties_set(props, "raop.audio.codec", value);
- } else if (spa_streq(key, "ch")) {
- /* Number of channels */
- pw_properties_set(props, PW_KEY_AUDIO_CHANNELS, value);
- } else if (spa_streq(key, "ss")) {
- /* Sample size */
- if (spa_streq(value, "16"))
- value = "S16";
- else if (spa_streq(value, "24"))
- value = "S24";
- else if (spa_streq(value, "32"))
- value = "S32";
- else
- value = "UNKNOWN";
- pw_properties_set(props, PW_KEY_AUDIO_FORMAT, value);
- } else if (spa_streq(key, "sr")) {
- /* Sample rate */
- pw_properties_set(props, PW_KEY_AUDIO_RATE, value);
- } else if (spa_streq(key, "am")) {
- /* Device model */
- pw_properties_set(props, "device.model", value);
- }
-}
-
-static void on_zeroconf_added(void *data, const void *user, const struct spa_dict *info)
-{
- struct impl *impl = data;
- const char *name, *str;
+ struct impl *impl = userdata;
+ struct tunnel_info tinfo;
struct tunnel *t;
- const struct spa_dict_item *it;
+ const char *str, *link_local_range = "169.254.";
+ AvahiStringList *l;
struct pw_properties *props = NULL;
+ char at[AVAHI_ADDRESS_STR_MAX], if_suffix[16] = "";
- name = spa_dict_lookup(info, PW_KEY_ZEROCONF_NAME);
-
- t = find_tunnel(impl, name);
- if (t == NULL) {
- if ((t = tunnel_new(impl, name)) == NULL) {
- pw_log_error("Can't make tunnel: %m");
- goto done;
- }
- }
- if (t->module != NULL) {
- pw_log_info("found duplicate mdns entry for %s on IP %s - "
- "skipping tunnel creation", name,
- spa_dict_lookup(info, PW_KEY_ZEROCONF_ADDRESS));
+ if (event != AVAHI_RESOLVER_FOUND) {
+ pw_log_error("Resolving of '%s' failed: %s", name,
+ avahi_strerror(avahi_client_errno(impl->client)));
goto done;
}
- if ((props = pw_properties_new(NULL, NULL)) == NULL) {
+ avahi_address_snprint(at, sizeof(at), a);
+ if (spa_strstartswith(at, link_local_range))
+ pw_log_info("found link-local ip address %s for '%s'", at, name);
+
+ tinfo = TUNNEL_INFO(.name = name);
+
+ t = find_tunnel(impl, &tinfo);
+ if (t == NULL)
+ t = make_tunnel(impl, &tinfo);
+ if (t == NULL) {
+ pw_log_error("Can't make tunnel: %m");
+ goto done;
+ }
+ if (t->module != NULL) {
+ pw_log_info("found duplicate mdns entry for %s on IP %s - skipping tunnel creation", name, at);
+ goto done;
+ }
+
+ props = pw_properties_new(NULL, NULL);
+ if (props == NULL) {
pw_log_error("Can't allocate properties: %m");
goto done;
}
- spa_dict_for_each(it, info)
- pw_properties_from_zeroconf(it->key, it->value, props);
+ if (a->proto == AVAHI_PROTO_INET6 &&
+ a->data.ipv6.address[0] == 0xfe &&
+ (a->data.ipv6.address[1] & 0xc0) == 0x80)
+ snprintf(if_suffix, sizeof(if_suffix), "%%%d", interface);
+
+ /* For IPv4 link-local, bind to the discovery interface */
+ if (a->proto == AVAHI_PROTO_INET &&
+ spa_strstartswith(at, link_local_range))
+ snprintf(if_suffix, sizeof(if_suffix), "%%%d", interface);
+
+ pw_properties_setf(props, "raop.ip", "%s%s", at, if_suffix);
+ pw_properties_setf(props, "raop.ifindex", "%d", interface);
+ pw_properties_setf(props, "raop.port", "%u", port);
+ pw_properties_setf(props, "raop.name", "%s", name);
+ pw_properties_setf(props, "raop.hostname", "%s", host_name);
+ pw_properties_setf(props, "raop.domain", "%s", domain);
+
+ for (l = txt; l; l = l->next) {
+ char *key, *value;
+
+ if (avahi_string_list_get_pair(l, &key, &value, NULL) != 0)
+ break;
+
+ pw_properties_from_avahi_string(key, value, props);
+ avahi_free(key);
+ avahi_free(value);
+ }
if ((str = pw_properties_get(impl->properties, "raop.latency.ms")) != NULL)
pw_properties_set(props, "raop.latency.ms", str);
@@ -413,28 +452,123 @@ static void on_zeroconf_added(void *data, const void *user, const struct spa_dic
if (!minfo.matched)
pw_log_info("unmatched service found %s", str);
}
+
done:
+ avahi_service_resolver_free(r);
pw_properties_free(props);
}
-static void on_zeroconf_removed(void *data, const void *user, const struct spa_dict *info)
+
+static void browser_cb(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiBrowserEvent event, const char *name, const char *type, const char *domain,
+ AvahiLookupResultFlags flags, void *userdata)
{
- struct impl *impl = data;
- const char *name;
+ struct impl *impl = userdata;
+ struct tunnel_info info;
struct tunnel *t;
- name = spa_dict_lookup(info, PW_KEY_ZEROCONF_NAME);
-
- if ((t = find_tunnel(impl, name)) == NULL)
+ if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) && !impl->discover_local)
return;
- tunnel_free(t);
+ info = TUNNEL_INFO(.name = name);
+
+ t = find_tunnel(impl, &info);
+
+ switch (event) {
+ case AVAHI_BROWSER_NEW:
+ if (t != NULL) {
+ pw_log_info("found duplicate mdns entry - skipping tunnel creation");
+ return;
+ }
+ if (!(avahi_service_resolver_new(impl->client,
+ interface, protocol,
+ name, type, domain,
+ AVAHI_PROTO_UNSPEC, 0,
+ resolver_cb, impl)))
+ pw_log_error("can't make service resolver: %s",
+ avahi_strerror(avahi_client_errno(impl->client)));
+ break;
+ case AVAHI_BROWSER_REMOVE:
+ if (t == NULL)
+ return;
+ free_tunnel(t);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static struct AvahiServiceBrowser *make_browser(struct impl *impl, const char *service_type)
+{
+ struct AvahiServiceBrowser *s;
+
+ s = avahi_service_browser_new(impl->client,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ service_type, NULL, 0,
+ browser_cb, impl);
+ if (s == NULL) {
+ pw_log_error("can't make browser for %s: %s", service_type,
+ avahi_strerror(avahi_client_errno(impl->client)));
+ }
+ return s;
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata)
+{
+ struct impl *impl = userdata;
+
+ impl->client = c;
+
+ switch (state) {
+ case AVAHI_CLIENT_S_REGISTERING:
+ case AVAHI_CLIENT_S_RUNNING:
+ case AVAHI_CLIENT_S_COLLISION:
+ if (impl->sink_browser == NULL)
+ impl->sink_browser = make_browser(impl, SERVICE_TYPE_SINK);
+ if (impl->sink_browser == NULL)
+ goto error;
+ break;
+ case AVAHI_CLIENT_FAILURE:
+ if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
+ start_client(impl);
+
+ SPA_FALLTHROUGH;
+ case AVAHI_CLIENT_CONNECTING:
+ if (impl->sink_browser) {
+ avahi_service_browser_free(impl->sink_browser);
+ impl->sink_browser = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ return;
+error:
+ pw_impl_module_schedule_destroy(impl->module);
+}
+
+static int start_client(struct impl *impl)
+{
+ int res;
+ if ((impl->client = avahi_client_new(impl->avahi_poll,
+ AVAHI_CLIENT_NO_FAIL,
+ client_callback, impl,
+ &res)) == NULL) {
+ pw_log_error("can't create client: %s", avahi_strerror(res));
+ pw_impl_module_schedule_destroy(impl->module);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int start_avahi(struct impl *impl)
+{
+
+ impl->avahi_poll = pw_avahi_poll_new(impl->context);
+
+ return start_client(impl);
}
-static const struct pw_zeroconf_events zeroconf_events = {
- PW_VERSION_ZEROCONF_EVENTS,
- .added = on_zeroconf_added,
- .removed = on_zeroconf_removed,
-};
SPA_EXPORT
int pipewire__module_init(struct pw_impl_module *module, const char *args)
@@ -442,7 +576,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
struct pw_context *context = pw_impl_module_get_context(module);
struct pw_properties *props;
struct impl *impl;
- const char *local;
int res;
PW_LOG_TOPIC_INIT(mod_topic);
@@ -466,24 +599,15 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
impl->context = context;
impl->properties = props;
- if ((local = pw_properties_get(impl->properties, "raop.discover-local")) == NULL)
- local = "false";
- pw_properties_set(impl->properties, PW_KEY_ZEROCONF_DISCOVER_LOCAL, local);
+ impl->discover_local = pw_properties_get_bool(impl->properties,
+ "raop.discover-local", false);
pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
- if ((impl->zeroconf = pw_zeroconf_new(context, &props->dict)) == NULL) {
- pw_log_error("can't create zeroconf: %m");
- goto error_errno;
- }
- pw_zeroconf_add_listener(impl->zeroconf, &impl->zeroconf_listener,
- &zeroconf_events, impl);
+ start_avahi(impl);
- pw_zeroconf_set_browse(impl->zeroconf, NULL,
- &SPA_DICT_ITEMS(
- SPA_DICT_ITEM(PW_KEY_ZEROCONF_TYPE, SERVICE_TYPE_SINK)));
return 0;
error_errno:
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 5e25b3089..e217ff5b2 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -45,7 +45,6 @@
#include "network-utils.h"
#include "module-raop/rtsp-client.h"
-#include "module-raop/base64.h"
#include "module-rtp/rtp.h"
#include "module-rtp/stream.h"
@@ -131,6 +130,11 @@
PW_LOG_TOPIC(mod_topic, "mod." NAME);
#define PW_LOG_TOPIC_DEFAULT mod_topic
+#define BUFFER_SIZE (1u<<22)
+#define BUFFER_MASK (BUFFER_SIZE-1)
+#define BUFFER_SIZE2 (BUFFER_SIZE>>1)
+#define BUFFER_MASK2 (BUFFER_SIZE2-1)
+
#define FRAMES_PER_TCP_PACKET 4096
#define FRAMES_PER_UDP_PACKET 352
@@ -269,6 +273,13 @@ struct impl {
bool mute;
float volume;
+
+ struct spa_ringbuffer ring;
+ uint8_t buffer[BUFFER_SIZE];
+
+ struct spa_io_position *io_position;
+
+ uint32_t filled;
};
static inline void bit_writer(uint8_t **p, int *pos, uint8_t data, int len)
@@ -346,7 +357,7 @@ static int send_udp_sync_packet(struct impl *impl, uint32_t rtptime, unsigned in
res = sendmsg(impl->control_fd, &msg, MSG_NOSIGNAL);
if (res < 0) {
res = -errno;
- pw_log_warn("error sending control packet: %d (%m)", res);
+ pw_log_warn("error sending control packet: %d", res);
}
pw_log_debug("raop control sync: first:%d latency:%u now:%"PRIx64" rtptime:%u",
@@ -692,6 +703,49 @@ on_control_source_io(void *data, int fd, uint32_t mask)
}
}
+static void base64_encode(const uint8_t *data, size_t len, char *enc, char pad)
+{
+ static const char tab[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ size_t i;
+ for (i = 0; i < len; i += 3) {
+ uint32_t v;
+ v = data[i+0] << 16;
+ v |= (i+1 < len ? data[i+1] : 0) << 8;
+ v |= (i+2 < len ? data[i+2] : 0);
+ *enc++ = tab[(v >> (3*6)) & 0x3f];
+ *enc++ = tab[(v >> (2*6)) & 0x3f];
+ *enc++ = i+1 < len ? tab[(v >> (1*6)) & 0x3f] : pad;
+ *enc++ = i+2 < len ? tab[(v >> (0*6)) & 0x3f] : pad;
+ }
+ *enc = '\0';
+}
+
+static size_t base64_decode(const char *data, size_t len, uint8_t *dec)
+{
+ uint8_t tab[] = {
+ 62, -1, -1, -1, 63, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
+ -1, -1, 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, -1, -1,
+ -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
+ size_t i, j;
+ for (i = 0, j = 0; i < len; i += 4) {
+ uint32_t v;
+ v = tab[data[i+0]-43] << (3*6);
+ v |= tab[data[i+1]-43] << (2*6);
+ v |= (data[i+2] == '=' ? 0 : tab[data[i+2]-43]) << (1*6);
+ v |= (data[i+3] == '=' ? 0 : tab[data[i+3]-43]);
+ dec[j++] = (v >> 16) & 0xff;
+ if (data[i+2] != '=') dec[j++] = (v >> 8) & 0xff;
+ if (data[i+3] != '=') dec[j++] = v & 0xff;
+ }
+ return j;
+}
+
SPA_PRINTF_FUNC(2,3)
static int MD5_hash(char hash[MD5_HASH_LENGTH+1], const char *fmt, ...)
{
@@ -724,7 +778,7 @@ static int rtsp_add_raop_auth_header(struct impl *impl, const char *method)
char buf[256];
char enc[512];
spa_scnprintf(buf, sizeof(buf), "%s:%s", RAOP_AUTH_USER_NAME, impl->password);
- pw_base64_encode((uint8_t*)buf, strlen(buf), enc, '=');
+ base64_encode((uint8_t*)buf, strlen(buf), enc, '=');
spa_scnprintf(auth, sizeof(auth), "Basic %s", enc);
}
else if (spa_streq(impl->auth_method, "Digest")) {
@@ -1082,8 +1136,8 @@ static int rsa_encrypt(uint8_t *data, int len, uint8_t *enc)
"imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
char e[] = "AQAB";
- msize = pw_base64_decode(n, strlen(n), modulus);
- esize = pw_base64_decode(e, strlen(e), exponent);
+ msize = base64_decode(n, strlen(n), modulus);
+ esize = base64_decode(e, strlen(e), exponent);
#if OPENSSL_API_LEVEL >= 30000
EVP_PKEY *pkey = NULL;
@@ -1209,15 +1263,15 @@ static int rtsp_do_announce(struct impl *impl)
(res = pw_getrandom(impl->aes_iv, sizeof(impl->aes_iv), 0)) < 0)
return res;
- pw_base64_encode(rac, sizeof(rac), sac, '\0');
+ base64_encode(rac, sizeof(rac), sac, '\0');
pw_properties_set(impl->headers, "Apple-Challenge", sac);
rsa_len = rsa_encrypt(impl->aes_key, 16, rsakey);
if (rsa_len < 0)
return -rsa_len;
- pw_base64_encode(rsakey, rsa_len, key, '=');
- pw_base64_encode(impl->aes_iv, 16, iv, '=');
+ base64_encode(rsakey, rsa_len, key, '=');
+ base64_encode(impl->aes_iv, 16, iv, '=');
sdp = spa_aprintf("v=0\r\n"
"o=iTunes %s 0 IN IP%d %s\r\n"
diff --git a/src/modules/module-raop/base64.h b/src/modules/module-raop/base64.h
deleted file mode 100644
index d8906c287..000000000
--- a/src/modules/module-raop/base64.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* PipeWire */
-/* SPDX-FileCopyrightText: Copyright © 2026 Wim Taymans */
-/* SPDX-License-Identifier: MIT */
-
-#ifndef PIPEWIRE_BASE64_H
-#define PIPEWIRE_BASE64_H
-
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline void pw_base64_encode(const uint8_t *data, size_t len, char *enc, char pad)
-{
- static const char tab[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- size_t i;
- for (i = 0; i < len; i += 3) {
- uint32_t v;
- v = data[i+0] << 16;
- v |= (i+1 < len ? data[i+1] : 0) << 8;
- v |= (i+2 < len ? data[i+2] : 0);
- *enc++ = tab[(v >> (3*6)) & 0x3f];
- *enc++ = tab[(v >> (2*6)) & 0x3f];
- *enc++ = i+1 < len ? tab[(v >> (1*6)) & 0x3f] : pad;
- *enc++ = i+2 < len ? tab[(v >> (0*6)) & 0x3f] : pad;
- }
- *enc = '\0';
-}
-
-static inline size_t pw_base64_decode(const char *data, size_t len, uint8_t *dec)
-{
- uint8_t tab[] = {
- 62, -1, -1, -1, 63, 52, 53, 54, 55, 56,
- 57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
- -1, -1, 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
- 18, 19, 20, 21, 22, 23, 24, 25, -1, -1,
- -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
- 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
- size_t i, j;
- for (i = 0, j = 0; i < len; i += 4) {
- uint32_t v;
- v = tab[data[i+0]-43] << (3*6);
- v |= tab[data[i+1]-43] << (2*6);
- v |= (data[i+2] == '=' ? 0 : tab[data[i+2]-43]) << (1*6);
- v |= (data[i+3] == '=' ? 0 : tab[data[i+3]-43]);
- dec[j++] = (v >> 16) & 0xff;
- if (data[i+2] != '=') dec[j++] = (v >> 8) & 0xff;
- if (data[i+3] != '=') dec[j++] = v & 0xff;
- }
- return j;
-}
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* PIPEWIRE_BASE64_H */
diff --git a/src/modules/module-raop/rtsp-client.c b/src/modules/module-raop/rtsp-client.c
index 4bcff8b88..fae71977c 100644
--- a/src/modules/module-raop/rtsp-client.c
+++ b/src/modules/module-raop/rtsp-client.c
@@ -445,10 +445,7 @@ on_source_io(void *data, int fd, uint32_t mask)
int res;
if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
- socklen_t len = sizeof(res);
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &res, &len) < 0)
- res = errno;
- res = -res;
+ res = -EPIPE;
goto error;
}
if (mask & SPA_IO_IN) {
@@ -481,7 +478,7 @@ int pw_rtsp_client_connect(struct pw_rtsp_client *client,
{
struct addrinfo hints;
struct addrinfo *result, *rp;
- int res, fd = -1;
+ int res, fd;
char port_str[12];
if (client->source != NULL)
diff --git a/src/modules/module-roc-sink.c b/src/modules/module-roc-sink.c
index 66a2c716b..39ca2bce1 100644
--- a/src/modules/module-roc-sink.c
+++ b/src/modules/module-roc-sink.c
@@ -120,6 +120,8 @@ struct module_roc_sink_data {
roc_endpoint *remote_control_addr;
int remote_control_port;
+
+ roc_log_level loglevel;
};
static void stream_destroy(void *d)
@@ -389,7 +391,8 @@ static const struct spa_dict_item module_roc_sink_info[] = {
"( remote.repair.port= ) "
"( remote.control.port= ) "
"( audio.position= ) "
- "( sink.props= { key=val ... } ) " },
+ "( sink.props= { key=val ... } ) "
+ "( log.level=|DEFAULT|NONE|RROR|INFO|DEBUG|TRACE ) " },
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
};
@@ -510,6 +513,14 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
pw_log_error("can't connect: %m");
goto out;
}
+ if ((str = pw_properties_get(props, "log.level")) != NULL) {
+ const struct spa_log *log_conf = pw_log_get();
+ const roc_log_level default_level = pw_roc_log_level_pw_2_roc(log_conf->level);
+ if (pw_roc_parse_log_level(&data->loglevel, str, default_level)) {
+ pw_log_error("Invalid log level %s, using default", str);
+ data->loglevel = default_level;
+ }
+ }
pw_proxy_add_listener((struct pw_proxy*)data->core,
&data->core_proxy_listener,
diff --git a/src/modules/module-roc-source.c b/src/modules/module-roc-source.c
index a46189e5d..2173c6af1 100644
--- a/src/modules/module-roc-source.c
+++ b/src/modules/module-roc-source.c
@@ -140,6 +140,8 @@ struct module_roc_source_data {
roc_endpoint *local_control_addr;
int local_control_port;
+
+ roc_log_level loglevel;
};
static void stream_destroy(void *d)
@@ -428,7 +430,8 @@ static const struct spa_dict_item module_roc_source_info[] = {
"( local.repair.port= ) "
"( local.control.port= ) "
"( audio.position= ) "
- "( source.props= { key=value ... } ) " },
+ "( source.props= { key=value ... } ) "
+ "( log.level=|DEFAULT|NONE|RROR|INFO|DEBUG|TRACE ) " },
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
};
@@ -564,6 +567,14 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
} else {
data->fec_code = ROC_FEC_ENCODING_DEFAULT;
}
+ if ((str = pw_properties_get(props, "log.level")) != NULL) {
+ const struct spa_log *log_conf = pw_log_get();
+ const roc_log_level default_level = pw_roc_log_level_pw_2_roc(log_conf->level);
+ if (pw_roc_parse_log_level(&data->loglevel, str, default_level)) {
+ pw_log_error("Invalid log level %s, using default", str);
+ data->loglevel = default_level;
+ }
+ }
data->core = pw_context_get_object(data->module_context, PW_TYPE_INTERFACE_Core);
if (data->core == NULL) {
diff --git a/src/modules/module-roc/common.c b/src/modules/module-roc/common.c
index 475c5f40f..244c203dd 100644
--- a/src/modules/module-roc/common.c
+++ b/src/modules/module-roc/common.c
@@ -5,54 +5,17 @@
PW_LOG_TOPIC(roc_log_topic, "mod.roc.lib");
-static inline roc_log_level pw_roc_log_level_pw_2_roc(const enum spa_log_level pw_log_level)
-{
- switch (pw_log_level) {
- case SPA_LOG_LEVEL_NONE:
- return ROC_LOG_NONE;
- case SPA_LOG_LEVEL_ERROR:
- return ROC_LOG_ERROR;
- case SPA_LOG_LEVEL_WARN:
- return ROC_LOG_ERROR;
- case SPA_LOG_LEVEL_INFO:
- return ROC_LOG_INFO;
- case SPA_LOG_LEVEL_DEBUG:
- return ROC_LOG_DEBUG;
- case SPA_LOG_LEVEL_TRACE:
- return ROC_LOG_TRACE;
- default:
- return ROC_LOG_NONE;
- }
-}
-
-static inline enum spa_log_level pw_roc_log_level_roc_2_pw(const roc_log_level roc_log_level)
-{
- switch (roc_log_level) {
- case ROC_LOG_NONE:
- return SPA_LOG_LEVEL_NONE;
- case ROC_LOG_ERROR:
- return SPA_LOG_LEVEL_ERROR;
- case ROC_LOG_INFO:
- return SPA_LOG_LEVEL_INFO;
- case ROC_LOG_DEBUG:
- return SPA_LOG_LEVEL_DEBUG;
- case ROC_LOG_TRACE:
- return SPA_LOG_LEVEL_TRACE;
- default:
- return SPA_LOG_LEVEL_NONE;
- }
-}
-
-static void pw_roc_log_handler(const roc_log_message *message, void *argument)
-{
- const enum spa_log_level log_level = pw_roc_log_level_roc_2_pw(message->level);
- if (SPA_UNLIKELY(pw_log_topic_enabled(log_level, roc_log_topic))) {
- pw_log_logt(log_level, roc_log_topic, message->file, message->line, message->module, "%s", message->text);
- }
-}
-
void pw_roc_log_init(void)
{
roc_log_set_handler(pw_roc_log_handler, NULL);
roc_log_set_level(pw_roc_log_level_pw_2_roc(roc_log_topic->has_custom_level ? roc_log_topic->level : pw_log_level));
}
+
+void pw_roc_log_handler(const roc_log_message *message, void *argument)
+{
+ const enum spa_log_level log_level = pw_roc_log_level_roc_2_pw(message->level);
+ if (SPA_UNLIKELY(pw_log_topic_enabled(log_level, roc_log_topic))) {
+ pw_log_logt(log_level, roc_log_topic, message->file, message->line, message->module, message->text, "");
+ }
+}
+
diff --git a/src/modules/module-roc/common.h b/src/modules/module-roc/common.h
index d49e392fe..c94ac69a8 100644
--- a/src/modules/module-roc/common.h
+++ b/src/modules/module-roc/common.h
@@ -3,6 +3,7 @@
#include
#include
+#include
#include
#include
@@ -20,6 +21,7 @@
#define PW_ROC_STEREO_POSITIONS "[ FL FR ]"
void pw_roc_log_init(void);
+void pw_roc_log_handler(const roc_log_message *message, void *argument);
static inline int pw_roc_parse_fec_encoding(roc_fec_encoding *out, const char *str)
{
@@ -135,4 +137,62 @@ static inline void pw_roc_fec_encoding_to_proto(roc_fec_encoding fec_code, roc_p
}
}
+static inline roc_log_level pw_roc_log_level_pw_2_roc(const enum spa_log_level pw_log_level)
+{
+ switch (pw_log_level) {
+ case SPA_LOG_LEVEL_NONE:
+ return ROC_LOG_NONE;
+ case SPA_LOG_LEVEL_ERROR:
+ return ROC_LOG_ERROR;
+ case SPA_LOG_LEVEL_WARN:
+ return ROC_LOG_ERROR;
+ case SPA_LOG_LEVEL_INFO:
+ return ROC_LOG_INFO;
+ case SPA_LOG_LEVEL_DEBUG:
+ return ROC_LOG_DEBUG;
+ case SPA_LOG_LEVEL_TRACE:
+ return ROC_LOG_TRACE;
+ default:
+ return ROC_LOG_NONE;
+ }
+}
+
+static inline enum spa_log_level pw_roc_log_level_roc_2_pw(const roc_log_level roc_log_level)
+{
+ switch (roc_log_level) {
+ case ROC_LOG_NONE:
+ return SPA_LOG_LEVEL_NONE;
+ case ROC_LOG_ERROR:
+ return SPA_LOG_LEVEL_ERROR;
+ case ROC_LOG_INFO:
+ return SPA_LOG_LEVEL_INFO;
+ case ROC_LOG_DEBUG:
+ return SPA_LOG_LEVEL_DEBUG;
+ case ROC_LOG_TRACE:
+ return SPA_LOG_LEVEL_TRACE;
+ default:
+ return SPA_LOG_LEVEL_NONE;
+ }
+}
+
+static inline int pw_roc_parse_log_level(roc_log_level *loglevel, const char *str,
+ roc_log_level default_level)
+{
+ if (spa_streq(str, "DEFAULT"))
+ *loglevel = default_level;
+ else if (spa_streq(str, "NONE"))
+ *loglevel = ROC_LOG_NONE;
+ else if (spa_streq(str, "ERROR"))
+ *loglevel = ROC_LOG_ERROR;
+ else if (spa_streq(str, "INFO"))
+ *loglevel = ROC_LOG_INFO;
+ else if (spa_streq(str, "DEBUG"))
+ *loglevel = ROC_LOG_DEBUG;
+ else if (spa_streq(str, "TRACE"))
+ *loglevel = ROC_LOG_TRACE;
+ else
+ return -EINVAL;
+ return 0;
+}
+
#endif /* MODULE_ROC_COMMON_H */
diff --git a/src/modules/module-rtp-sap.c b/src/modules/module-rtp-sap.c
index cfbcbf419..c734758c3 100644
--- a/src/modules/module-rtp-sap.c
+++ b/src/modules/module-rtp-sap.c
@@ -1235,11 +1235,8 @@ static struct session *session_new_announce(struct impl *impl, struct node *node
sess->has_sdp = true;
}
- pw_log_debug("sending out initial SAP");
send_sap(impl, sess, 0);
- pw_log_debug("new announcement session up and running");
-
return sess;
error_free:
@@ -1838,7 +1835,6 @@ static int start_sap(struct impl *impl)
pw_timer_queue_add(impl->timer_queue, &impl->start_sap_retry_timer,
NULL, 1 * SPA_NSEC_PER_SEC,
on_start_sap_retry_timer_event, impl);
- pw_log_info("starting SAP retry timer");
/* It is important to return 0 in this case. Otherwise, the nonzero return
* value will later be propagated through the core as an error. */
@@ -2009,10 +2005,8 @@ static void impl_destroy(struct impl *impl)
pw_timer_queue_cancel(&impl->sap_send_timer);
pw_timer_queue_cancel(&impl->start_sap_retry_timer);
pw_timer_queue_cancel(&impl->igmp_recovery.timer);
- if (impl->sap_source) {
- pw_log_info("destroying SAP source");
+ if (impl->sap_source)
pw_loop_destroy_source(impl->loop, impl->sap_source);
- }
if (impl->sap_fd != -1)
close(impl->sap_fd);
diff --git a/src/modules/module-rtp-session.c b/src/modules/module-rtp-session.c
index 2dca681d6..63470c539 100644
--- a/src/modules/module-rtp-session.c
+++ b/src/modules/module-rtp-session.c
@@ -27,7 +27,12 @@
#include
#include
-#include "zeroconf-utils/zeroconf.h"
+#include
+#include
+#include
+#include
+
+#include "module-zeroconf-discover/avahi-poll.h"
#include
#include
@@ -155,21 +160,31 @@ static const struct spa_dict_item module_info[] = {
};
struct service_info {
- int ifindex;
- int protocol;
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
const char *name;
const char *type;
const char *domain;
+ const char *host_name;
+ AvahiAddress address;
+ uint16_t port;
};
#define SERVICE_INFO(...) ((struct service_info){ __VA_ARGS__ })
+struct service {
+ struct service_info info;
+
+ struct spa_list link;
+ struct impl *impl;
+
+ struct session *sess;
+};
+
struct session {
struct impl *impl;
struct spa_list link;
- struct service_info info;
-
struct sockaddr_storage ctrl_addr;
socklen_t ctrl_len;
struct sockaddr_storage data_addr;
@@ -214,8 +229,11 @@ struct impl {
struct pw_properties *props;
bool discover_local;
- struct pw_zeroconf *zeroconf;
- struct spa_hook zeroconf_listener;
+ AvahiPoll *avahi_poll;
+ AvahiClient *client;
+ AvahiServiceBrowser *browser;
+ AvahiEntryGroup *group;
+ struct spa_list service_list;
struct pw_properties *stream_props;
@@ -577,9 +595,6 @@ static void free_session(struct session *sess)
if (sess->recv)
rtp_stream_destroy(sess->recv);
free(sess->name);
- free((char *) sess->info.name);
- free((char *) sess->info.type);
- free((char *) sess->info.domain);
free(sess);
}
@@ -597,8 +612,7 @@ static bool cmp_ip(const struct sockaddr_storage *sa, const struct sockaddr_stor
return false;
}
-static struct session *make_session(struct impl *impl, struct service_info *info,
- struct pw_properties *props)
+static struct session *make_session(struct impl *impl, struct pw_properties *props)
{
struct session *sess;
const char *str;
@@ -613,11 +627,6 @@ static struct session *make_session(struct impl *impl, struct service_info *info
sess->impl = impl;
sess->ssrc = pw_rand32();
- sess->info.ifindex = info->ifindex;
- sess->info.protocol = info->protocol;
- sess->info.name = strdup(info->name);
- sess->info.type = strdup(info->type);
- sess->info.domain = strdup(info->domain);
str = pw_properties_get(props, "sess.name");
sess->name = str ? strdup(str) : strdup("RTP Session");
@@ -660,21 +669,6 @@ error:
return NULL;
}
-static struct session *find_session_by_info(struct impl *impl,
- const struct service_info *info)
-{
- struct session *s;
- spa_list_for_each(s, &impl->sessions, link) {
- if (s->info.ifindex == info->ifindex &&
- s->info.protocol == info->protocol &&
- spa_streq(s->info.name, info->name) &&
- spa_streq(s->info.type, info->type) &&
- spa_streq(s->info.domain, info->domain))
- return s;
- }
- return NULL;
-}
-
static struct session *find_session_by_addr_name(struct impl *impl,
const struct sockaddr_storage *sa, const char *name)
{
@@ -1226,8 +1220,8 @@ static void impl_destroy(struct impl *impl)
if (impl->data_source)
pw_loop_destroy_source(impl->data_loop, impl->data_source);
- if (impl->zeroconf)
- pw_zeroconf_destroy(impl->zeroconf);
+ if (impl->client)
+ avahi_client_free(impl->client);
if (impl->data_loop)
pw_context_release_loop(impl->context, impl->data_loop);
@@ -1269,7 +1263,21 @@ static const struct pw_core_events core_events = {
.error = on_core_error,
};
-static const char *get_service_type(struct impl *impl)
+static void free_service(struct service *s)
+{
+ spa_list_remove(&s->link);
+
+ if (s->sess)
+ free_session(s->sess);
+
+ free((char *) s->info.name);
+ free((char *) s->info.type);
+ free((char *) s->info.domain);
+ free((char *) s->info.host_name);
+ free(s);
+}
+
+static const char *get_service_name(struct impl *impl)
{
const char *str;
str = pw_properties_get(impl->props, "sess.media");
@@ -1280,36 +1288,21 @@ static const char *get_service_type(struct impl *impl)
return NULL;
}
-static void on_zeroconf_added(void *data, const void *user, const struct spa_dict *info)
+static struct service *make_service(struct impl *impl, const struct service_info *info,
+ AvahiStringList *txt)
{
- struct impl *impl = data;
- const char *str, *service_type, *address, *hostname;
- struct service_info sinfo;
+ struct service *s = NULL;
+ char at[AVAHI_ADDRESS_STR_MAX], if_suffix[16] = "";
struct session *sess;
- int ifindex = -1, protocol = 0, res, port = 0;
+ int res, ipv;
struct pw_properties *props = NULL;
+ const char *service_name, *str;
+ AvahiStringList *l;
bool compatible = true;
- if ((str = spa_dict_lookup(info, PW_KEY_ZEROCONF_IFINDEX)))
- ifindex = atoi(str);
- if ((str = spa_dict_lookup(info, PW_KEY_ZEROCONF_PROTO)))
- protocol = atoi(str);
- if ((str = spa_dict_lookup(info, PW_KEY_ZEROCONF_PORT)))
- port = atoi(str);
-
- sinfo = SERVICE_INFO(.ifindex = ifindex,
- .protocol = protocol,
- .name = spa_dict_lookup(info, PW_KEY_ZEROCONF_NAME),
- .type = spa_dict_lookup(info, PW_KEY_ZEROCONF_TYPE),
- .domain = spa_dict_lookup(info, PW_KEY_ZEROCONF_DOMAIN));
-
- sess = find_session_by_info(impl, &sinfo);
- if (sess != NULL)
- return;
-
/* check for compatible session */
- service_type = get_service_type(impl);
- compatible = spa_streq(service_type, sinfo.type);
+ service_name = get_service_name(impl);
+ compatible = spa_streq(service_name, info->type);
props = pw_properties_copy(impl->stream_props);
if (props == NULL) {
@@ -1317,53 +1310,55 @@ static void on_zeroconf_added(void *data, const void *user, const struct spa_dic
goto error;
}
- if (spa_streq(service_type, "_pipewire-audio._udp")) {
+ if (spa_streq(service_name, "_pipewire-audio._udp")) {
uint32_t mask = 0;
- const struct spa_dict_item *it;
- spa_dict_for_each(it, info) {
+ for (l = txt; l && compatible; l = l->next) {
const char *k = NULL;
+ char *key, *value;
- if (!compatible)
+ if (avahi_string_list_get_pair(l, &key, &value, NULL) != 0)
break;
- if (spa_streq(it->key, "subtype")) {
+ if (spa_streq(key, "subtype")) {
k = "sess.media";
mask |= 1<<0;
- } else if (spa_streq(it->key, "format")) {
+ } else if (spa_streq(key, "format")) {
k = PW_KEY_AUDIO_FORMAT;
mask |= 1<<1;
- } else if (spa_streq(it->key, "rate")) {
+ } else if (spa_streq(key, "rate")) {
k = PW_KEY_AUDIO_RATE;
mask |= 1<<2;
- } else if (spa_streq(it->key, "channels")) {
+ } else if (spa_streq(key, "channels")) {
k = PW_KEY_AUDIO_CHANNELS;
mask |= 1<<3;
- } else if (spa_streq(it->key, "position")) {
+ } else if (spa_streq(key, "position")) {
pw_properties_set(props,
- SPA_KEY_AUDIO_POSITION, it->value);
- } else if (spa_streq(it->key, "layout")) {
+ SPA_KEY_AUDIO_POSITION, value);
+ } else if (spa_streq(key, "layout")) {
pw_properties_set(props,
- SPA_KEY_AUDIO_LAYOUT, it->value);
- } else if (spa_streq(it->key, "channelnames")) {
+ SPA_KEY_AUDIO_LAYOUT, value);
+ } else if (spa_streq(key, "channelnames")) {
pw_properties_set(props,
- PW_KEY_NODE_CHANNELNAMES, it->value);
- } else if (spa_streq(it->key, "ts-refclk")) {
+ PW_KEY_NODE_CHANNELNAMES, value);
+ } else if (spa_streq(key, "ts-refclk")) {
pw_properties_set(props,
- "sess.ts-refclk", it->value);
- if (spa_streq(it->value, impl->ts_refclk))
+ "sess.ts-refclk", value);
+ if (spa_streq(value, impl->ts_refclk))
pw_properties_set(props,
"sess.ts-direct", "true");
- } else if (spa_streq(it->key, "ts-offset")) {
+ } else if (spa_streq(key, "ts-offset")) {
uint32_t v;
- if (spa_atou32(it->value, &v, 0))
+ if (spa_atou32(value, &v, 0))
pw_properties_setf(props,
"rtp.receiver-ts-offset", "%u", v);
}
if (k != NULL) {
str = pw_properties_get(props, k);
- if (str == NULL || !spa_streq(str, it->value))
+ if (str == NULL || !spa_streq(str, value))
compatible = false;
}
+ avahi_free(key);
+ avahi_free(value);
}
str = pw_properties_get(props, "sess.media");
if (spa_streq(str, "opus") && mask != 0xd)
@@ -1373,147 +1368,281 @@ static void on_zeroconf_added(void *data, const void *user, const struct spa_dic
}
if (!compatible) {
pw_log_info("found incompatible session IP%d:%s",
- sinfo.protocol, sinfo.name);
+ info->protocol == AVAHI_PROTO_INET ? 4 : 6,
+ info->name);
res = 0;
goto error;
}
- address = spa_dict_lookup(info, PW_KEY_ZEROCONF_ADDRESS);
- hostname = spa_dict_lookup(info, PW_KEY_ZEROCONF_HOSTNAME);
+ s = calloc(1, sizeof(*s));
+ if (s == NULL) {
+ res = -errno;
+ goto error;
+ }
- pw_log_info("create session: %s %s:%u %s", sinfo.name, address, port, sinfo.type);
+ s->impl = impl;
+ spa_list_append(&impl->service_list, &s->link);
- pw_properties_set(props, "sess.name", sinfo.name);
- pw_properties_set(props, "destination.ip", address);
- pw_properties_setf(props, "destination.ifindex", "%u", sinfo.ifindex);
- pw_properties_setf(props, "destination.port", "%u", port);
+ s->info.interface = info->interface;
+ s->info.protocol = info->protocol;
+ s->info.name = strdup(info->name);
+ s->info.type = strdup(info->type);
+ s->info.domain = strdup(info->domain);
+ s->info.host_name = strdup(info->host_name);
+ s->info.address = info->address;
+ s->info.port = info->port;
+
+ avahi_address_snprint(at, sizeof(at), &s->info.address);
+ pw_log_info("create session: %s %s:%u %s", s->info.name, at, s->info.port, s->info.type);
+
+ if (s->info.protocol == AVAHI_PROTO_INET6 &&
+ s->info.address.data.ipv6.address[0] == 0xfe &&
+ (s->info.address.data.ipv6.address[1] & 0xc0) == 0x80)
+ snprintf(if_suffix, sizeof(if_suffix), "%%%d", s->info.interface);
+
+ ipv = s->info.protocol == AVAHI_PROTO_INET ? 4 : 6;
+ pw_properties_set(props, "sess.name", s->info.name);
+ pw_properties_setf(props, "destination.ip", "%s%s", at, if_suffix);
+ pw_properties_setf(props, "destination.ifindex", "%u", s->info.interface);
+ pw_properties_setf(props, "destination.port", "%u", s->info.port);
if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
pw_properties_setf(props, PW_KEY_NODE_NAME, "rtp_session.%s.%s.ipv%d",
- sinfo.name, hostname, sinfo.protocol);
+ s->info.name, s->info.host_name, ipv);
if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL)
pw_properties_setf(props, PW_KEY_NODE_DESCRIPTION, "%s (IPv%d)",
- sinfo.name, sinfo.protocol);
+ s->info.name, ipv);
if (pw_properties_get(props, PW_KEY_MEDIA_NAME) == NULL)
pw_properties_setf(props, PW_KEY_MEDIA_NAME, "RTP Session with %s (IPv%d)",
- sinfo.name, sinfo.protocol);
+ s->info.name, ipv);
- sess = make_session(impl, &sinfo, spa_steal_ptr(props));
+ sess = make_session(impl, props);
+ props = NULL;
if (sess == NULL) {
res = -errno;
pw_log_error("can't create session: %m");
goto error;
}
+ s->sess = sess;
- if ((res = pw_net_parse_address(address, port, &sess->ctrl_addr, &sess->ctrl_len)) < 0) {
- pw_log_error("invalid address %s: %s", address, spa_strerror(res));
+ if ((res = pw_net_parse_address(at, s->info.port, &sess->ctrl_addr, &sess->ctrl_len)) < 0) {
+ pw_log_error("invalid address %s: %s", at, spa_strerror(res));
}
- if ((res = pw_net_parse_address(address, port+1, &sess->data_addr, &sess->data_len)) < 0) {
- pw_log_error("invalid address %s: %s", address, spa_strerror(res));
+ if ((res = pw_net_parse_address(at, s->info.port+1, &sess->data_addr, &sess->data_len)) < 0) {
+ pw_log_error("invalid address %s: %s", at, spa_strerror(res));
}
- return;
+ return s;
error:
pw_properties_free(props);
- return;
+ if (s != NULL)
+ free_service(s);
+ errno = -res;
+ return NULL;
}
-static void on_zeroconf_removed(void *data, const void *user, const struct spa_dict *info)
+static struct service *find_service(struct impl *impl, const struct service_info *info)
{
- struct impl *impl = data;
+ struct service *s;
+ spa_list_for_each(s, &impl->service_list, link) {
+ if (s->info.interface == info->interface &&
+ s->info.protocol == info->protocol &&
+ spa_streq(s->info.name, info->name) &&
+ spa_streq(s->info.type, info->type) &&
+ spa_streq(s->info.domain, info->domain))
+ return s;
+ }
+ return NULL;
+}
+
+static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiResolverEvent event, const char *name, const char *type, const char *domain,
+ const char *host_name, const AvahiAddress *a, uint16_t port, AvahiStringList *txt,
+ AvahiLookupResultFlags flags, void *userdata)
+{
+ struct impl *impl = userdata;
struct service_info sinfo;
- struct session *sess;
- const char *str;
- int ifindex = -1, protocol = 0;
- if ((str = spa_dict_lookup(info, PW_KEY_ZEROCONF_IFINDEX)))
- ifindex = atoi(str);
- if ((str = spa_dict_lookup(info, PW_KEY_ZEROCONF_PROTO)))
- protocol = atoi(str);
+ if (event != AVAHI_RESOLVER_FOUND) {
+ pw_log_error("Resolving of '%s' failed: %s", name,
+ avahi_strerror(avahi_client_errno(impl->client)));
+ goto done;
+ }
- sinfo = SERVICE_INFO(.ifindex = ifindex,
+ sinfo = SERVICE_INFO(.interface = interface,
.protocol = protocol,
- .name = spa_dict_lookup(info, PW_KEY_ZEROCONF_NAME),
- .type = spa_dict_lookup(info, PW_KEY_ZEROCONF_TYPE),
- .domain = spa_dict_lookup(info, PW_KEY_ZEROCONF_DOMAIN));
+ .name = name,
+ .type = type,
+ .domain = domain,
+ .host_name = host_name,
+ .address = *a,
+ .port = port);
- sess = find_session_by_info(impl, &sinfo);
- if (sess == NULL)
+ make_service(impl, &sinfo, txt);
+done:
+ avahi_service_resolver_free(r);
+}
+
+static void browser_cb(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiBrowserEvent event, const char *name, const char *type, const char *domain,
+ AvahiLookupResultFlags flags, void *userdata)
+{
+ struct impl *impl = userdata;
+ struct service_info info;
+ struct service *s;
+
+ if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) && !impl->discover_local)
return;
- free_session(sess);
+ info = SERVICE_INFO(.interface = interface,
+ .protocol = protocol,
+ .name = name,
+ .type = type,
+ .domain = domain);
+
+ s = find_service(impl, &info);
+
+ switch (event) {
+ case AVAHI_BROWSER_NEW:
+ if (s != NULL)
+ return;
+ if (!(avahi_service_resolver_new(impl->client,
+ interface, protocol,
+ name, type, domain,
+ AVAHI_PROTO_UNSPEC, 0,
+ resolver_cb, impl)))
+ pw_log_error("can't make service resolver: %s",
+ avahi_strerror(avahi_client_errno(impl->client)));
+ break;
+ case AVAHI_BROWSER_REMOVE:
+ if (s == NULL)
+ return;
+ free_service(s);
+ break;
+ default:
+ break;
+ }
}
static int make_browser(struct impl *impl)
{
- const char *service_type;
- int res;
+ const char *service_name;
- service_type = get_service_type(impl);
- if (service_type == NULL)
+ service_name = get_service_name(impl);
+ if (service_name == NULL)
return -EINVAL;
- if ((res = pw_zeroconf_set_browse(impl->zeroconf, impl,
- &SPA_DICT_ITEMS(
- SPA_DICT_ITEM(PW_KEY_ZEROCONF_TYPE, service_type)))) < 0) {
- pw_log_error("can't make browser for %s: %s",
- service_type, spa_strerror(res));
- return res;
+ if (impl->browser == NULL) {
+ impl->browser = avahi_service_browser_new(impl->client,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ service_name, NULL, 0,
+ browser_cb, impl);
+ }
+ if (impl->browser == NULL) {
+ pw_log_error("can't make browser: %s",
+ avahi_strerror(avahi_client_errno(impl->client)));
+ return -EIO;
}
return 0;
}
+static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata)
+{
+ switch (state) {
+ case AVAHI_ENTRY_GROUP_ESTABLISHED:
+ pw_log_info("Service successfully established");
+ break;
+ case AVAHI_ENTRY_GROUP_COLLISION:
+ pw_log_error("Service name collision");
+ break;
+ case AVAHI_ENTRY_GROUP_FAILURE:
+ pw_log_error("Entry group failure: %s",
+ avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
+ break;
+ case AVAHI_ENTRY_GROUP_UNCOMMITED:
+ case AVAHI_ENTRY_GROUP_REGISTERING:;
+ break;
+ }
+}
+
static int make_announce(struct impl *impl)
{
int res;
- const char *service_type, *str;
- struct pw_properties *props;
+ const char *service_name, *str;
+ AvahiStringList *txt = NULL;
- props = pw_properties_new(NULL, NULL);
-
- if ((service_type = get_service_type(impl)) == NULL)
+ if ((service_name = get_service_name(impl)) == NULL)
return -ENOTSUP;
- if (spa_streq(service_type, "_pipewire-audio._udp")) {
+ if (impl->group == NULL) {
+ impl->group = avahi_entry_group_new(impl->client,
+ entry_group_callback, impl);
+ }
+ if (impl->group == NULL) {
+ pw_log_error("can't make group: %s",
+ avahi_strerror(avahi_client_errno(impl->client)));
+ return -EIO;
+ }
+ avahi_entry_group_reset(impl->group);
+
+ if (spa_streq(service_name, "_pipewire-audio._udp")) {
str = pw_properties_get(impl->props, "sess.media");
- pw_properties_set(props, "subtype", str);
+ txt = avahi_string_list_add_pair(txt, "subtype", str);
if ((str = pw_properties_get(impl->stream_props, PW_KEY_AUDIO_FORMAT)) != NULL)
- pw_properties_set(props, "format", str);
+ txt = avahi_string_list_add_pair(txt, "format", str);
if ((str = pw_properties_get(impl->stream_props, PW_KEY_AUDIO_RATE)) != NULL)
- pw_properties_set(props, "rate", str);
+ txt = avahi_string_list_add_pair(txt, "rate", str);
if ((str = pw_properties_get(impl->stream_props, PW_KEY_AUDIO_CHANNELS)) != NULL)
- pw_properties_set(props, "channels", str);
+ txt = avahi_string_list_add_pair(txt, "channels", str);
if ((str = pw_properties_get(impl->stream_props, SPA_KEY_AUDIO_POSITION)) != NULL)
- pw_properties_set(props, "position", str);
+ txt = avahi_string_list_add_pair(txt, "position", str);
if ((str = pw_properties_get(impl->stream_props, SPA_KEY_AUDIO_LAYOUT)) != NULL)
- pw_properties_set(props, "layout", str);
+ txt = avahi_string_list_add_pair(txt, "layout", str);
if ((str = pw_properties_get(impl->stream_props, PW_KEY_NODE_CHANNELNAMES)) != NULL)
- pw_properties_set(props, "channelnames", str);
+ txt = avahi_string_list_add_pair(txt, "channelnames", str);
if (impl->ts_refclk != NULL) {
- pw_properties_set(props, "ts-refclk", impl->ts_refclk);
- pw_properties_setf(props, "ts-offset", "%u", impl->ts_offset);
+ txt = avahi_string_list_add_pair(txt, "ts-refclk", impl->ts_refclk);
+ txt = avahi_string_list_add_printf(txt, "ts-offset=%u", impl->ts_offset);
}
}
+ res = avahi_entry_group_add_service_strlst(impl->group,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ (AvahiPublishFlags)0, impl->session_name,
+ service_name, NULL, NULL,
+ impl->ctrl_port, txt);
- pw_properties_set(props, PW_KEY_ZEROCONF_NAME, impl->session_name);
- pw_properties_set(props, PW_KEY_ZEROCONF_TYPE, service_type);
- pw_properties_setf(props, PW_KEY_ZEROCONF_PORT, "%u", impl->ctrl_port);
-
- res = pw_zeroconf_set_announce(impl->zeroconf, impl, &props->dict);
-
- pw_properties_free(props);
+ avahi_string_list_free(txt);
if (res < 0) {
- pw_log_error("can't add service: %s", spa_strerror(res));
- return res;
+ pw_log_error("can't add service: %s",
+ avahi_strerror(avahi_client_errno(impl->client)));
+ return -EIO;
+ }
+ if ((res = avahi_entry_group_commit(impl->group)) < 0) {
+ pw_log_error("can't commit group: %s",
+ avahi_strerror(avahi_client_errno(impl->client)));
+ return -EIO;
}
return 0;
}
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata)
+{
+ struct impl *impl = userdata;
+ impl->client = c;
-static const struct pw_zeroconf_events zeroconf_events = {
- PW_VERSION_ZEROCONF_EVENTS,
- .added = on_zeroconf_added,
- .removed = on_zeroconf_removed,
-};
+ switch (state) {
+ case AVAHI_CLIENT_S_REGISTERING:
+ case AVAHI_CLIENT_S_RUNNING:
+ case AVAHI_CLIENT_S_COLLISION:
+ make_browser(impl);
+ make_announce(impl);
+ break;
+ case AVAHI_CLIENT_FAILURE:
+ case AVAHI_CLIENT_CONNECTING:
+ break;
+ default:
+ break;
+ }
+}
static void copy_props(struct impl *impl, struct pw_properties *props, const char *key)
{
@@ -1544,6 +1673,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
args = "";
spa_list_init(&impl->sessions);
+ spa_list_init(&impl->service_list);
props = pw_properties_new_string(args);
if (props == NULL) {
@@ -1555,8 +1685,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
impl->discover_local = pw_properties_get_bool(impl->props,
"sess.discover-local", false);
- pw_properties_set(impl->props, PW_KEY_ZEROCONF_DISCOVER_LOCAL,
- impl->discover_local ? "true" : "false");
stream_props = pw_properties_new(NULL, NULL);
if (stream_props == NULL) {
@@ -1676,17 +1804,14 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
if ((res = setup_apple_session(impl)) < 0)
goto out;
- impl->zeroconf = pw_zeroconf_new(impl->context, &impl->props->dict);
- if (impl->zeroconf == NULL) {
- res = -errno;
- pw_log_error("can't create zeroconf: %m");
+ impl->avahi_poll = pw_avahi_poll_new(impl->context);
+ if ((impl->client = avahi_client_new(impl->avahi_poll,
+ AVAHI_CLIENT_NO_FAIL,
+ client_callback, impl,
+ &res)) == NULL) {
+ pw_log_error("can't create avahi client: %s", avahi_strerror(res));
goto out;
}
- pw_zeroconf_add_listener(impl->zeroconf, &impl->zeroconf_listener,
- &zeroconf_events, impl);
-
- make_browser(impl);
- make_announce(impl);
pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
diff --git a/src/modules/module-rtp-source.c b/src/modules/module-rtp-source.c
index b0b52ed50..8f911c62a 100644
--- a/src/modules/module-rtp-source.c
+++ b/src/modules/module-rtp-source.c
@@ -231,7 +231,7 @@ struct impl {
/* Monotonic timestamp of the last time a packet was
* received. This is accessed with atomic accessors
* to avoid race conditions. */
- SPA_ALIGNED(8) uint64_t last_packet_time;
+ uint64_t last_packet_time;
struct pw_timer standby_timer;
/* This timer is used when the first stream_start() call fails because
@@ -246,9 +246,6 @@ struct impl {
socklen_t src_len;
struct spa_source *source;
- bool is_multicast;
- bool filter_by_address;
-
uint8_t *buffer;
size_t buffer_size;
@@ -303,41 +300,14 @@ on_rtp_io(void *data, int fd, uint32_t mask)
ssize_t len;
int suppressed;
uint64_t current_time;
- struct sockaddr_storage recvaddr;
- socklen_t recvaddr_len = sizeof(recvaddr);
current_time = get_time_ns(impl);
if (mask & SPA_IO_IN) {
- if ((len = recvfrom(fd, impl->buffer, impl->buffer_size, 0, (struct sockaddr *)(&recvaddr), &recvaddr_len)) < 0)
+
+ if ((len = recv(fd, impl->buffer, impl->buffer_size, 0)) < 0)
goto receive_error;
- /* Filter the packets to exclude those with source addresses
- * that do not match the expected one. Only used with unicast.
- * (The bind() call in make_socket takes care of only
- * receiving packets that target the specified port.) */
- if (impl->filter_by_address && !pw_net_are_addresses_equal(&recvaddr, &(impl->src_addr), false)) {
- /* In the IPv6 case, pw_net_get_ip() produces output formatted
- * as "%". Both constants
- * INET6_ADDRSTRLEN and IFNAMSIZ include the null terminator
- * in their respective length values. This works out well for
- * the formatted output, since this ensures there is one extra
- * character for the % delimiter and another extra character
- * for the null terminator of the entire string.
- *
- * (In the IPv4 case, pw_net_get_ip() just outputs the address.) */
- char address_str[INET6_ADDRSTRLEN + IFNAMSIZ];
- int res;
-
- res = pw_net_get_ip(&recvaddr, address_str, sizeof(address_str), NULL, NULL);
- if (SPA_LIKELY(res == 0))
- pw_log_trace("Filtering out packet with mismatching address %s", address_str);
- else
- pw_log_warn("Filtering out packet with unrecognized address");
-
- return;
- }
-
if (len < 12)
goto short_packet;
@@ -504,12 +474,12 @@ finish:
}
static int make_socket(const struct sockaddr* sa, socklen_t salen, char *ifname,
- struct igmp_recovery *igmp_recovery, bool *is_multicast,
- bool *filter_by_address)
+ struct igmp_recovery *igmp_recovery)
{
int af, fd, val, res;
struct ifreq req;
struct sockaddr_storage ba = *(struct sockaddr_storage *)sa;
+ bool do_connect = false;
char addr[128];
af = sa->sa_family;
@@ -552,16 +522,12 @@ static int make_socket(const struct sockaddr* sa, socklen_t salen, char *ifname,
pw_net_get_ip((struct sockaddr_storage*)sa, addr, sizeof(addr), NULL, NULL);
pw_log_info("join IPv4 group: %s", addr);
res = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr4, sizeof(mr4));
- *filter_by_address = false;
- *is_multicast = true;
} else {
struct sockaddr_in *ba4 = (struct sockaddr_in*)&ba;
- *filter_by_address = (ba4->sin_addr.s_addr != INADDR_ANY);
- *is_multicast = false;
- /* Make sure the ANY address is always used. This is important
- * for the bind() call below - with unicast, it shall only filter
- * by port number (address filtering is done by recvfrom()). */
- ba4->sin_addr.s_addr = INADDR_ANY;
+ if (ba4->sin_addr.s_addr != INADDR_ANY) {
+ ba4->sin_addr.s_addr = INADDR_ANY;
+ do_connect = true;
+ }
}
} else if (af == AF_INET6) {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa;
@@ -573,15 +539,8 @@ static int make_socket(const struct sockaddr* sa, socklen_t salen, char *ifname,
pw_net_get_ip((struct sockaddr_storage*)sa, addr, sizeof(addr), NULL, NULL);
pw_log_info("join IPv6 group: %s", addr);
res = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mr6, sizeof(mr6));
- *filter_by_address = false;
- *is_multicast = true;
} else {
struct sockaddr_in6 *ba6 = (struct sockaddr_in6*)&ba;
- *filter_by_address = !IN6_IS_ADDR_UNSPECIFIED(&(ba6->sin6_addr.s6_addr));
- *is_multicast = false;
- /* Make sure the ANY address is always used. This is important
- * for the bind() call below - with unicast, it shall only filter
- * by port number (address filtering is done by recvfrom()). */
ba6->sin6_addr = in6addr_any;
}
} else {
@@ -610,6 +569,13 @@ static int make_socket(const struct sockaddr* sa, socklen_t salen, char *ifname,
pw_log_error("bind() failed: %m");
goto error;
}
+ if (do_connect) {
+ if (connect(fd, sa, salen) < 0) {
+ res = -errno;
+ pw_log_error("connect() failed: %m");
+ goto error;
+ }
+ }
return fd;
error:
close(fd);
@@ -647,9 +613,7 @@ static void stream_open_connection(void *data, int *result)
if ((fd = make_socket((const struct sockaddr *)&impl->src_addr,
impl->src_len, impl->ifname,
- &(impl->igmp_recovery),
- &(impl->is_multicast),
- &(impl->filter_by_address))) < 0) {
+ &(impl->igmp_recovery))) < 0) {
/* If make_socket() tries to create a socket and join to a multicast
* group while the network interfaces are not ready yet to do so
* (usually because a network manager component is still setting up
@@ -699,13 +663,11 @@ static void stream_open_connection(void *data, int *result)
goto finish;
}
- if (impl->is_multicast) {
- if ((res = pw_timer_queue_add(impl->timer_queue, &impl->igmp_recovery.timer,
- NULL, impl->igmp_recovery.check_interval * SPA_NSEC_PER_SEC,
- on_igmp_recovery_timer_event, impl)) < 0) {
- pw_log_error("can't add timer: %s", spa_strerror(res));
- goto finish;
- }
+ if ((res = pw_timer_queue_add(impl->timer_queue, &impl->igmp_recovery.timer,
+ NULL, impl->igmp_recovery.check_interval * SPA_NSEC_PER_SEC,
+ on_igmp_recovery_timer_event, impl)) < 0) {
+ pw_log_error("can't add timer: %s", spa_strerror(res));
+ goto finish;
}
finish:
diff --git a/src/modules/module-rtp/audio.c b/src/modules/module-rtp/audio.c
index 085f3bae8..d20e9a37c 100644
--- a/src/modules/module-rtp/audio.c
+++ b/src/modules/module-rtp/audio.c
@@ -546,7 +546,7 @@ static void rtp_audio_flush_packets(struct impl *impl, uint32_t num_packets, uin
else
header.m = 0;
- rtp_timestamp = impl->ts_offset + impl->ts_align + (set_timestamp ? set_timestamp : timestamp);
+ rtp_timestamp = impl->ts_offset + (set_timestamp ? set_timestamp : timestamp);
header.sequence_number = htons(impl->seq);
header.timestamp = htonl(rtp_timestamp);
@@ -556,12 +556,12 @@ static void rtp_audio_flush_packets(struct impl *impl, uint32_t num_packets, uin
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
&iov[1], tosend * stride);
- pw_log_trace_fp("sending %d packet:%d ts_offset:%d timestamp:%u (%f s)",
+ pw_log_trace("sending %d packet:%d ts_offset:%d timestamp:%u (%f s)",
tosend, num_packets, impl->ts_offset, timestamp,
(double)timestamp * impl->io_position->clock.rate.num /
impl->io_position->clock.rate.denom);
- rtp_stream_call_send_packet(impl, iov, 3);
+ rtp_stream_emit_send_packet(impl, iov, 3);
impl->seq++;
impl->first = false;
@@ -606,7 +606,7 @@ static void rtp_audio_stop_timer(struct impl *impl)
static void rtp_audio_flush_timeout(struct impl *impl, uint64_t expirations)
{
if (expirations > 1)
- pw_log_trace("missing timeout %"PRIu64, expirations);
+ pw_log_warn("missing timeout %"PRIu64, expirations);
rtp_audio_flush_packets(impl, expirations, 0);
}
@@ -705,10 +705,8 @@ static void rtp_audio_process_capture(void *data)
* that resynchronization is needed, then this will be done immediately below. */
if (!impl->have_sync) {
- if (!impl->direct_timestamp)
- impl->ts_align = actual_timestamp - impl->ring.readindex;
- pw_log_info("(re)sync to timestamp:%u seq:%u ts_offset:%u ts_align:%u SSRC:%u",
- actual_timestamp, impl->seq, impl->ts_offset, impl->ts_align, impl->ssrc);
+ pw_log_info("(re)sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u",
+ actual_timestamp, impl->seq, impl->ts_offset, impl->ssrc);
spa_ringbuffer_read_update(&impl->ring, actual_timestamp);
spa_ringbuffer_write_update(&impl->ring, actual_timestamp);
memset(impl->buffer, 0, BUFFER_SIZE);
diff --git a/src/modules/module-rtp/midi.c b/src/modules/module-rtp/midi.c
index 1237f66c6..5fbdf3b63 100644
--- a/src/modules/module-rtp/midi.c
+++ b/src/modules/module-rtp/midi.c
@@ -151,7 +151,7 @@ static int parse_journal(struct impl *impl, uint8_t *packet, uint16_t seq, uint3
return -EINVAL;
j = (struct rtp_midi_journal*)packet;
uint16_t seqnum = ntohs(j->checkpoint_seqnum);
- rtp_stream_call_send_feedback(impl, seqnum);
+ rtp_stream_emit_send_feedback(impl, seqnum);
return 0;
}
@@ -271,6 +271,9 @@ static int rtp_midi_receive_midi(struct impl *impl, uint8_t *packet, uint32_t ti
while (offs < end) {
uint32_t delta;
int size;
+ uint64_t state = 0;
+ uint8_t *d;
+ size_t s;
if (first && !hdr.z)
delta = 0;
@@ -291,9 +294,17 @@ static int rtp_midi_receive_midi(struct impl *impl, uint8_t *packet, uint32_t ti
return -EINVAL;
}
- spa_pod_builder_control(&b, timestamp, SPA_CONTROL_Midi);
- spa_pod_builder_bytes(&b, &packet[offs], size);
+ d = &packet[offs];
+ s = size;
+ while (s > 0) {
+ uint32_t ump[4];
+ int ump_size = spa_ump_from_midi(&d, &s, ump, sizeof(ump), 0, &state);
+ if (ump_size <= 0)
+ break;
+ spa_pod_builder_control(&b, timestamp, SPA_CONTROL_UMP);
+ spa_pod_builder_bytes(&b, ump, ump_size);
+ }
offs += size;
first = false;
}
@@ -367,7 +378,7 @@ unexpected_ssrc:
return -EINVAL;
}
-static int write_event(uint8_t *p, uint32_t buffer_size, uint32_t value, const void *ev, uint32_t size)
+static int write_event(uint8_t *p, uint32_t buffer_size, uint32_t value, void *ev, uint32_t size)
{
uint64_t buffer;
uint8_t b;
@@ -426,54 +437,62 @@ static void rtp_midi_flush_packets(struct impl *impl,
while (spa_pod_parser_get_control_body(parser, &c, &c_body) >= 0) {
uint32_t delta, offset;
- uint32_t size = c.value.size;
- const uint8_t *data = c_body;
+ uint8_t event[16];
+ int size;
+ size_t c_size = c.value.size;
+ uint64_t state = 0;
- if (c.type != SPA_CONTROL_Midi)
+ if (c.type != SPA_CONTROL_UMP)
continue;
- offset = c.offset * impl->rate / rate;
+ while (c_size > 0) {
+ size = spa_ump_to_midi((const uint32_t **)&c_body, &c_size, event, sizeof(event), &state);
+ if (size <= 0)
+ break;
- if (len > 0 && (len + size > max_size ||
- offset - base > impl->psamples)) {
- /* flush packet when we have one and when it's either
- * too large or has too much data. */
- if (len < 16) {
- midi_header.b = 0;
- midi_header.len = len;
- iov[1].iov_len = sizeof(midi_header) - 1;
- } else {
- midi_header.b = 1;
- midi_header.len = (len >> 8) & 0xf;
- midi_header.len_b = len & 0xff;
- iov[1].iov_len = sizeof(midi_header);
+ offset = c.offset * impl->rate / rate;
+
+ if (len > 0 && (len + size > max_size ||
+ offset - base > impl->psamples)) {
+ /* flush packet when we have one and when it's either
+ * too large or has too much data. */
+ if (len < 16) {
+ midi_header.b = 0;
+ midi_header.len = len;
+ iov[1].iov_len = sizeof(midi_header) - 1;
+ } else {
+ midi_header.b = 1;
+ midi_header.len = (len >> 8) & 0xf;
+ midi_header.len_b = len & 0xff;
+ iov[1].iov_len = sizeof(midi_header);
+ }
+ iov[2].iov_len = len;
+
+ pw_log_trace("sending %d timestamp:%d %u %u",
+ len, timestamp + base,
+ offset, impl->psamples);
+ rtp_stream_emit_send_packet(impl, iov, 3);
+
+ impl->seq++;
+ len = 0;
}
- iov[2].iov_len = len;
+ if ((unsigned int)size > BUFFER_SIZE || len > BUFFER_SIZE - size) {
+ pw_log_error("Buffer overflow prevented!");
+ return; // FIXME: what to do instead?
+ }
+ if (len == 0) {
+ /* start new packet */
+ base = prev_offset = offset;
+ header.sequence_number = htons(impl->seq);
+ header.timestamp = htonl(impl->ts_offset + timestamp + base);
- pw_log_trace("sending %d timestamp:%d %u %u",
- len, timestamp + base,
- offset, impl->psamples);
- rtp_stream_call_send_packet(impl, iov, 3);
-
- impl->seq++;
- len = 0;
- }
- if ((unsigned int)size > BUFFER_SIZE || len > BUFFER_SIZE - size) {
- pw_log_error("Buffer overflow prevented!");
- return; // FIXME: what to do instead?
- }
- if (len == 0) {
- /* start new packet */
- base = prev_offset = offset;
- header.sequence_number = htons(impl->seq);
- header.timestamp = htonl(impl->ts_offset + timestamp + base);
-
- memcpy(&impl->buffer[len], data, size);
- len += size;
- } else {
- delta = offset - prev_offset;
- prev_offset = offset;
- len += write_event(&impl->buffer[len], BUFFER_SIZE - len, delta, data, size);
+ memcpy(&impl->buffer[len], event, size);
+ len += size;
+ } else {
+ delta = offset - prev_offset;
+ prev_offset = offset;
+ len += write_event(&impl->buffer[len], BUFFER_SIZE - len, delta, event, size);
+ }
}
}
if (len > 0) {
@@ -491,7 +510,7 @@ static void rtp_midi_flush_packets(struct impl *impl,
iov[2].iov_len = len;
pw_log_trace("sending %d timestamp:%d", len, base);
- rtp_stream_call_send_packet(impl, iov, 3);
+ rtp_stream_emit_send_packet(impl, iov, 3);
impl->seq++;
}
}
diff --git a/src/modules/module-rtp/opus.c b/src/modules/module-rtp/opus.c
index 9175d4a31..d13a4efaf 100644
--- a/src/modules/module-rtp/opus.c
+++ b/src/modules/module-rtp/opus.c
@@ -252,7 +252,7 @@ static void rtp_opus_flush_packets(struct impl *impl)
pw_log_trace("sending %d len:%d timestamp:%d", tosend, res, timestamp);
iov[1].iov_len = res;
- rtp_stream_call_send_packet(impl, iov, 2);
+ rtp_stream_emit_send_packet(impl, iov, 2);
impl->seq++;
timestamp += tosend;
diff --git a/src/modules/module-rtp/stream.c b/src/modules/module-rtp/stream.c
index 11bba4f98..d69b16524 100644
--- a/src/modules/module-rtp/stream.c
+++ b/src/modules/module-rtp/stream.c
@@ -48,11 +48,8 @@ PW_LOG_TOPIC_EXTERN(mod_topic);
#define rtp_stream_emit_open_connection(s,r) rtp_stream_emit(s, open_connection, 0,r)
#define rtp_stream_emit_close_connection(s,r) rtp_stream_emit(s, close_connection, 0,r)
#define rtp_stream_emit_param_changed(s,i,p) rtp_stream_emit(s, param_changed,0,i,p)
-
-#define rtp_stream_call(s,m,v,...) spa_callbacks_call_fast(&s->rtp_callbacks, \
- struct rtp_stream_events, m, v, ##__VA_ARGS__)
-#define rtp_stream_call_send_packet(s,i,l) rtp_stream_call(s, send_packet,0,i,l)
-#define rtp_stream_call_send_feedback(s,seq) rtp_stream_call(s, send_feedback,0,seq)
+#define rtp_stream_emit_send_packet(s,i,l) rtp_stream_emit(s, send_packet,0,i,l)
+#define rtp_stream_emit_send_feedback(s,seq) rtp_stream_emit(s, send_feedback,0,seq)
enum rtp_stream_internal_state {
/* The state when the stream is idle / stopped. The background
@@ -88,8 +85,6 @@ struct impl {
struct spa_hook stream_listener;
struct pw_stream_events stream_events;
- struct spa_callbacks rtp_callbacks;
-
struct spa_hook_list listener_list;
struct spa_hook listener;
@@ -114,7 +109,6 @@ struct impl {
uint32_t mtu;
uint32_t header_size;
uint32_t payload_size;
- uint32_t ts_align;
struct spa_ringbuffer ring;
uint8_t buffer[BUFFER_SIZE];
@@ -432,7 +426,7 @@ static int stream_stop(struct impl *impl)
* because a stop involves closing the connection. If the timer is still
* running, it needs an open connection for sending out remaining packets. */
if (!timer_running) {
- int res = 0;
+ int res;
pw_log_info("closing connection as part of stopping the stream");
rtp_stream_emit_close_connection(impl, &res);
if (res > 0) {
@@ -1008,7 +1002,6 @@ struct rtp_stream *rtp_stream_new(struct pw_core *core,
(res = stream_start(impl)) < 0)
goto out;
- impl->rtp_callbacks = SPA_CALLBACKS_INIT(events, data);
spa_hook_list_append(&impl->listener_list, &impl->listener, events, data);
return (struct rtp_stream*)impl;
diff --git a/src/modules/module-scheduler-v1.c b/src/modules/module-scheduler-v1.c
deleted file mode 100644
index 5756f7a56..000000000
--- a/src/modules/module-scheduler-v1.c
+++ /dev/null
@@ -1,1040 +0,0 @@
-/* PipeWire */
-/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
-/* SPDX-License-Identifier: MIT */
-
-#include "config.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef HAVE_SYS_VFS_H
-#include
-#endif
-#ifdef HAVE_SYS_MOUNT_H
-#include
-#endif
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-/** \page page_module_scheduler_v1 SchedulerV1
- *
- *
- * ## Module Name
- *
- * `libpipewire-module-scheduler-v1`
- *
- * ## Module Options
- *
- * Options specific to the behavior of this module
- *
- * ## General options
- *
- * Options with well-known behavior:
- *
- * ## Config override
- *
- * A `module.scheduler-v1.args` config section can be added
- * to override the module arguments.
- *
- *\code{.unparsed}
- * # ~/.config/pipewire/pipewire.conf.d/my-scheduler-v1-args.conf
- *
- * module.scheduler-v1.args = {
- * }
- *\endcode
- *
- * ## Example configuration
- *
- *\code{.unparsed}
- * context.modules = [
- * { name = libpipewire-module-scheduler-v1
- * args = {
- * }
- * }
- *]
- *\endcode
- *
- * Since: 1.7.0
- */
-
-#define NAME "scheduler-v1"
-
-PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
-#define PW_LOG_TOPIC_DEFAULT mod_topic
-
-#define MODULE_USAGE ""
-
-static const struct spa_dict_item module_props[] = {
- { PW_KEY_MODULE_AUTHOR, "Wim Taymans " },
- { PW_KEY_MODULE_DESCRIPTION, "Implement the Scheduler V1" },
- { PW_KEY_MODULE_USAGE, MODULE_USAGE },
- { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
-};
-
-#define MAX_HOPS 64
-#define MAX_SYNC 4u
-
-struct impl {
- struct pw_context *context;
-
- struct pw_properties *props;
-
- struct spa_hook context_listener;
- struct spa_hook module_listener;
-};
-
-static int ensure_state(struct pw_impl_node *node, bool running, bool idle)
-{
- enum pw_node_state state = node->info.state;
- bool need_config = SPA_FLAG_IS_SET(node->spa_flags, SPA_NODE_FLAG_NEED_CONFIGURE);
- if (node->active && node->runnable && !need_config & running)
- state = PW_NODE_STATE_RUNNING;
- else if (!node->active && !need_config & idle)
- state = PW_NODE_STATE_IDLE;
- else if (state > PW_NODE_STATE_IDLE)
- state = PW_NODE_STATE_IDLE;
- return pw_impl_node_set_state(node, state);
-}
-
-/* Make a node runnable. Peer nodes are also made runnable when the passive_mode
- * of the peer port is !TRUE.
- *
- * A (*) -> B if A running -> B set to running
- * A (*) -> (f) B if A running -> B set to running
- * A (*) -> (fs) B if A running -> B set to running
- * A (*) -> (p) B if A running -> B no change
- */
-static inline bool makes_runnable(struct pw_impl_port *a, struct pw_impl_port *b)
-{
- return b->passive_mode != PASSIVE_MODE_TRUE;
-}
-static void make_runnable(struct pw_context *context, struct pw_impl_node *node)
-{
- struct pw_impl_port *p;
- struct pw_impl_link *l;
- struct pw_impl_node *n;
-
- if (!node->runnable) {
- pw_log_debug("%s is runnable", node->name);
- node->runnable = true;
- }
-
- spa_list_for_each(p, &node->output_ports, link) {
- spa_list_for_each(l, &p->links, output_link) {
- n = l->input->node;
- pw_log_trace(" out-port %p: link %p passive:%d prepared:%d active:%d runn:%d", p,
- l, l->input->passive_mode, l->prepared, n->active, n->runnable);
- if (!n->active || !makes_runnable(p, l->input))
- continue;
- pw_impl_link_prepare(l);
- if (!l->prepared)
- continue;
- if (!n->runnable)
- make_runnable(context, n);
- }
- }
- spa_list_for_each(p, &node->input_ports, link) {
- spa_list_for_each(l, &p->links, input_link) {
- n = l->output->node;
- pw_log_trace(" in-port %p: link %p passive:%d prepared:%d active:%d runn:%d", p,
- l, l->output->passive_mode, l->prepared, n->active, n->runnable);
- if (!n->active || !makes_runnable(p, l->output))
- continue;
- pw_impl_link_prepare(l);
- if (!l->prepared)
- continue;
- if (!n->runnable)
- make_runnable(context, n);
- }
- }
- /* now go through all the nodes that share groups and link_groups
- * that are not yet runnable. We don't include sync-groups because they
- * are only used to group the node with a driver, not to determine the
- * runnable state of a node. */
- if (node->groups != NULL || node->link_groups != NULL) {
- spa_list_for_each(n, &context->node_list, link) {
- if (n->exported || !n->active || n->runnable)
- continue;
- /* the other node will be scheduled with this one if it's in
- * the same group or link group */
- if (pw_strv_find_common(n->groups, node->groups) < 0 &&
- pw_strv_find_common(n->link_groups, node->link_groups) < 0)
- continue;
-
- make_runnable(context, n);
- }
- }
-}
-
-/* check if a node and its peer can run.
- *
- * Only consider ports that have a PASSIVE_MODE_FALSE link.
- * All other port modes don't make A and B runnable.
- *
- * A -> B A + B both set to running
- * A -> (p) B A + B both set to running
- * A -> (f) B A + B both set to running
- * A (fs) -> (fs) B A + B both set to running
- * A (p) -> (*) B A + B no change
- * A (f) -> (*) B A + B no change
- * A (fs) -> (*) B A + B no change
- *
- * There is a special case for FOLLOW_SUSPEND<->FOLLOW_SUSPEND to make
- * it possible to manually link a source to a sink
- */
-static inline bool runnable_pair(struct pw_impl_port *a, struct pw_impl_port *b)
-{
- bool res = false;
- if (a->passive_mode == PASSIVE_MODE_FALSE)
- res = true;
- if (a->passive_mode == PASSIVE_MODE_FOLLOW_SUSPEND &&
- b->passive_mode == PASSIVE_MODE_FOLLOW_SUSPEND)
- res = true;
- pw_log_trace(" port %p <-> %p: %s <> %s -> %d", a, b,
- passive_mode_to_string(a->passive_mode),
- passive_mode_to_string(b->passive_mode), res);
- return res;
-}
-static void check_runnable(struct pw_context *context, struct pw_impl_node *node)
-{
- struct pw_impl_port *p;
- struct pw_impl_link *l;
- struct pw_impl_node *n;
-
- pw_log_trace("node %p: '%s' always-process:%d runnable:%u active:%d", node,
- node->name, node->always_process, node->runnable, node->active);
-
- if (node->always_process && !node->runnable)
- make_runnable(context, node);
-
- spa_list_for_each(p, &node->output_ports, link) {
- spa_list_for_each(l, &p->links, output_link) {
- n = l->input->node;
- /* the peer needs to be active and we are linked to it
- * with a non-passive link */
- pw_log_trace(" out-port %p: link %p prepared:%d active:%d", p,
- l, l->prepared, n->active);
- if (!n->active || !runnable_pair(p, l->input))
- continue;
- /* explicitly prepare the link in case it was suspended */
- pw_impl_link_prepare(l);
- if (!l->prepared)
- continue;
- make_runnable(context, node);
- make_runnable(context, n);
- }
- }
- spa_list_for_each(p, &node->input_ports, link) {
- spa_list_for_each(l, &p->links, input_link) {
- n = l->output->node;
- pw_log_trace(" in-port %p: link %p prepared:%d active:%d", p,
- l, l->prepared, n->active);
- if (!n->active || !runnable_pair(p, l->output))
- continue;
- pw_impl_link_prepare(l);
- if (!l->prepared)
- continue;
- make_runnable(context, node);
- make_runnable(context, n);
- }
- }
-}
-
-/* Follow all links and groups from node.
- *
- * After this is done, we end up with a list of nodes in collect that are all
- * linked to node.
- *
- * We don't need to care about active nodes or links, we just follow and group everything.
- * The inactive nodes or links will simply not be runnable but will already be grouped
- * correctly when they do become active and prepared.
- */
-static int collect_nodes(struct pw_context *context, struct pw_impl_node *node, struct spa_list *collect)
-{
- struct spa_list queue;
- struct pw_impl_node *n, *t;
- struct pw_impl_port *p;
- struct pw_impl_link *l;
- uint32_t n_sync;
- char *sync[MAX_SYNC+1];
-
- pw_log_debug("node %p: '%s'", node, node->name);
-
- /* start with node in the queue */
- spa_list_init(&queue);
- spa_list_append(&queue, &node->sort_link);
- node->visited = true;
-
- n_sync = 0;
- sync[0] = NULL;
-
- /* now follow all the links from the nodes in the queue
- * and add the peers to the queue. */
- spa_list_consume(n, &queue, sort_link) {
- spa_list_remove(&n->sort_link);
- spa_list_append(collect, &n->sort_link);
-
- pw_log_debug(" next node %p: '%s' runnable:%u active:%d",
- n, n->name, n->runnable, n->active);
-
- if (n->sync) {
- for (uint32_t i = 0; n->sync_groups[i]; i++) {
- if (n_sync >= MAX_SYNC)
- break;
- if (pw_strv_find(sync, n->sync_groups[i]) >= 0)
- continue;
- sync[n_sync++] = n->sync_groups[i];
- sync[n_sync] = NULL;
- }
- }
-
- spa_list_for_each(p, &n->input_ports, link) {
- spa_list_for_each(l, &p->links, input_link) {
- t = l->output->node;
- if (!t->visited) {
- t->visited = true;
- spa_list_append(&queue, &t->sort_link);
- }
- }
- }
- spa_list_for_each(p, &n->output_ports, link) {
- spa_list_for_each(l, &p->links, output_link) {
- t = l->input->node;
- if (!t->visited) {
- t->visited = true;
- spa_list_append(&queue, &t->sort_link);
- }
- }
- }
- /* now go through all the nodes that have the same groups and
- * that are not yet visited */
- if (n->groups != NULL || n->link_groups != NULL || sync[0] != NULL) {
- spa_list_for_each(t, &context->node_list, link) {
- if (t->exported || t->visited)
- continue;
- /* the other node will be scheduled with this one if it's in
- * the same group, link group or sync group */
- if (pw_strv_find_common(t->groups, n->groups) < 0 &&
- pw_strv_find_common(t->link_groups, n->link_groups) < 0 &&
- pw_strv_find_common(t->sync_groups, sync) < 0)
- continue;
-
- pw_log_debug("%p: %s join group of %s",
- t, t->name, n->name);
- t->visited = true;
- spa_list_append(&queue, &t->sort_link);
- }
- }
- pw_log_debug(" next node %p: '%s' runnable:%u %p %p %p", n, n->name, n->runnable,
- n->groups, n->link_groups, sync);
- }
- return 0;
-}
-
-static void move_to_driver(struct pw_context *context, struct spa_list *nodes,
- struct pw_impl_node *driver)
-{
- struct pw_impl_node *n;
- pw_log_debug("driver: %p %s runnable:%u", driver, driver->name, driver->runnable);
- spa_list_consume(n, nodes, sort_link) {
- spa_list_remove(&n->sort_link);
-
- driver->runnable |= n->runnable;
-
- pw_log_debug(" follower: %p %s runnable:%u driver-runnable:%u", n, n->name,
- n->runnable, driver->runnable);
- pw_impl_node_set_driver(n, driver);
- }
-}
-static void remove_from_driver(struct pw_context *context, struct spa_list *nodes)
-{
- struct pw_impl_node *n;
- spa_list_consume(n, nodes, sort_link) {
- spa_list_remove(&n->sort_link);
- pw_impl_node_set_driver(n, NULL);
- ensure_state(n, false, false);
- }
-}
-
-static inline void get_quantums(struct pw_context *context, uint32_t *def,
- uint32_t *min, uint32_t *max, uint32_t *rate, uint32_t *floor, uint32_t *ceil)
-{
- struct settings *s = &context->settings;
- if (s->clock_force_quantum != 0) {
- *def = *min = *max = s->clock_force_quantum;
- *rate = 0;
- } else {
- *def = s->clock_quantum;
- *min = s->clock_min_quantum;
- *max = s->clock_max_quantum;
- *rate = s->clock_rate;
- }
- *floor = s->clock_quantum_floor;
- *ceil = s->clock_quantum_limit;
-}
-
-static inline const uint32_t *get_rates(struct pw_context *context, uint32_t *def, uint32_t *n_rates,
- bool *force)
-{
- struct settings *s = &context->settings;
- if (s->clock_force_rate != 0) {
- *force = true;
- *n_rates = 1;
- *def = s->clock_force_rate;
- return &s->clock_force_rate;
- } else {
- *force = false;
- *n_rates = s->n_clock_rates;
- *def = s->clock_rate;
- return s->clock_rates;
- }
-}
-static void reconfigure_driver(struct pw_context *context, struct pw_impl_node *n)
-{
- struct pw_impl_node *s;
-
- spa_list_for_each(s, &n->follower_list, follower_link) {
- if (s == n)
- continue;
- pw_log_debug("%p: follower %p: '%s' suspend",
- context, s, s->name);
- pw_impl_node_set_state(s, PW_NODE_STATE_SUSPENDED);
- }
- pw_log_debug("%p: driver %p: '%s' suspend",
- context, n, n->name);
-
- if (n->info.state >= PW_NODE_STATE_IDLE)
- n->need_resume = !n->pause_on_idle;
- pw_impl_node_set_state(n, PW_NODE_STATE_SUSPENDED);
-}
-
-/* find smaller power of 2 */
-static uint32_t flp2(uint32_t x)
-{
- x = x | (x >> 1);
- x = x | (x >> 2);
- x = x | (x >> 4);
- x = x | (x >> 8);
- x = x | (x >> 16);
- return x - (x >> 1);
-}
-
-/* cmp fractions, avoiding overflows */
-static int fraction_compare(const struct spa_fraction *a, const struct spa_fraction *b)
-{
- uint64_t fa = (uint64_t)a->num * (uint64_t)b->denom;
- uint64_t fb = (uint64_t)b->num * (uint64_t)a->denom;
- return fa < fb ? -1 : (fa > fb ? 1 : 0);
-}
-
-static inline uint32_t calc_gcd(uint32_t a, uint32_t b)
-{
- while (b != 0) {
- uint32_t temp = a;
- a = b;
- b = temp % b;
- }
- return a;
-}
-
-struct rate_info {
- uint32_t rate;
- uint32_t gcd;
- uint32_t diff;
-};
-
-static inline void update_highest_rate(struct rate_info *best, struct rate_info *current)
-{
- /* find highest rate */
- if (best->rate == 0 || best->rate < current->rate)
- *best = *current;
-}
-
-static inline void update_nearest_gcd(struct rate_info *best, struct rate_info *current)
-{
- /* find nearest GCD */
- if (best->rate == 0 ||
- (best->gcd < current->gcd) ||
- (best->gcd == current->gcd && best->diff > current->diff))
- *best = *current;
-}
-static inline void update_nearest_rate(struct rate_info *best, struct rate_info *current)
-{
- /* find nearest rate */
- if (best->rate == 0 || best->diff > current->diff)
- *best = *current;
-}
-
-static uint32_t find_best_rate(const uint32_t *rates, uint32_t n_rates, uint32_t rate, uint32_t def)
-{
- uint32_t i, limit;
- struct rate_info best;
- struct rate_info info[n_rates];
-
- for (i = 0; i < n_rates; i++) {
- info[i].rate = rates[i];
- info[i].gcd = calc_gcd(rate, rates[i]);
- info[i].diff = SPA_ABS((int32_t)rate - (int32_t)rates[i]);
- }
-
- /* first find higher nearest GCD. This tries to find next bigest rate that
- * requires the least amount of resample filter banks. Usually these are
- * rates that are multiples of each other or multiples of a common rate.
- *
- * 44100 and [ 32000 56000 88200 96000 ] -> 88200
- * 48000 and [ 32000 56000 88200 96000 ] -> 96000
- * 88200 and [ 44100 48000 96000 192000 ] -> 96000
- * 32000 and [ 44100 192000 ] -> 44100
- * 8000 and [ 44100 48000 ] -> 48000
- * 8000 and [ 44100 192000 ] -> 44100
- * 11025 and [ 44100 48000 ] -> 44100
- * 44100 and [ 48000 176400 ] -> 48000
- * 144 and [ 44100 48000 88200 96000] -> 48000
- */
- spa_zero(best);
- /* Don't try to do excessive upsampling by limiting the max rate
- * for desired < default to default*2. For other rates allow
- * a x3 upsample rate max. For values lower than half of the default,
- * limit to the default. */
- limit = rate < def/2 ? def : rate < def ? def*2 : rate*3;
- for (i = 0; i < n_rates; i++) {
- if (info[i].rate >= rate && info[i].rate <= limit)
- update_nearest_gcd(&best, &info[i]);
- }
- if (best.rate != 0)
- return best.rate;
-
- /* we would need excessive upsampling, pick a nearest higher rate */
- spa_zero(best);
- for (i = 0; i < n_rates; i++) {
- if (info[i].rate >= rate)
- update_nearest_rate(&best, &info[i]);
- }
- if (best.rate != 0)
- return best.rate;
-
- /* There is nothing above the rate, we need to downsample. Try to downsample
- * but only to something that is from a common rate family. Also don't
- * try to downsample to something that will sound worse (< 44100).
- *
- * 88200 and [ 22050 44100 48000 ] -> 44100
- * 88200 and [ 22050 48000 ] -> 48000
- */
- spa_zero(best);
- for (i = 0; i < n_rates; i++) {
- if (info[i].rate >= 44100)
- update_nearest_gcd(&best, &info[i]);
- }
- if (best.rate != 0)
- return best.rate;
-
- /* There is nothing to downsample above our threshold. Downsample to whatever
- * is the highest rate then. */
- spa_zero(best);
- for (i = 0; i < n_rates; i++)
- update_highest_rate(&best, &info[i]);
- if (best.rate != 0)
- return best.rate;
-
- return def;
-}
-
-/* here we evaluate the complete state of the graph.
- *
- * It roughly operates in 4 stages:
- *
- * 1. go over all nodes and check if they should be scheduled (runnable) or not.
- *
- * 2. go over all drivers and collect the nodes that need to be scheduled with the
- * driver. This include all nodes that have an active link with the driver or
- * with a node already scheduled with the driver.
- *
- * 3. go over all nodes that are not assigned to a driver. The ones that require
- * a driver are moved to some random active driver found in step 2.
- *
- * 4. go over all drivers again, collect the quantum/rate of all followers, select
- * the desired final value and activate the followers and then the driver.
- *
- * A complete graph evaluation is performed for each change that is made to the
- * graph, such as making/destroying links, adding/removing nodes, property changes such
- * as quantum/rate changes or metadata changes.
- */
-static void context_recalc_graph(void *data)
-{
- struct impl *impl = data;
- struct pw_context *context = impl->context;
- struct settings *settings = &context->settings;
- struct pw_impl_node *n, *s, *target, *fallback;
- const uint32_t *rates;
- uint32_t max_quantum, min_quantum, def_quantum, rate_quantum, floor_quantum, ceil_quantum;
- uint32_t n_rates, def_rate, transport;
- bool freewheel, global_force_rate, global_force_quantum;
- struct spa_list collect;
-
-again:
- freewheel = false;
-
- /* clean up the flags first */
- spa_list_for_each(n, &context->node_list, link) {
- n->visited = false;
- n->checked = 0;
- n->runnable = false;
- }
-
- get_quantums(context, &def_quantum, &min_quantum, &max_quantum, &rate_quantum,
- &floor_quantum, &ceil_quantum);
- rates = get_rates(context, &def_rate, &n_rates, &global_force_rate);
-
- global_force_quantum = rate_quantum == 0;
-
- /* first look at all nodes and decide which one should be runnable */
- spa_list_for_each(n, &context->node_list, link) {
- if (n->exported || !n->active)
- continue;
- check_runnable(context, n);
- }
-
- /* start from all drivers and group all nodes that are linked
- * to it. Some nodes are not (yet) linked to anything and they
- * will end up 'unassigned' to a driver. Other nodes are drivers
- * and if they have active followers, we can use them to schedule
- * the unassigned nodes. */
- target = fallback = NULL;
- spa_list_for_each(n, &context->driver_list, driver_link) {
- if (n->exported)
- continue;
-
- if (!n->visited) {
- spa_list_init(&collect);
- collect_nodes(context, n, &collect);
- move_to_driver(context, &collect, n);
- }
- /* from now on we are only interested in active driving nodes
- * with a driver_priority. We're going to see if there are
- * active followers. */
- if (!n->driving || !n->active || n->priority_driver <= 0)
- continue;
-
- /* first active driving node is fallback */
- if (fallback == NULL)
- fallback = n;
-
- if (!n->runnable)
- continue;
-
- spa_list_for_each(s, &n->follower_list, follower_link) {
- pw_log_debug("%p: driver %p: follower %p %s: active:%d",
- context, n, s, s->name, s->active);
- if (s != n && s->active) {
- /* if the driving node has active followers, it
- * is a target for our unassigned nodes */
- if (target == NULL)
- target = n;
- if (n->freewheel)
- freewheel = true;
- break;
- }
- }
- }
- /* no active node, use fallback driving node */
- if (target == NULL)
- target = fallback;
-
- /* update the freewheel status */
- pw_context_set_freewheel(context, freewheel);
-
- /* now go through all available nodes. The ones we didn't visit
- * in collect_nodes() are not linked to any driver. We assign them
- * to either an active driver or the first driver if they are in a
- * group that needs a driver. Else we remove them from a driver
- * and stop them. */
- spa_list_for_each(n, &context->node_list, link) {
- struct pw_impl_node *t, *driver;
-
- if (n->exported || n->visited)
- continue;
-
- pw_log_debug("%p: unassigned node %p: '%s' active:%d want_driver:%d target:%p",
- context, n, n->name, n->active, n->want_driver, target);
-
- /* collect all nodes in this group */
- spa_list_init(&collect);
- collect_nodes(context, n, &collect);
-
- driver = NULL;
- spa_list_for_each(t, &collect, sort_link) {
- /* is any active and want a driver */
- if ((t->want_driver && t->active && t->runnable) ||
- t->always_process) {
- driver = target;
- break;
- }
- }
- if (driver != NULL) {
- driver->runnable = true;
- /* driver needed for this group */
- move_to_driver(context, &collect, driver);
- } else {
- /* no driver, make sure the nodes stop */
- remove_from_driver(context, &collect);
- }
- }
-
- /* assign final quantum and set state for followers and drivers */
- spa_list_for_each(n, &context->driver_list, driver_link) {
- bool running = false, lock_quantum = false, lock_rate = false;
- struct spa_fraction latency = SPA_FRACTION(0, 0);
- struct spa_fraction max_latency = SPA_FRACTION(0, 0);
- struct spa_fraction rate = SPA_FRACTION(0, 0);
- uint32_t target_quantum, target_rate, current_rate, current_quantum;
- uint64_t quantum_stamp = 0, rate_stamp = 0;
- bool force_rate, force_quantum, restore_rate = false, restore_quantum = false;
- bool do_reconfigure = false, need_resume, was_target_pending;
- bool have_request = false;
- const uint32_t *node_rates;
- uint32_t node_n_rates, node_def_rate;
- uint32_t node_max_quantum, node_min_quantum, node_def_quantum, node_rate_quantum;
-
- if (!n->driving || n->exported)
- continue;
-
- node_def_quantum = def_quantum;
- node_min_quantum = min_quantum;
- node_max_quantum = max_quantum;
- node_rate_quantum = rate_quantum;
- force_quantum = global_force_quantum;
-
- node_def_rate = def_rate;
- node_n_rates = n_rates;
- node_rates = rates;
- force_rate = global_force_rate;
-
- /* collect quantum and rate */
- spa_list_for_each(s, &n->follower_list, follower_link) {
-
- if (!s->moved) {
- /* We only try to enforce the lock flags for nodes that
- * are not recently moved between drivers. The nodes that
- * are moved should try to enforce their quantum on the
- * new driver. */
- lock_quantum |= s->lock_quantum;
- lock_rate |= s->lock_rate;
- }
- if (!global_force_quantum && s->force_quantum > 0 &&
- s->stamp > quantum_stamp) {
- node_def_quantum = node_min_quantum = node_max_quantum = s->force_quantum;
- node_rate_quantum = 0;
- quantum_stamp = s->stamp;
- force_quantum = true;
- }
- if (!global_force_rate && s->force_rate > 0 &&
- s->stamp > rate_stamp) {
- node_def_rate = s->force_rate;
- node_n_rates = 1;
- node_rates = &s->force_rate;
- force_rate = true;
- rate_stamp = s->stamp;
- }
-
- /* smallest latencies */
- if (latency.denom == 0 ||
- (s->latency.denom > 0 &&
- fraction_compare(&s->latency, &latency) < 0))
- latency = s->latency;
- if (max_latency.denom == 0 ||
- (s->max_latency.denom > 0 &&
- fraction_compare(&s->max_latency, &max_latency) < 0))
- max_latency = s->max_latency;
-
- /* largest rate, which is in fact the smallest fraction */
- if (rate.denom == 0 ||
- (s->rate.denom > 0 &&
- fraction_compare(&s->rate, &rate) < 0))
- rate = s->rate;
-
- if (s->active)
- running = n->runnable;
-
- pw_log_debug("%p: follower %p running:%d runnable:%d rate:%u/%u latency %u/%u '%s'",
- context, s, running, s->runnable, rate.num, rate.denom,
- latency.num, latency.denom, s->name);
-
- if (running && s != n && s->supports_request > 0)
- have_request = true;
-
- s->moved = false;
- }
-
- if (n->forced_rate && !force_rate && n->runnable) {
- /* A node that was forced to a rate but is no longer being
- * forced can restore its rate */
- pw_log_info("(%s-%u) restore rate", n->name, n->info.id);
- restore_rate = true;
- }
- if (n->forced_quantum && !force_quantum && n->runnable) {
- /* A node that was forced to a quantum but is no longer being
- * forced can restore its quantum */
- pw_log_info("(%s-%u) restore quantum", n->name, n->info.id);
- restore_quantum = true;
- }
-
- if (force_quantum)
- lock_quantum = false;
- if (force_rate)
- lock_rate = false;
-
- need_resume = n->need_resume;
- if (need_resume) {
- running = true;
- n->need_resume = false;
- }
-
- current_rate = n->target_rate.denom;
- if (!restore_rate &&
- (lock_rate || need_resume || !running ||
- (!force_rate && (n->info.state > PW_NODE_STATE_IDLE)))) {
- pw_log_debug("%p: keep rate:1/%u restore:%u lock:%u resume:%u "
- "running:%u force:%u state:%s", context,
- current_rate, restore_rate, lock_rate, need_resume,
- running, force_rate,
- pw_node_state_as_string(n->info.state));
-
- /* when we don't need to restore or rate and
- * when someone wants us to lock the rate of this driver or
- * when we are in the process of reconfiguring the driver or
- * when we are not running any followers or
- * when the driver is busy and we don't need to force a rate,
- * keep the current rate */
- target_rate = current_rate;
- }
- else {
- /* Here we are allowed to change the rate of the driver.
- * Start with the default rate. If the desired rate is
- * allowed, switch to it */
- if (rate.denom != 0 && rate.num == 1)
- target_rate = rate.denom;
- else
- target_rate = node_def_rate;
-
- target_rate = find_best_rate(node_rates, node_n_rates,
- target_rate, node_def_rate);
-
- pw_log_debug("%p: def_rate:%d target_rate:%d rate:%d/%d", context,
- node_def_rate, target_rate, rate.num, rate.denom);
- }
-
- was_target_pending = n->target_pending;
-
- if (target_rate != current_rate) {
- /* we doing a rate switch */
- pw_log_info("(%s-%u) state:%s new rate:%u/(%u)->%u",
- n->name, n->info.id,
- pw_node_state_as_string(n->info.state),
- n->target_rate.denom, current_rate,
- target_rate);
-
- if (force_rate) {
- if (settings->clock_rate_update_mode == CLOCK_RATE_UPDATE_MODE_HARD)
- do_reconfigure |= !was_target_pending;
- } else {
- if (n->info.state >= PW_NODE_STATE_SUSPENDED)
- do_reconfigure |= !was_target_pending;
- }
- /* we're setting the pending rate. This will become the new
- * current rate in the next iteration of the graph. */
- n->target_rate = SPA_FRACTION(1, target_rate);
- n->forced_rate = force_rate;
- n->target_pending = true;
- current_rate = target_rate;
- }
-
- if (node_rate_quantum != 0 && current_rate != node_rate_quantum) {
- /* the quantum values are scaled with the current rate */
- node_def_quantum = SPA_SCALE32(node_def_quantum, current_rate, node_rate_quantum);
- node_min_quantum = SPA_SCALE32(node_min_quantum, current_rate, node_rate_quantum);
- node_max_quantum = SPA_SCALE32(node_max_quantum, current_rate, node_rate_quantum);
- }
-
- /* calculate desired quantum. Don't limit to the max_latency when we are
- * going to force a quantum or rate and reconfigure the nodes. */
- if (max_latency.denom != 0 && !force_quantum && !force_rate) {
- uint32_t tmp = SPA_SCALE32(max_latency.num, current_rate, max_latency.denom);
- if (tmp < node_max_quantum)
- node_max_quantum = tmp;
- }
-
- current_quantum = n->target_quantum;
- if (!restore_quantum && (lock_quantum || need_resume || !running)) {
- pw_log_debug("%p: keep quantum:%u restore:%u lock:%u resume:%u "
- "running:%u force:%u state:%s", context,
- current_quantum, restore_quantum, lock_quantum, need_resume,
- running, force_quantum,
- pw_node_state_as_string(n->info.state));
- target_quantum = current_quantum;
- }
- else {
- target_quantum = node_def_quantum;
- if (latency.denom != 0)
- target_quantum = SPA_SCALE32(latency.num, current_rate, latency.denom);
- target_quantum = SPA_CLAMP(target_quantum, node_min_quantum, node_max_quantum);
- target_quantum = SPA_CLAMP(target_quantum, floor_quantum, ceil_quantum);
-
- if (settings->clock_power_of_two_quantum && !force_quantum)
- target_quantum = flp2(target_quantum);
- }
-
- if (target_quantum != current_quantum) {
- pw_log_info("(%s-%u) new quantum:%"PRIu64"->%u",
- n->name, n->info.id,
- n->target_quantum,
- target_quantum);
- /* this is the new pending quantum */
- n->target_quantum = target_quantum;
- n->forced_quantum = force_quantum;
- n->target_pending = true;
-
- if (force_quantum)
- do_reconfigure |= !was_target_pending;
- }
-
- if (n->target_pending) {
- if (do_reconfigure) {
- reconfigure_driver(context, n);
- /* we might be suspended now and the links need to be prepared again */
- goto again;
- }
- /* we have a pending change. We place the new values in the
- * pending fields so that they are picked up by the driver in
- * the next cycle */
- pw_log_debug("%p: apply duration:%"PRIu64" rate:%u/%u", context,
- n->target_quantum, n->target_rate.num,
- n->target_rate.denom);
- SPA_SEQ_WRITE(n->rt.position->clock.target_seq);
- n->rt.position->clock.target_duration = n->target_quantum;
- n->rt.position->clock.target_rate = n->target_rate;
- SPA_SEQ_WRITE(n->rt.position->clock.target_seq);
-
- if (n->info.state < PW_NODE_STATE_RUNNING) {
- n->rt.position->clock.duration = n->target_quantum;
- n->rt.position->clock.rate = n->target_rate;
- }
- n->target_pending = false;
- } else {
- n->target_quantum = n->rt.position->clock.target_duration;
- n->target_rate = n->rt.position->clock.target_rate;
- }
-
- if (n->info.state < PW_NODE_STATE_RUNNING)
- n->rt.position->clock.nsec = get_time_ns(n->rt.target.system);
-
- SPA_FLAG_UPDATE(n->rt.position->clock.flags,
- SPA_IO_CLOCK_FLAG_LAZY, have_request && n->supports_lazy > 0);
-
- pw_log_debug("%p: driver %p running:%d runnable:%d quantum:%u rate:%u (%"PRIu64"/%u)'%s'",
- context, n, running, n->runnable, target_quantum, target_rate,
- n->rt.position->clock.target_duration,
- n->rt.position->clock.target_rate.denom, n->name);
-
- transport = PW_NODE_ACTIVATION_COMMAND_NONE;
-
- /* first change the node states of the followers to the new target */
- spa_list_for_each(s, &n->follower_list, follower_link) {
- if (s->transport != PW_NODE_ACTIVATION_COMMAND_NONE) {
- transport = s->transport;
- s->transport = PW_NODE_ACTIVATION_COMMAND_NONE;
- }
- if (s == n)
- continue;
- pw_log_debug("%p: follower %p: active:%d '%s'",
- context, s, s->active, s->name);
- ensure_state(s, running, true);
- }
-
- if (transport != PW_NODE_ACTIVATION_COMMAND_NONE) {
- pw_log_info("%s: transport %d", n->name, transport);
- SPA_ATOMIC_STORE(n->rt.target.activation->command, transport);
- }
-
- /* now that all the followers are ready, start the driver */
- ensure_state(n, running, false);
- }
-}
-
-static const struct pw_context_events context_events = {
- PW_VERSION_CONTEXT_EVENTS,
- .recalc_graph = context_recalc_graph,
-};
-
-static void module_destroy(void *data)
-{
- struct impl *impl = data;
-
- if (impl->context) {
- spa_hook_remove(&impl->context_listener);
- spa_hook_remove(&impl->module_listener);
- }
-
- pw_properties_free(impl->props);
-
- free(impl);
-}
-
-static const struct pw_impl_module_events module_events = {
- PW_VERSION_IMPL_MODULE_EVENTS,
- .destroy = module_destroy,
-};
-
-SPA_EXPORT
-int pipewire__module_init(struct pw_impl_module *module, const char *args_str)
-{
- struct pw_context *context = pw_impl_module_get_context(module);
- struct pw_properties *args;
- struct impl *impl;
- int res;
-
- PW_LOG_TOPIC_INIT(mod_topic);
-
- impl = calloc(1, sizeof(struct impl));
- if (impl == NULL)
- return -errno;
-
- pw_log_debug("module %p: new %s", impl, args_str);
-
- if (args_str)
- args = pw_properties_new_string(args_str);
- else
- args = pw_properties_new(NULL, NULL);
-
- if (!args) {
- res = -errno;
- goto error;
- }
-
- pw_context_conf_update_props(context, "module."NAME".args", args);
-
- impl->props = args;
- impl->context = context;
-
- pw_context_add_listener(context, &impl->context_listener, &context_events, impl);
- pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
-
- pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
-
- return 0;
-
-error:
- module_destroy(impl);
- return res;
-}
diff --git a/src/modules/module-sendspin-recv.c b/src/modules/module-sendspin-recv.c
deleted file mode 100644
index fe6525767..000000000
--- a/src/modules/module-sendspin-recv.c
+++ /dev/null
@@ -1,1401 +0,0 @@
-/* PipeWire */
-/* SPDX-FileCopyrightText: Copyright © 2026 Wim Taymans */
-/* SPDX-License-Identifier: MIT */
-
-#include "config.h"
-
-#include
-#include
-#include