mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-02-23 01:40:24 -05:00
Compare commits
No commits in common. "master" and "1.5.85" have entirely different histories.
76 changed files with 1610 additions and 3240 deletions
|
|
@ -658,7 +658,7 @@ doccheck:
|
||||||
- cat pipewire_module_pages
|
- cat pipewire_module_pages
|
||||||
- |
|
- |
|
||||||
for page in $(cat pipewire_module_pages); do
|
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
|
done
|
||||||
|
|
||||||
check_missing_headers:
|
check_missing_headers:
|
||||||
|
|
|
||||||
102
NEWS
102
NEWS
|
|
@ -1,102 +1,3 @@
|
||||||
# PipeWire 1.6.0 (2026-02-19)
|
|
||||||
|
|
||||||
This is the 1.6 release that is API and ABI compatible with previous
|
|
||||||
1.4.x releases.
|
|
||||||
|
|
||||||
This release contains some of the bigger changes that happened since
|
|
||||||
the 1.4 release last year, including:
|
|
||||||
|
|
||||||
* An LDAC decoder was added for bluetooth.
|
|
||||||
* SpanDSP for bluetooth packet loss concealment.
|
|
||||||
* Safe parsing and building of PODs in shared memory.
|
|
||||||
* Added support for metadata features. This is used to signal that
|
|
||||||
the sync_timeline metadata supports the RELEASE operation.
|
|
||||||
* Node commands and events can contain extra user data.
|
|
||||||
* Support for more compressed format helper functions to create
|
|
||||||
and parse formats.
|
|
||||||
* Support for compile time max channels. The max channels was
|
|
||||||
increased to 128.
|
|
||||||
* Support for audio channel layouts was added. This makes it possible
|
|
||||||
to set "audio.layout" = "5.1" instead of the more verbose
|
|
||||||
audio.position = [ FL, FR, FC, LFE, SL, SR ]
|
|
||||||
* Support for Capability Params was added. This can be used to
|
|
||||||
negotiate capabilities on a link before format and buffer
|
|
||||||
negotiation takes place.
|
|
||||||
* More HDR colortypes are added.
|
|
||||||
* Loops now have locking with priority inversion. Most code was adapted
|
|
||||||
to use the faster locks instead of epoll/eventfd to update shared state.
|
|
||||||
* Channel position are parsed from EDID data.
|
|
||||||
* Channel maps are now set on ALSA.
|
|
||||||
* The resampler now supports configurable window functions such
|
|
||||||
as blackman and kaiser windows. The phases are now also calculated
|
|
||||||
with fixed point math, which makes it more accurate.
|
|
||||||
* Many bluetooth updates and improvements.
|
|
||||||
* The filter-graph has an ffmpeg and ONNX plugin. The ffmpeg plugin
|
|
||||||
can run an audio AVFilterGraph. The ONNX plugin can run some models
|
|
||||||
such as the silero VAD.
|
|
||||||
* Many AVB updates. Work is ongoing to merge the Milan protocol.
|
|
||||||
* Support for v0 clients was removed.
|
|
||||||
* The jack-tunnel module can now autoconnect ports.
|
|
||||||
* ROC support multitrack layouts now.
|
|
||||||
* Many RTP updates.
|
|
||||||
* rlimits can now be set in the config file.
|
|
||||||
* Thread reset on fork can now be configured. JACK clients expect this
|
|
||||||
to be disabled.
|
|
||||||
* node.exclusive is now enforced.
|
|
||||||
* node.reliable enables reliable transport.
|
|
||||||
* pw-cat supports sysex and midiclip as well as some more uncompressed
|
|
||||||
formats. Options were added to set the container and codec formats
|
|
||||||
as well as list the supported containers, codecs, layouts and channel
|
|
||||||
names.
|
|
||||||
* Documentation updates.
|
|
||||||
|
|
||||||
|
|
||||||
## Highlights (since the previous 1.5.85 prerelease)
|
|
||||||
- Fix a 64 channel limit in the channel mixer.
|
|
||||||
- Fix an fd leak in pulse-server in some error cases.
|
|
||||||
- Some small fixes and improvements.
|
|
||||||
|
|
||||||
|
|
||||||
## PipeWire
|
|
||||||
- Fix Capability leaks.
|
|
||||||
- Return an error in pw-stream get-time when not STREAMING.
|
|
||||||
- Set the current time in the driver position before starting.
|
|
||||||
Some followers might look at it.
|
|
||||||
|
|
||||||
## Modules
|
|
||||||
- Improve default channel handling in module-filter-chain.
|
|
||||||
- Support source and sink only module-filter-chain.
|
|
||||||
- Tweak the filter-chain spatializer example gains.
|
|
||||||
- Handle new snapcast service type. (#5104)
|
|
||||||
- Implement socket activation without depending on libsystemd.
|
|
||||||
- Support ipv4 link-local addresses in RAOP and snapcast. (#4830)
|
|
||||||
- Forward ROC-toolkit logs to pipewire.
|
|
||||||
|
|
||||||
## SPA
|
|
||||||
- Improve default channel handling in filter-graph. (#5084)
|
|
||||||
- Clamp control values to min/max. (#5088)
|
|
||||||
- Support mode JBL gaming headsets.
|
|
||||||
- Handle some SOFA errors and add gain option.
|
|
||||||
- Really handle more than 64 channels in the channelmixer. (#5118)
|
|
||||||
- Allow removal in ALSA-udev of ignored cards.
|
|
||||||
|
|
||||||
# pulse-server
|
|
||||||
- Fix mono mixdown query.
|
|
||||||
- Expose headset autoswitch message.
|
|
||||||
- Handle EPROTO errors by disconnecting.
|
|
||||||
- Handle timeouts in play-sample streams. (#5099)
|
|
||||||
|
|
||||||
## GStreamer
|
|
||||||
- Fix crop metadata.
|
|
||||||
- Fix a race in the buffer release function.
|
|
||||||
|
|
||||||
## Tools
|
|
||||||
- Improve format support and detection in pw-cat.
|
|
||||||
- Add some more options to pw-cat to list supported containers
|
|
||||||
and formats. (#5117)
|
|
||||||
|
|
||||||
Older versions:
|
|
||||||
|
|
||||||
# PipeWire 1.5.85 (2026-01-19)
|
# PipeWire 1.5.85 (2026-01-19)
|
||||||
|
|
||||||
This is the fifth and hopefully last 1.6 release candidate that
|
This is the fifth and hopefully last 1.6 release candidate that
|
||||||
|
|
@ -156,6 +57,9 @@ releases.
|
||||||
## Docs
|
## Docs
|
||||||
- Document the resampler properties better.
|
- Document the resampler properties better.
|
||||||
|
|
||||||
|
|
||||||
|
Older versions:
|
||||||
|
|
||||||
# PipeWire 1.5.84 (2025-11-27)
|
# PipeWire 1.5.84 (2025-11-27)
|
||||||
|
|
||||||
This is the fourth 1.6 release candidate that is API and ABI
|
This is the fourth 1.6 release candidate that is API and ABI
|
||||||
|
|
|
||||||
|
|
@ -1171,15 +1171,6 @@ in a platform-specific way. See `tests/examples/bt-pinephone.lua` in WirePlumber
|
||||||
Do not enable this setting if you don't know what all this means, as it won't work.
|
Do not enable this setting if you don't know what all this means, as it won't work.
|
||||||
\endparblock
|
\endparblock
|
||||||
|
|
||||||
@PAR@ monitor-prop bluez5.hw-offload-datapath # integer
|
|
||||||
\parblock
|
|
||||||
HFP/HSP hardware offload data path ID (default: 0).
|
|
||||||
|
|
||||||
This feature configures the SCO hardware‑offload data path for HFP/HSP using the Bluetooth
|
|
||||||
SIG–specified procedure. It is intended for advanced setups and vendor integrations. Do not
|
|
||||||
edit this unless required; incorrect values can disable SCO offload.
|
|
||||||
\endparblock
|
|
||||||
|
|
||||||
@PAR@ monitor-prop bluez5.a2dp.opus.pro.channels = 3 # integer
|
@PAR@ monitor-prop bluez5.a2dp.opus.pro.channels = 3 # integer
|
||||||
PipeWire Opus Pro audio profile channel count.
|
PipeWire Opus Pro audio profile channel count.
|
||||||
|
|
||||||
|
|
@ -1211,7 +1202,6 @@ PipeWire Opus Pro audio profile duplex max bitrate.
|
||||||
PipeWire Opus Pro audio profile duplex frame duration (1/10 ms).
|
PipeWire Opus Pro audio profile duplex frame duration (1/10 ms).
|
||||||
|
|
||||||
@PAR@ monitor-prop bluez5.bcast_source.config = [] # JSON
|
@PAR@ monitor-prop bluez5.bcast_source.config = [] # JSON
|
||||||
For a per-adapter configuration of multiple BIGs use an "adapter" entry in the BIG with the BD address.
|
|
||||||
\parblock
|
\parblock
|
||||||
Example:
|
Example:
|
||||||
```
|
```
|
||||||
|
|
@ -1373,12 +1363,6 @@ BAP QoS framing that needs to be applied for vendor defined preset
|
||||||
This property is experimental.
|
This property is experimental.
|
||||||
Default: as per QoS preset.
|
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-reliabilty QoS configuration table are used.
|
|
||||||
This property is experimental.
|
|
||||||
Available: low-latency, high-reliabilty, balanced
|
|
||||||
|
|
||||||
## Node properties
|
## Node properties
|
||||||
|
|
||||||
@PAR@ node-prop bluez5.media-source-role # string
|
@PAR@ node-prop bluez5.media-source-role # string
|
||||||
|
|
|
||||||
|
|
@ -313,13 +313,12 @@ performed.
|
||||||
Device ID negotiation needs explicit support by both end points of a stream, thus, the
|
Device ID negotiation needs explicit support by both end points of a stream, thus, the
|
||||||
first step of negotiation is discovering whether other peer has support for it. This is
|
first step of negotiation is discovering whether other peer has support for it. This is
|
||||||
done by advertising a \ref SPA_PARAM_Capability with the key \ref
|
done by advertising a \ref SPA_PARAM_Capability with the key \ref
|
||||||
PW_CAPABILITY_DEVICE_ID_NEGOTIATION and value `1` which corresponds to the
|
PW_CAPABILITY_DEVICE_ID_NEGOTIATION and value `true`
|
||||||
current negotiation API version.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
|
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
|
||||||
&SPA_DICT_ITEMS(
|
&SPA_DICT_ITEMS(
|
||||||
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1")));
|
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "true")));
|
||||||
```
|
```
|
||||||
|
|
||||||
To do this, when connecting to the stream, the \ref PW_STREAM_FLAG_INACTIVE flag must be
|
To do this, when connecting to the stream, the \ref PW_STREAM_FLAG_INACTIVE flag must be
|
||||||
|
|
@ -365,15 +364,12 @@ with. This can be used to reduce the amount of devices that are queried for form
|
||||||
metadata, which can be a time consuming task, if devices needs to be woken up.
|
metadata, which can be a time consuming task, if devices needs to be woken up.
|
||||||
|
|
||||||
To achieve this, the consumer adds another \ref SPA_PARAM_PeerCapability item with the key
|
To achieve this, the consumer adds another \ref SPA_PARAM_PeerCapability item with the key
|
||||||
\ref PW_CAPABILITY_DEVICE_IDS set to a JSON object describing what device IDs are supported.
|
\ref PW_CAPABILITY_DEVICE_IDS set to a string of base 64 encoded `dev_t` device IDs.
|
||||||
|
|
||||||
This JSON object as of version 1 contains a single key "available-devices" that contain
|
|
||||||
a list of hexadecimal encoded `dev_t` device IDs.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
char *device_ids = "{\"available-devices\": [\"6464000000000000\",\"c8c8000000000000\"]}";
|
char *device_ids = ...; /* Base 64 encoding of a dev_t. */.
|
||||||
&SPA_DICT_ITEMS(
|
&SPA_DICT_ITEMS(
|
||||||
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1"),
|
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "true"),
|
||||||
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_IDS, device_ids)));
|
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_IDS, device_ids)));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,8 @@ updated as follows:
|
||||||
|
|
||||||
- \ref spa_io_clock::nsec : Must be set to the time (according to the monotonic
|
- \ref spa_io_clock::nsec : Must be set to the time (according to the monotonic
|
||||||
system clock) when the cycle that the driver is about to trigger started. To
|
system clock) when the cycle that the driver is about to trigger started. To
|
||||||
minimize jitter, it is usually a good idea to increment this by a computed
|
minimize jitter, it is usually a good idea to increment this by a fixed amount
|
||||||
amount (instead of sampling a timestamp from the monotonic system clock)
|
|
||||||
except for when the driver starts and when discontinuities occur in its clock.
|
except for when the driver starts and when discontinuities occur in its clock.
|
||||||
The computed amount can be fixed, or varying over time, for example due to
|
|
||||||
adjustments made by a DLL (more on that below).
|
|
||||||
- \ref spa_io_clock::rate : Set to a value that can translate samples to nanoseconds.
|
- \ref spa_io_clock::rate : Set to a value that can translate samples to nanoseconds.
|
||||||
- \ref spa_io_clock::position : Current cycle position, in samples. This is the
|
- \ref spa_io_clock::position : Current cycle position, in samples. This is the
|
||||||
ideal position of the graph cycle (this is explained in greater detail further below).
|
ideal position of the graph cycle (this is explained in greater detail further below).
|
||||||
|
|
@ -55,7 +52,7 @@ updated as follows:
|
||||||
some cases, this may actually be in the past relative to nsec, for example, when
|
some cases, this may actually be in the past relative to nsec, for example, when
|
||||||
some internal driver clock experienced a discontinuity. Consider setting the
|
some internal driver clock experienced a discontinuity. Consider setting the
|
||||||
\ref SPA_IO_CLOCK_FLAG_DISCONT flag in such a case. Just like with nsec, to
|
\ref SPA_IO_CLOCK_FLAG_DISCONT flag in such a case. Just like with nsec, to
|
||||||
minimize jitter, it is usually a good idea to increment this by a computed amount
|
minimize jitter, it is usually a good idea to increment this by a fixed amount
|
||||||
except for when the driver starts and when discontinuities occur in its clock.
|
except for when the driver starts and when discontinuities occur in its clock.
|
||||||
|
|
||||||
The driver node signals the start of the graph cycle by calling \ref spa_node_call_ready
|
The driver node signals the start of the graph cycle by calling \ref spa_node_call_ready
|
||||||
|
|
@ -63,11 +60,6 @@ with the \ref SPA_STATUS_HAVE_DATA and \ref SPA_STATUS_NEED_DATA flags passed
|
||||||
to that function call. That call must happen inside the thread that runs the
|
to that function call. That call must happen inside the thread that runs the
|
||||||
data loop assigned to the driver node.
|
data loop assigned to the driver node.
|
||||||
|
|
||||||
Drivers must make sure that the next cycle is started at the time indicated by
|
|
||||||
the \ref spa_io_clock::next_nsec timestamp. They do not have to use the monotonic
|
|
||||||
clock itself for scheduling the next cycle. If for example the internal clock
|
|
||||||
can directly be used with \c timerfd , the next cycle can be triggered that way.
|
|
||||||
|
|
||||||
As mentioned above, the \ref spa_io_clock::position field is the _ideal_ position
|
As mentioned above, the \ref spa_io_clock::position field is the _ideal_ position
|
||||||
of the graph cycle, in samples. This contrasts with \ref spa_io_clock::nsec, which
|
of the graph cycle, in samples. This contrasts with \ref spa_io_clock::nsec, which
|
||||||
is the moment in monotonic clock time when the cycle _actually_ happens. This is an
|
is the moment in monotonic clock time when the cycle _actually_ happens. This is an
|
||||||
|
|
@ -111,12 +103,11 @@ expected position (in samples) and the actual position (derived from the current
|
||||||
of the driver's internal clock), passes the delta between these two quantities into the
|
of the driver's internal clock), passes the delta between these two quantities into the
|
||||||
DLL, and the DLL computes a correction factor (2.0 in the above example) which is used
|
DLL, and the DLL computes a correction factor (2.0 in the above example) which is used
|
||||||
for scaling durations between \c timerfd timeouts. This forms a control loop, since the
|
for scaling durations between \c timerfd timeouts. This forms a control loop, since the
|
||||||
correction factor causes the \ref spa_io_clock::next_nsec increments (that is, the
|
correction factor causes the durations between the timeouts to be adjusted such that the
|
||||||
durations between the timerfd timeouts) to be adjusted such that, over time, the difference
|
difference between the expected position and the actual position reaches zero. Keep in
|
||||||
between the expected position and the actual position reaches zero. Keep in mind the
|
mind the notes above about \ref spa_io_clock::position being the ideal position of the
|
||||||
notes above about \ref spa_io_clock::position being the ideal position of the graph
|
graph cycle, meaning that even in this case, the duration it is incremented by is
|
||||||
cycle, meaning that even in this case, the duration it is incremented by is _not_
|
_not_ scaled by the correction factor; the duration in samples remains unchanged.
|
||||||
scaled by the correction factor; the duration in samples remains unchanged.
|
|
||||||
|
|
||||||
(Other popular control loop mechanisms that are suitable alternatives to the DLL are
|
(Other popular control loop mechanisms that are suitable alternatives to the DLL are
|
||||||
PID controllers and Kalman filters.)
|
PID controllers and Kalman filters.)
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,6 @@ List of known modules:
|
||||||
- \subpage page_module_raop_discover
|
- \subpage page_module_raop_discover
|
||||||
- \subpage page_module_roc_sink
|
- \subpage page_module_roc_sink
|
||||||
- \subpage page_module_roc_source
|
- \subpage page_module_roc_source
|
||||||
- \subpage page_module_scheduler_v1
|
|
||||||
- \subpage page_module_rtp_sap
|
- \subpage page_module_rtp_sap
|
||||||
- \subpage page_module_rtp_sink
|
- \subpage page_module_rtp_sink
|
||||||
- \subpage page_module_rtp_source
|
- \subpage page_module_rtp_source
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ Play and record media with PipeWire
|
||||||
|
|
||||||
**pw-cat** is a simple tool for playing back or capturing raw or encoded
|
**pw-cat** is a simple tool for playing back or capturing raw or encoded
|
||||||
media files on a PipeWire server. It understands all audio file formats
|
media files on a PipeWire server. It understands all audio file formats
|
||||||
supported by `libsndfile` for PCM capture and playback. When no container
|
supported by `libsndfile` for PCM capture and playback. When capturing
|
||||||
is specified for capturing PCM, the filename extension is used to guess
|
PCM, the filename extension is used to guess the file format with the
|
||||||
the file format with the WAV file format as the default.
|
WAV file format as the default.
|
||||||
|
|
||||||
It understands standard MIDI files and MIDI 2.0 clip files for playback
|
It understands standard MIDI files and MIDI 2.0 clip files for playback
|
||||||
and recording. This tool will not render MIDI files, it will simply make
|
and recording. This tool will not render MIDI files, it will simply make
|
||||||
|
|
@ -37,15 +37,8 @@ DSD playback is supported with the DSF file format. This tool will only
|
||||||
work with native DSD capable hardware and will produce an error when no
|
work with native DSD capable hardware and will produce an error when no
|
||||||
such hardware was found.
|
such hardware was found.
|
||||||
|
|
||||||
When the *FILE* is - input will be from STDIN. If no format is specified,
|
When the *FILE* is - input and output will be raw data from STDIN and
|
||||||
libsndfile will attempt to parse and stream the format from STDIN. For
|
STDOUT respectively.
|
||||||
some formats, this is not possible and libsndfile will give an error.
|
|
||||||
Raw, MIDI and DSD formats are all streamable from STDIN.
|
|
||||||
|
|
||||||
When the *FILE* is - output will be to STDOUT. If no format is specified,
|
|
||||||
libsndfile is instructed to output the .au format, which is streamble and
|
|
||||||
preserves the format, rate and channels.
|
|
||||||
Raw and DSD formats are all streamable to STDOUT.
|
|
||||||
|
|
||||||
# OPTIONS
|
# OPTIONS
|
||||||
|
|
||||||
|
|
@ -94,11 +87,6 @@ DSD mode. *FILE* is a DSF file. If the tool is called under the name
|
||||||
render the DSD audio. You need a DSD capable device to play DSD content
|
render the DSD audio. You need a DSD capable device to play DSD content
|
||||||
or this program will exit with an error.
|
or this program will exit with an error.
|
||||||
|
|
||||||
\par -s | \--sysex
|
|
||||||
SysEx mode. *FILE* is a File that contains a raw SysEx MIDI message.
|
|
||||||
If the tool is called under the name **pw-sysex** this is the default.
|
|
||||||
The File is read and sent as a MIDI control message into the graph.
|
|
||||||
|
|
||||||
\par \--media-type=VALUE
|
\par \--media-type=VALUE
|
||||||
Set the media type property (default Audio/Midi depending on mode). The
|
Set the media type property (default Audio/Midi depending on mode). The
|
||||||
media type is used by the session manager to select a suitable target to
|
media type is used by the session manager to select a suitable target to
|
||||||
|
|
@ -150,17 +138,6 @@ does not match the samplerate of the server, the data will be resampled.
|
||||||
Higher quality uses more CPU. Values between 0 and 15 are allowed, the
|
Higher quality uses more CPU. Values between 0 and 15 are allowed, the
|
||||||
default quality is 4.
|
default quality is 4.
|
||||||
|
|
||||||
\par -a | \--raw
|
|
||||||
Raw samples will be read or written. The \--rate, \--format, \--channels
|
|
||||||
and \--channelmap can be used to specify the raw format.
|
|
||||||
|
|
||||||
\par -M | \--force-midi
|
|
||||||
Force midi format, one of "midi" or "ump", (default ump).
|
|
||||||
When reading or writing midi, for one of midi or UMP.
|
|
||||||
|
|
||||||
\par -n | \--sample-count=COUNT
|
|
||||||
Stop after COUNT samples.
|
|
||||||
|
|
||||||
\par \--rate=VALUE
|
\par \--rate=VALUE
|
||||||
The sample rate, default 48000.
|
The sample rate, default 48000.
|
||||||
|
|
||||||
|
|
@ -168,38 +145,19 @@ The sample rate, default 48000.
|
||||||
The number of channels, default 2.
|
The number of channels, default 2.
|
||||||
|
|
||||||
\par \--channel-map=VALUE
|
\par \--channel-map=VALUE
|
||||||
The channelmap. Possible values include are either a channel layout
|
The channelmap. Possible values include: **mono**, **stereo**,
|
||||||
such as **mono**, **stereo**,
|
|
||||||
**surround-21**, **quad**, **surround-22**, **surround-40**,
|
**surround-21**, **quad**, **surround-22**, **surround-40**,
|
||||||
or comma separated array of channel names such as **FL,FR**.
|
**surround-31**, **surround-41**, **surround-50**, **surround-51**,
|
||||||
See \--list-layouts and \--list-channel-names to get a complete
|
**surround-51r**, **surround-70**, **surround-71** or a comma separated
|
||||||
list of possible values.
|
list of channel names: **FL**, **FR**, **FC**, **LFE**, **SL**, **SR**,
|
||||||
|
**FLC**, **FRC**, **RC**, **RL**, **RR**, **TC**, **TFL**, **TFC**,
|
||||||
\par \--list-layouts
|
**TFR**, **TRL**, **TRC**, **TRR**, **RLC**, **RRC**, **FLW**, **FRW**,
|
||||||
List all known channel layouts. One of these can be used as the
|
**LFE2**, **FLH**, **FCH**, **FRH**, **TFLC**, **TFRC**, **TSL**,
|
||||||
\--channel-map value.
|
**TSR**, **LLFR**, **RLFE**, **BC**, **BLC**, **BRC**
|
||||||
|
|
||||||
\par \--list-channel-names
|
|
||||||
List all known channel names. An array of these can be used as the
|
|
||||||
\--channel-map value.
|
|
||||||
|
|
||||||
\par \--format=VALUE
|
\par \--format=VALUE
|
||||||
The sample format to use. Some possible values include: **u8**, **s8**,
|
The sample format to use. One of: **u8**, **s8**, **s16** (default),
|
||||||
**s16** (default), **s24**, **s32**, **f32**, **f64**. See
|
**s24**, **s32**, **f32**, **f64**.
|
||||||
\--list-formats to get a complete list of values.
|
|
||||||
|
|
||||||
\par \--list-formats
|
|
||||||
List all known format values.
|
|
||||||
|
|
||||||
\par \--container=VALUE
|
|
||||||
Specify the container to use when saving. This is usually guessed from
|
|
||||||
the filename extension but can be specified explicitly. When using
|
|
||||||
STDOUT and no container is specified, the AU container will be used.
|
|
||||||
Then using a filename and the container was not specified and it could
|
|
||||||
not be derived from the filename, the WAV container is used.
|
|
||||||
|
|
||||||
\par \--list-containers
|
|
||||||
List all known container values.
|
|
||||||
|
|
||||||
\par \--volume=VALUE
|
\par \--volume=VALUE
|
||||||
The stream volume, default 1.000. Depending on the locale you have
|
The stream volume, default 1.000. Depending on the locale you have
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ node and device statistics.
|
||||||
|
|
||||||
A hierarchical view is shown of Driver nodes and follower nodes. The
|
A hierarchical view is shown of Driver nodes and follower nodes. The
|
||||||
Driver nodes are actively using a timer to schedule dataflow in the
|
Driver nodes are actively using a timer to schedule dataflow in the
|
||||||
followers. The followers of a driver node are shown below their driver
|
followers. The followers of a driver node as shown below their driver
|
||||||
with a + sign (or = for async nodes) in a tree-like representation.
|
with a + sign (or = for async nodes) in a tree-like representation.
|
||||||
|
|
||||||
The columns presented are as follows:
|
The columns presented are as follows:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
project('pipewire', ['c' ],
|
project('pipewire', ['c' ],
|
||||||
version : '1.7.0',
|
version : '1.5.85',
|
||||||
license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
|
license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
|
||||||
meson_version : '>= 0.61.1',
|
meson_version : '>= 0.61.1',
|
||||||
default_options : [ 'warning_level=3',
|
default_options : [ 'warning_level=3',
|
||||||
|
|
@ -82,7 +82,6 @@ common_flags = [
|
||||||
'-fvisibility=hidden',
|
'-fvisibility=hidden',
|
||||||
'-fno-strict-aliasing',
|
'-fno-strict-aliasing',
|
||||||
'-fno-strict-overflow',
|
'-fno-strict-overflow',
|
||||||
'-DSPA_AUDIO_MAX_CHANNELS=128u',
|
|
||||||
'-Werror=suggest-attribute=format',
|
'-Werror=suggest-attribute=format',
|
||||||
'-Wsign-compare',
|
'-Wsign-compare',
|
||||||
'-Wpointer-arith',
|
'-Wpointer-arith',
|
||||||
|
|
@ -116,7 +115,7 @@ cc_flags = common_flags + [
|
||||||
'-Werror=old-style-definition',
|
'-Werror=old-style-definition',
|
||||||
'-Werror=missing-parameter-type',
|
'-Werror=missing-parameter-type',
|
||||||
'-Werror=strict-prototypes',
|
'-Werror=strict-prototypes',
|
||||||
'-Werror=discarded-qualifiers',
|
'-DSPA_AUDIO_MAX_CHANNELS=128u',
|
||||||
]
|
]
|
||||||
add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c')
|
add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c')
|
||||||
add_project_arguments(cc_native.get_supported_arguments(cc_flags),
|
add_project_arguments(cc_native.get_supported_arguments(cc_flags),
|
||||||
|
|
|
||||||
|
|
@ -5318,7 +5318,7 @@ int jack_set_freewheel(jack_client_t* client, int onoff)
|
||||||
pw_thread_loop_lock(c->context.loop);
|
pw_thread_loop_lock(c->context.loop);
|
||||||
str = pw_properties_get(c->props, PW_KEY_NODE_GROUP);
|
str = pw_properties_get(c->props, PW_KEY_NODE_GROUP);
|
||||||
if (str != NULL) {
|
if (str != NULL) {
|
||||||
const char *p = strstr(str, ",pipewire.freewheel");
|
char *p = strstr(str, ",pipewire.freewheel");
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
p = strstr(str, "pipewire.freewheel");
|
p = strstr(str, "pipewire.freewheel");
|
||||||
if (p == NULL && onoff)
|
if (p == NULL && onoff)
|
||||||
|
|
|
||||||
155
po/sl.po
155
po/sl.po
|
|
@ -2,21 +2,23 @@
|
||||||
# Copyright (C) 2024 PipeWire's COPYRIGHT HOLDER
|
# Copyright (C) 2024 PipeWire's COPYRIGHT HOLDER
|
||||||
# This file is distributed under the same license as the PipeWire package.
|
# This file is distributed under the same license as the PipeWire package.
|
||||||
#
|
#
|
||||||
# Martin <miles@filmsi.net>, 2024, 2025, 2026.
|
# Martin <miles@filmsi.net>, 2024, 2025.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PipeWire master\n"
|
"Project-Id-Version: PipeWire master\n"
|
||||||
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues\n"
|
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
|
||||||
"POT-Creation-Date: 2026-02-09 12:55+0000\n"
|
"issues\n"
|
||||||
"PO-Revision-Date: 2026-02-15 16:18+0100\n"
|
"POT-Creation-Date: 2025-12-04 15:34+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-12-07 08:53+0100\n"
|
||||||
"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
|
"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
|
||||||
"Language-Team: Slovenian <gnome-si@googlegroups.com>\n"
|
"Language-Team: Slovenian <gnome-si@googlegroups.com>\n"
|
||||||
"Language: sl\n"
|
"Language: sl\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n%100<=4 ? 2 : 3);\n"
|
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n"
|
||||||
|
"%100<=4 ? 2 : 3);\n"
|
||||||
"X-Generator: Poedit 2.2.1\n"
|
"X-Generator: Poedit 2.2.1\n"
|
||||||
|
|
||||||
#: src/daemon/pipewire.c:29
|
#: src/daemon/pipewire.c:29
|
||||||
|
|
@ -33,7 +35,8 @@ msgstr ""
|
||||||
" -h, --help Pokaži to pomoč\n"
|
" -h, --help Pokaži to pomoč\n"
|
||||||
" -v, --verbose Povečaj opisnost za eno raven\n"
|
" -v, --verbose Povečaj opisnost za eno raven\n"
|
||||||
" --version Pokaži različico\n"
|
" --version Pokaži različico\n"
|
||||||
" -c, --config Naloži prilagoditev config (privzeto %s)\n"
|
" -c, --config Naloži prilagoditev config (privzeto "
|
||||||
|
"%s)\n"
|
||||||
" -P --properties Določi lastnosti konteksta\n"
|
" -P --properties Določi lastnosti konteksta\n"
|
||||||
|
|
||||||
#: src/daemon/pipewire.desktop.in:3
|
#: src/daemon/pipewire.desktop.in:3
|
||||||
|
|
@ -59,46 +62,21 @@ msgstr "Lažni izhod"
|
||||||
msgid "Tunnel for %s@%s"
|
msgid "Tunnel for %s@%s"
|
||||||
msgstr "Prehod za %s@%s"
|
msgstr "Prehod za %s@%s"
|
||||||
|
|
||||||
#: src/modules/module-zeroconf-discover.c:326
|
#: src/modules/module-zeroconf-discover.c:320
|
||||||
msgid "Unknown device"
|
msgid "Unknown device"
|
||||||
msgstr "Neznana naprava"
|
msgstr "Neznana naprava"
|
||||||
|
|
||||||
#: src/modules/module-zeroconf-discover.c:338
|
#: src/modules/module-zeroconf-discover.c:332
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s on %s@%s"
|
msgid "%s on %s@%s"
|
||||||
msgstr "%s na %s@%s"
|
msgstr "%s na %s@%s"
|
||||||
|
|
||||||
#: src/modules/module-zeroconf-discover.c:342
|
#: src/modules/module-zeroconf-discover.c:336
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s on %s"
|
msgid "%s on %s"
|
||||||
msgstr "%s na %s"
|
msgstr "%s na %s"
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:264
|
#: src/tools/pw-cat.c:1103
|
||||||
#, c-format
|
|
||||||
msgid "Supported formats:\n"
|
|
||||||
msgstr "Podprti zapisi:\n"
|
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:749
|
|
||||||
#, c-format
|
|
||||||
msgid "Supported channel layouts:\n"
|
|
||||||
msgstr "Podprte postavitve kanalov:\n"
|
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:759
|
|
||||||
#, c-format
|
|
||||||
msgid "Supported channel layout aliases:\n"
|
|
||||||
msgstr "Podprti vzdevki postavitev kanalov:\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 "Podprta imena kanalov:\n"
|
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:1177
|
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"%s [options] [<file>|-]\n"
|
"%s [options] [<file>|-]\n"
|
||||||
|
|
@ -114,7 +92,7 @@ msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"</file>\n"
|
"</file>\n"
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:1184
|
#: src/tools/pw-cat.c:1110
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
" -R, --remote Remote daemon name\n"
|
" -R, --remote Remote daemon name\n"
|
||||||
|
|
@ -151,23 +129,20 @@ msgstr ""
|
||||||
" -P --properties Nastavi lastnosti vozlišča\n"
|
" -P --properties Nastavi lastnosti vozlišča\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:1202
|
#: src/tools/pw-cat.c:1128
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
" --rate Sample rate (default %u)\n"
|
" --rate Sample rate (req. for rec) (default "
|
||||||
" --channels Number of channels (default %u)\n"
|
"%u)\n"
|
||||||
|
" --channels Number of channels (req. for rec) "
|
||||||
|
"(default %u)\n"
|
||||||
" --channel-map Channel map\n"
|
" --channel-map Channel map\n"
|
||||||
" a channel layout: \"Stereo\", "
|
" one of: \"Stereo\", \"5.1\",... "
|
||||||
"\"5.1\",... or\n"
|
"or\n"
|
||||||
" comma separated list of channel "
|
" comma separated list of channel "
|
||||||
"names: eg. \"FL,FR\"\n"
|
"names: eg. \"FL,FR\"\n"
|
||||||
" --list-layouts List supported channel layouts\n"
|
" --format Sample format %s (req. for rec) "
|
||||||
" --list-channel-names List supported channel maps\n"
|
"(default %s)\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"
|
|
||||||
" --volume Stream volume 0-1.0 (default %.3f)\n"
|
" --volume Stream volume 0-1.0 (default %.3f)\n"
|
||||||
" -q --quality Resampler quality (0 - 15) (default "
|
" -q --quality Resampler quality (0 - 15) (default "
|
||||||
"%d)\n"
|
"%d)\n"
|
||||||
|
|
@ -186,13 +161,8 @@ msgstr ""
|
||||||
"\"5.1\",... ali\n"
|
"\"5.1\",... ali\n"
|
||||||
" seznam imen kanalov, ločen z "
|
" seznam imen kanalov, ločen z "
|
||||||
"vejico: npr. \"FL,FR\"\n"
|
"vejico: npr. \"FL,FR\"\n"
|
||||||
" —list-layouts Izpiše podprte postavitve kanalov\n"
|
" --format Vzorčne oblike zapisa %s (zahtevano "
|
||||||
" —list-channel-names Izpiše podprte preslikave kanalov\n"
|
"za rec) (privzeto %s)\n"
|
||||||
" --format Oblika zapisa vzorcev (privzeto %s)\n"
|
|
||||||
" —list-formats Izpiše podprte zapise vzorcev\n"
|
|
||||||
" —container Oblika vsebnika\n"
|
|
||||||
" —list-containers Seznam podprtih vsebnikov in "
|
|
||||||
"razširitev\n"
|
|
||||||
" --volume Glasnost toka 0-1.0 (privzeto %.3f)\n"
|
" --volume Glasnost toka 0-1.0 (privzeto %.3f)\n"
|
||||||
" -q --quality Kakovost prevzorčenja (0 - 15) "
|
" -q --quality Kakovost prevzorčenja (0 - 15) "
|
||||||
"(privzeto %d)\n"
|
"(privzeto %d)\n"
|
||||||
|
|
@ -202,7 +172,7 @@ msgstr ""
|
||||||
" -n, --sample-count ŠTEVEC Ustavi po ŠTEVEC vzorcih\n"
|
" -n, --sample-count ŠTEVEC Ustavi po ŠTEVEC vzorcih\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:1227
|
#: src/tools/pw-cat.c:1148
|
||||||
msgid ""
|
msgid ""
|
||||||
" -p, --playback Playback mode\n"
|
" -p, --playback Playback mode\n"
|
||||||
" -r, --record Recording mode\n"
|
" -r, --record Recording mode\n"
|
||||||
|
|
@ -222,11 +192,6 @@ msgstr ""
|
||||||
" -c, --midi-clip Način posnetka MIDI\n"
|
" -c, --midi-clip Način posnetka MIDI\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:1827
|
|
||||||
#, c-format
|
|
||||||
msgid "Supported containers and extensions:\n"
|
|
||||||
msgstr "Podprti vsebniki in razširitve:\n"
|
|
||||||
|
|
||||||
#: src/tools/pw-cli.c:2386
|
#: src/tools/pw-cli.c:2386
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
|
|
@ -252,7 +217,7 @@ msgid "Pro Audio"
|
||||||
msgstr "Profesionalni zvok"
|
msgstr "Profesionalni zvok"
|
||||||
|
|
||||||
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
|
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2021
|
#: spa/plugins/bluez5/bluez5-device.c:1976
|
||||||
msgid "Off"
|
msgid "Off"
|
||||||
msgstr "Izklopljeno"
|
msgstr "Izklopljeno"
|
||||||
|
|
||||||
|
|
@ -284,7 +249,7 @@ msgstr "Linijski vhod"
|
||||||
|
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
|
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
|
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2422
|
#: spa/plugins/bluez5/bluez5-device.c:2374
|
||||||
msgid "Microphone"
|
msgid "Microphone"
|
||||||
msgstr "Mikrofon"
|
msgstr "Mikrofon"
|
||||||
|
|
||||||
|
|
@ -350,15 +315,15 @@ msgid "No Bass Boost"
|
||||||
msgstr "Brez ojačitve nizkih tonov"
|
msgstr "Brez ojačitve nizkih tonov"
|
||||||
|
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
|
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2428
|
#: spa/plugins/bluez5/bluez5-device.c:2380
|
||||||
msgid "Speaker"
|
msgid "Speaker"
|
||||||
msgstr "Zvočnik"
|
msgstr "Zvočnik"
|
||||||
|
|
||||||
#. Don't call it "headset", the HF one has the mic
|
#. 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:2742
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
|
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2434
|
#: spa/plugins/bluez5/bluez5-device.c:2386
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2501
|
#: spa/plugins/bluez5/bluez5-device.c:2453
|
||||||
msgid "Headphones"
|
msgid "Headphones"
|
||||||
msgstr "Slušalke"
|
msgstr "Slušalke"
|
||||||
|
|
||||||
|
|
@ -468,7 +433,7 @@ msgstr "Stereo"
|
||||||
|
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
|
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
|
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2410
|
#: spa/plugins/bluez5/bluez5-device.c:2362
|
||||||
msgid "Headset"
|
msgid "Headset"
|
||||||
msgstr "Slušalka"
|
msgstr "Slušalka"
|
||||||
|
|
||||||
|
|
@ -715,100 +680,100 @@ msgstr "Vgrajen zvok"
|
||||||
msgid "Modem"
|
msgid "Modem"
|
||||||
msgstr "Modem"
|
msgstr "Modem"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2032
|
#: spa/plugins/bluez5/bluez5-device.c:1987
|
||||||
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
|
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
|
||||||
msgstr "Zvožni prehod (vir A2DP in HSP/HFP AG)"
|
msgstr "Zvožni prehod (vir A2DP in HSP/HFP AG)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2061
|
#: spa/plugins/bluez5/bluez5-device.c:2016
|
||||||
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
|
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
|
||||||
msgstr "Pretakanje zvoka za slušne aparate (ponor ASHA)"
|
msgstr "Pretakanje zvoka za slušne aparate (ponor ASHA)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2104
|
#: spa/plugins/bluez5/bluez5-device.c:2059
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
|
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
|
||||||
msgstr "Predvajanje visoke ločljivosti (ponor A2DP, kodek %s)"
|
msgstr "Predvajanje visoke ločljivosti (ponor A2DP, kodek %s)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2107
|
#: spa/plugins/bluez5/bluez5-device.c:2062
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
|
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
|
||||||
msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP, kodek %s)"
|
msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP, kodek %s)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2115
|
#: spa/plugins/bluez5/bluez5-device.c:2070
|
||||||
msgid "High Fidelity Playback (A2DP Sink)"
|
msgid "High Fidelity Playback (A2DP Sink)"
|
||||||
msgstr "Predvajanje visoke ločljivosti (ponor A2DP)"
|
msgstr "Predvajanje visoke ločljivosti (ponor A2DP)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2117
|
#: spa/plugins/bluez5/bluez5-device.c:2072
|
||||||
msgid "High Fidelity Duplex (A2DP Source/Sink)"
|
msgid "High Fidelity Duplex (A2DP Source/Sink)"
|
||||||
msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP)"
|
msgstr "Dupleks visoke ločljivosti (vir/ponor A2DP)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2194
|
#: spa/plugins/bluez5/bluez5-device.c:2146
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "High Fidelity Playback (BAP Sink, codec %s)"
|
msgid "High Fidelity Playback (BAP Sink, codec %s)"
|
||||||
msgstr "Predvajanje visoke ločljivosti (ponor BAP, kodek %s)"
|
msgstr "Predvajanje visoke ločljivosti (ponor BAP, kodek %s)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2199
|
#: spa/plugins/bluez5/bluez5-device.c:2151
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "High Fidelity Input (BAP Source, codec %s)"
|
msgid "High Fidelity Input (BAP Source, codec %s)"
|
||||||
msgstr "Vhod visoke ločljivosti (vir BAP, kodek %s)"
|
msgstr "Vhod visoke ločljivosti (vir BAP, kodek %s)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2203
|
#: spa/plugins/bluez5/bluez5-device.c:2155
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
|
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
|
||||||
msgstr "Dupleks visoke ločljivosti (vir/ponor BAP, kodek %s)"
|
msgstr "Dupleks visoke ločljivosti (vir/ponor BAP, kodek %s)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2212
|
#: spa/plugins/bluez5/bluez5-device.c:2164
|
||||||
msgid "High Fidelity Playback (BAP Sink)"
|
msgid "High Fidelity Playback (BAP Sink)"
|
||||||
msgstr "Predvajanje visoke ločljivosti (ponor BAP)"
|
msgstr "Predvajanje visoke ločljivosti (ponor BAP)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2216
|
#: spa/plugins/bluez5/bluez5-device.c:2168
|
||||||
msgid "High Fidelity Input (BAP Source)"
|
msgid "High Fidelity Input (BAP Source)"
|
||||||
msgstr "Vhod visoke ločljivosti (vir BAP)"
|
msgstr "Vhod visoke ločljivosti (vir BAP)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2219
|
#: spa/plugins/bluez5/bluez5-device.c:2171
|
||||||
msgid "High Fidelity Duplex (BAP Source/Sink)"
|
msgid "High Fidelity Duplex (BAP Source/Sink)"
|
||||||
msgstr "Dupleks visoke ločljivosti (vir/ponor BAP)"
|
msgstr "Dupleks visoke ločljivosti (vir/ponor BAP)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2259
|
#: spa/plugins/bluez5/bluez5-device.c:2211
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Headset Head Unit (HSP/HFP, codec %s)"
|
msgid "Headset Head Unit (HSP/HFP, codec %s)"
|
||||||
msgstr "Naglavna enota slušalk (HSP/HFP, kodek %s)"
|
msgstr "Naglavna enota slušalk (HSP/HFP, kodek %s)"
|
||||||
|
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2363
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2368
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2375
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2381
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2387
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2393
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2399
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2405
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2411
|
#: 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"
|
msgid "Handsfree"
|
||||||
msgstr "Prostoročno telefoniranje"
|
msgstr "Prostoročno telefoniranje"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2417
|
#: spa/plugins/bluez5/bluez5-device.c:2369
|
||||||
msgid "Handsfree (HFP)"
|
msgid "Handsfree (HFP)"
|
||||||
msgstr "Prostoročno telefoniranje (HFP)"
|
msgstr "Prostoročno telefoniranje (HFP)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2440
|
#: spa/plugins/bluez5/bluez5-device.c:2392
|
||||||
msgid "Portable"
|
msgid "Portable"
|
||||||
msgstr "Prenosna naprava"
|
msgstr "Prenosna naprava"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2446
|
#: spa/plugins/bluez5/bluez5-device.c:2398
|
||||||
msgid "Car"
|
msgid "Car"
|
||||||
msgstr "Avtomobil"
|
msgstr "Avtomobil"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2452
|
#: spa/plugins/bluez5/bluez5-device.c:2404
|
||||||
msgid "HiFi"
|
msgid "HiFi"
|
||||||
msgstr "HiFi"
|
msgstr "HiFi"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2458
|
#: spa/plugins/bluez5/bluez5-device.c:2410
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Telefon"
|
msgstr "Telefon"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2465
|
#: spa/plugins/bluez5/bluez5-device.c:2417
|
||||||
msgid "Bluetooth"
|
msgid "Bluetooth"
|
||||||
msgstr "Bluetooth"
|
msgstr "Bluetooth"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2466
|
#: spa/plugins/bluez5/bluez5-device.c:2418
|
||||||
msgid "Bluetooth Handsfree"
|
msgid "Bluetooth Handsfree"
|
||||||
msgstr "Bluetooth - prostoročno"
|
msgstr "Bluetooth - prostoročno"
|
||||||
|
|
|
||||||
157
po/zh_CN.po
157
po/zh_CN.po
|
|
@ -6,15 +6,15 @@
|
||||||
# Cheng-Chia Tseng <pswo10680@gmail.com>, 2010, 2012.
|
# Cheng-Chia Tseng <pswo10680@gmail.com>, 2010, 2012.
|
||||||
# Frank Hill <hxf.prc@gmail.com>, 2015.
|
# Frank Hill <hxf.prc@gmail.com>, 2015.
|
||||||
# Mingye Wang (Arthur2e5) <arthur200126@gmail.com>, 2015.
|
# Mingye Wang (Arthur2e5) <arthur200126@gmail.com>, 2015.
|
||||||
# lumingzh <lumingzh@qq.com>, 2024-2026.
|
# lumingzh <lumingzh@qq.com>, 2024-2025.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: pipewire.master-tx\n"
|
"Project-Id-Version: pipewire.master-tx\n"
|
||||||
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
|
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
|
||||||
"issues\n"
|
"issues\n"
|
||||||
"POT-Creation-Date: 2026-02-11 16:53+0000\n"
|
"POT-Creation-Date: 2025-11-25 15:35+0000\n"
|
||||||
"PO-Revision-Date: 2026-02-13 09:36+0800\n"
|
"PO-Revision-Date: 2025-11-26 10:19+0800\n"
|
||||||
"Last-Translator: lumingzh <lumingzh@qq.com>\n"
|
"Last-Translator: lumingzh <lumingzh@qq.com>\n"
|
||||||
"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
|
"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
|
||||||
"Language: zh_CN\n"
|
"Language: zh_CN\n"
|
||||||
|
|
@ -65,46 +65,21 @@ msgstr "虚拟输出"
|
||||||
msgid "Tunnel for %s@%s"
|
msgid "Tunnel for %s@%s"
|
||||||
msgstr "用于 %s@%s 的隧道"
|
msgstr "用于 %s@%s 的隧道"
|
||||||
|
|
||||||
#: src/modules/module-zeroconf-discover.c:326
|
#: src/modules/module-zeroconf-discover.c:320
|
||||||
msgid "Unknown device"
|
msgid "Unknown device"
|
||||||
msgstr "未知设备"
|
msgstr "未知设备"
|
||||||
|
|
||||||
#: src/modules/module-zeroconf-discover.c:338
|
#: src/modules/module-zeroconf-discover.c:332
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s on %s@%s"
|
msgid "%s on %s@%s"
|
||||||
msgstr "%2$s@%3$s 上的 %1$s"
|
msgstr "%2$s@%3$s 上的 %1$s"
|
||||||
|
|
||||||
#: src/modules/module-zeroconf-discover.c:342
|
#: src/modules/module-zeroconf-discover.c:336
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s on %s"
|
msgid "%s on %s"
|
||||||
msgstr "%2$s 上的 %1$s"
|
msgstr "%2$s 上的 %1$s"
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:264
|
#: src/tools/pw-cat.c:1088
|
||||||
#, c-format
|
|
||||||
msgid "Supported formats:\n"
|
|
||||||
msgstr "支持的格式:\n"
|
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:749
|
|
||||||
#, c-format
|
|
||||||
msgid "Supported channel layouts:\n"
|
|
||||||
msgstr "支持的声道布局:\n"
|
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:759
|
|
||||||
#, c-format
|
|
||||||
msgid "Supported channel layout aliases:\n"
|
|
||||||
msgstr "支持的声道布局别名:\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 "支持的声道名称:\n"
|
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:1177
|
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"%s [options] [<file>|-]\n"
|
"%s [options] [<file>|-]\n"
|
||||||
|
|
@ -119,7 +94,7 @@ msgstr ""
|
||||||
" -v, --verbose 输出详细操作\n"
|
" -v, --verbose 输出详细操作\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:1184
|
#: src/tools/pw-cat.c:1095
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
" -R, --remote Remote daemon name\n"
|
" -R, --remote Remote daemon name\n"
|
||||||
|
|
@ -151,23 +126,20 @@ msgstr ""
|
||||||
" -P --properties 设置节点属性\n"
|
" -P --properties 设置节点属性\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:1202
|
#: src/tools/pw-cat.c:1113
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
" --rate Sample rate (default %u)\n"
|
" --rate Sample rate (req. for rec) (default "
|
||||||
" --channels Number of channels (default %u)\n"
|
"%u)\n"
|
||||||
|
" --channels Number of channels (req. for rec) "
|
||||||
|
"(default %u)\n"
|
||||||
" --channel-map Channel map\n"
|
" --channel-map Channel map\n"
|
||||||
" a channel layout: \"Stereo\", "
|
" one of: \"Stereo\", \"5.1\",... "
|
||||||
"\"5.1\",... or\n"
|
"or\n"
|
||||||
" comma separated list of channel "
|
" comma separated list of channel "
|
||||||
"names: eg. \"FL,FR\"\n"
|
"names: eg. \"FL,FR\"\n"
|
||||||
" --list-layouts List supported channel layouts\n"
|
" --format Sample format %s (req. for rec) "
|
||||||
" --list-channel-names List supported channel maps\n"
|
"(default %s)\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"
|
|
||||||
" --volume Stream volume 0-1.0 (default %.3f)\n"
|
" --volume Stream volume 0-1.0 (default %.3f)\n"
|
||||||
" -q --quality Resampler quality (0 - 15) (default "
|
" -q --quality Resampler quality (0 - 15) (default "
|
||||||
"%d)\n"
|
"%d)\n"
|
||||||
|
|
@ -177,19 +149,15 @@ msgid ""
|
||||||
" -n, --sample-count COUNT Stop after COUNT samples\n"
|
" -n, --sample-count COUNT Stop after COUNT samples\n"
|
||||||
"\n"
|
"\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
" --rate 采样率 (默认 %u)\n"
|
" --rate 采样率 (录制模式需要) (默认 %u)\n"
|
||||||
" --channels 声道数 (默认 %u)\n"
|
" --channels 通道数 (录制模式需要) (默认 %u)\n"
|
||||||
" --channel-map 声道映射\n"
|
" --channel-map 通道映射\n"
|
||||||
" 声道布局:\"stereo\", "
|
" \"stereo\", \"5.1\",... 中的其一"
|
||||||
"\"5.1\",... 或\n"
|
"或\n"
|
||||||
" 以英文逗号分隔的声道名列表: 如 "
|
" 以英文逗号分隔的通道名列表: 如 "
|
||||||
"\"FL,FR\"\n"
|
"\"FL,FR\"\n"
|
||||||
" --list-layouts 列出支持的声道布局\n"
|
" --format 采样格式 %s (录制模式需要) (默认 "
|
||||||
" --list-channel-names 列出支持的声道映射\n"
|
"%s)\n"
|
||||||
" --format 采样格式 (默认 %s)\n"
|
|
||||||
" --list-formats 列出支持的采样格式\n"
|
|
||||||
" --container 容器格式\n"
|
|
||||||
" --list-containers 列出支持的容器和扩展\n"
|
|
||||||
" --volume 媒体流音量 0-1.0 (默认 %.3f)\n"
|
" --volume 媒体流音量 0-1.0 (默认 %.3f)\n"
|
||||||
" -q --quality 重采样质量 (0 - 15) (默认 %d)\n"
|
" -q --quality 重采样质量 (0 - 15) (默认 %d)\n"
|
||||||
" -a, --raw 原生模式\n"
|
" -a, --raw 原生模式\n"
|
||||||
|
|
@ -198,7 +166,7 @@ msgstr ""
|
||||||
" -n, --sample-count COUNT 计数采样后停止\n"
|
" -n, --sample-count COUNT 计数采样后停止\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:1227
|
#: src/tools/pw-cat.c:1133
|
||||||
msgid ""
|
msgid ""
|
||||||
" -p, --playback Playback mode\n"
|
" -p, --playback Playback mode\n"
|
||||||
" -r, --record Recording mode\n"
|
" -r, --record Recording mode\n"
|
||||||
|
|
@ -218,11 +186,6 @@ msgstr ""
|
||||||
" -c, --midi-clip MIDI 剪辑模式\n"
|
" -c, --midi-clip MIDI 剪辑模式\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
#: src/tools/pw-cat.c:1827
|
|
||||||
#, c-format
|
|
||||||
msgid "Supported containers and extensions:\n"
|
|
||||||
msgstr "支持的容器和扩展:\n"
|
|
||||||
|
|
||||||
#: src/tools/pw-cli.c:2386
|
#: src/tools/pw-cli.c:2386
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
|
|
@ -246,7 +209,7 @@ msgid "Pro Audio"
|
||||||
msgstr "专业音频"
|
msgstr "专业音频"
|
||||||
|
|
||||||
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
|
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2021
|
#: spa/plugins/bluez5/bluez5-device.c:1976
|
||||||
msgid "Off"
|
msgid "Off"
|
||||||
msgstr "关"
|
msgstr "关"
|
||||||
|
|
||||||
|
|
@ -278,7 +241,7 @@ msgstr "输入插孔"
|
||||||
|
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
|
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
|
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2422
|
#: spa/plugins/bluez5/bluez5-device.c:2374
|
||||||
msgid "Microphone"
|
msgid "Microphone"
|
||||||
msgstr "话筒"
|
msgstr "话筒"
|
||||||
|
|
||||||
|
|
@ -344,15 +307,15 @@ msgid "No Bass Boost"
|
||||||
msgstr "无重低音增强"
|
msgstr "无重低音增强"
|
||||||
|
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
|
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2428
|
#: spa/plugins/bluez5/bluez5-device.c:2380
|
||||||
msgid "Speaker"
|
msgid "Speaker"
|
||||||
msgstr "扬声器"
|
msgstr "扬声器"
|
||||||
|
|
||||||
#. Don't call it "headset", the HF one has the mic
|
#. 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:2742
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
|
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2434
|
#: spa/plugins/bluez5/bluez5-device.c:2386
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2501
|
#: spa/plugins/bluez5/bluez5-device.c:2453
|
||||||
msgid "Headphones"
|
msgid "Headphones"
|
||||||
msgstr "模拟耳机"
|
msgstr "模拟耳机"
|
||||||
|
|
||||||
|
|
@ -462,7 +425,7 @@ msgstr "立体声"
|
||||||
|
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
|
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
|
||||||
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
|
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2410
|
#: spa/plugins/bluez5/bluez5-device.c:2362
|
||||||
msgid "Headset"
|
msgid "Headset"
|
||||||
msgstr "耳机"
|
msgstr "耳机"
|
||||||
|
|
||||||
|
|
@ -657,101 +620,101 @@ msgstr "内置音频"
|
||||||
msgid "Modem"
|
msgid "Modem"
|
||||||
msgstr "调制解调器"
|
msgstr "调制解调器"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2032
|
#: spa/plugins/bluez5/bluez5-device.c:1987
|
||||||
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
|
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
|
||||||
msgstr "音频网关 (A2DP 信源 或 HSP/HFP 网关)"
|
msgstr "音频网关 (A2DP 信源 或 HSP/HFP 网关)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2061
|
#: spa/plugins/bluez5/bluez5-device.c:2016
|
||||||
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
|
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
|
||||||
msgstr "助听器音频流 (ASHA 信宿)"
|
msgstr "助听器音频流 (ASHA 信宿)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2104
|
#: spa/plugins/bluez5/bluez5-device.c:2059
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
|
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
|
||||||
msgstr "高保真回放 (A2DP 信宿, 编码 %s)"
|
msgstr "高保真回放 (A2DP 信宿, 编码 %s)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2107
|
#: spa/plugins/bluez5/bluez5-device.c:2062
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
|
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
|
||||||
msgstr "高保真双工 (A2DP 信源/信宿, 编码 %s)"
|
msgstr "高保真双工 (A2DP 信源/信宿, 编码 %s)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2115
|
#: spa/plugins/bluez5/bluez5-device.c:2070
|
||||||
msgid "High Fidelity Playback (A2DP Sink)"
|
msgid "High Fidelity Playback (A2DP Sink)"
|
||||||
msgstr "高保真回放 (A2DP 信宿)"
|
msgstr "高保真回放 (A2DP 信宿)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2117
|
#: spa/plugins/bluez5/bluez5-device.c:2072
|
||||||
msgid "High Fidelity Duplex (A2DP Source/Sink)"
|
msgid "High Fidelity Duplex (A2DP Source/Sink)"
|
||||||
msgstr "高保真双工 (A2DP 信源/信宿)"
|
msgstr "高保真双工 (A2DP 信源/信宿)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2194
|
#: spa/plugins/bluez5/bluez5-device.c:2146
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "High Fidelity Playback (BAP Sink, codec %s)"
|
msgid "High Fidelity Playback (BAP Sink, codec %s)"
|
||||||
msgstr "高保真回放 (BAP 信宿, 编码 %s)"
|
msgstr "高保真回放 (BAP 信宿, 编码 %s)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2199
|
#: spa/plugins/bluez5/bluez5-device.c:2151
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "High Fidelity Input (BAP Source, codec %s)"
|
msgid "High Fidelity Input (BAP Source, codec %s)"
|
||||||
msgstr "高保真输入 (BAP 信源, 编码 %s)"
|
msgstr "高保真输入 (BAP 信源, 编码 %s)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2203
|
#: spa/plugins/bluez5/bluez5-device.c:2155
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
|
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
|
||||||
msgstr "高保真双工 (BAP 信源/信宿, 编码 %s)"
|
msgstr "高保真双工 (BAP 信源/信宿, 编码 %s)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2212
|
#: spa/plugins/bluez5/bluez5-device.c:2164
|
||||||
msgid "High Fidelity Playback (BAP Sink)"
|
msgid "High Fidelity Playback (BAP Sink)"
|
||||||
msgstr "高保真回放 (BAP 信宿)"
|
msgstr "高保真回放 (BAP 信宿)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2216
|
#: spa/plugins/bluez5/bluez5-device.c:2168
|
||||||
msgid "High Fidelity Input (BAP Source)"
|
msgid "High Fidelity Input (BAP Source)"
|
||||||
msgstr "高保真输入 (BAP 信源)"
|
msgstr "高保真输入 (BAP 信源)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2219
|
#: spa/plugins/bluez5/bluez5-device.c:2171
|
||||||
msgid "High Fidelity Duplex (BAP Source/Sink)"
|
msgid "High Fidelity Duplex (BAP Source/Sink)"
|
||||||
msgstr "高保真双工 (BAP 信源/信宿)"
|
msgstr "高保真双工 (BAP 信源/信宿)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2259
|
#: spa/plugins/bluez5/bluez5-device.c:2211
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Headset Head Unit (HSP/HFP, codec %s)"
|
msgid "Headset Head Unit (HSP/HFP, codec %s)"
|
||||||
msgstr "头戴式耳机单元 (HSP/HFP, 编码 %s)"
|
msgstr "头戴式耳机单元 (HSP/HFP, 编码 %s)"
|
||||||
|
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2363
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2368
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2375
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2381
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2387
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2393
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2399
|
||||||
|
#: spa/plugins/bluez5/bluez5-device.c:2405
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2411
|
#: 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"
|
msgid "Handsfree"
|
||||||
msgstr "免提"
|
msgstr "免提"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2417
|
#: spa/plugins/bluez5/bluez5-device.c:2369
|
||||||
msgid "Handsfree (HFP)"
|
msgid "Handsfree (HFP)"
|
||||||
msgstr "免提(HFP)"
|
msgstr "免提(HFP)"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2440
|
#: spa/plugins/bluez5/bluez5-device.c:2392
|
||||||
msgid "Portable"
|
msgid "Portable"
|
||||||
msgstr "便携式"
|
msgstr "便携式"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2446
|
#: spa/plugins/bluez5/bluez5-device.c:2398
|
||||||
msgid "Car"
|
msgid "Car"
|
||||||
msgstr "车内"
|
msgstr "车内"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2452
|
#: spa/plugins/bluez5/bluez5-device.c:2404
|
||||||
msgid "HiFi"
|
msgid "HiFi"
|
||||||
msgstr "高保真"
|
msgstr "高保真"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2458
|
#: spa/plugins/bluez5/bluez5-device.c:2410
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "电话"
|
msgstr "电话"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2465
|
#: spa/plugins/bluez5/bluez5-device.c:2417
|
||||||
msgid "Bluetooth"
|
msgid "Bluetooth"
|
||||||
msgstr "蓝牙"
|
msgstr "蓝牙"
|
||||||
|
|
||||||
#: spa/plugins/bluez5/bluez5-device.c:2466
|
#: spa/plugins/bluez5/bluez5-device.c:2418
|
||||||
msgid "Bluetooth Handsfree"
|
msgid "Bluetooth Handsfree"
|
||||||
msgstr "蓝牙免提"
|
msgstr "蓝牙免提"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -187,11 +187,6 @@ ATTRS{idVendor}=="1395", ATTRS{idProduct}=="0300", ENV{ACP_PROFILE_SET}="usb-gam
|
||||||
# Sennheiser GSP 670 USB headset
|
# Sennheiser GSP 670 USB headset
|
||||||
ATTRS{idVendor}=="1395", ATTRS{idProduct}=="008a", ENV{ACP_PROFILE_SET}="usb-gaming-headset.conf"
|
ATTRS{idVendor}=="1395", ATTRS{idProduct}=="008a", ENV{ACP_PROFILE_SET}="usb-gaming-headset.conf"
|
||||||
|
|
||||||
# JBL Quantum One
|
|
||||||
ATTRS{idVendor}=="0ecb", ATTRS{idProduct}=="203a", ENV{ACP_PROFILE_SET}="usb-gaming-headset-gamefirst.conf"
|
|
||||||
# JBL Quantum 810 Wireless
|
|
||||||
ATTRS{idVendor}=="0ecb", ATTRS{idProduct}=="2069", ENV{ACP_PROFILE_SET}="usb-gaming-headset-gamefirst.conf"
|
|
||||||
|
|
||||||
# Audioengine HD3 powered speakers support IEC958 but don't actually
|
# Audioengine HD3 powered speakers support IEC958 but don't actually
|
||||||
# have any digital outputs.
|
# have any digital outputs.
|
||||||
ATTRS{idVendor}=="0a12", ATTRS{idProduct}=="4007", ENV{ACP_PROFILE_SET}="analog-only.conf"
|
ATTRS{idVendor}=="0a12", ATTRS{idProduct}=="4007", ENV{ACP_PROFILE_SET}="analog-only.conf"
|
||||||
|
|
|
||||||
|
|
@ -429,9 +429,9 @@ 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")
|
#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)
|
if (!p)
|
||||||
return NULL;
|
return NULL;
|
||||||
if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
|
if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
|
||||||
|
|
|
||||||
|
|
@ -3040,17 +3040,10 @@ static int update_time(struct state *state, uint64_t current_time, snd_pcm_sfram
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->rate_match) {
|
if (state->rate_match) {
|
||||||
/* Only set rate_match rate when matching is active. When not matching,
|
|
||||||
* set it to 1.0 to indicate no rate adjustment needed, even though DLL
|
|
||||||
* may still be running for buffer level management. */
|
|
||||||
if (state->matching) {
|
|
||||||
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
||||||
state->rate_match->rate = corr;
|
state->rate_match->rate = corr;
|
||||||
else
|
else
|
||||||
state->rate_match->rate = 1.0/corr;
|
state->rate_match->rate = 1.0/corr;
|
||||||
} else {
|
|
||||||
state->rate_match->rate = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->pitch_elem && state->matching)
|
if (state->pitch_elem && state->matching)
|
||||||
spa_alsa_update_rate_match(state);
|
spa_alsa_update_rate_match(state);
|
||||||
|
|
|
||||||
|
|
@ -725,11 +725,11 @@ static bool check_access(struct impl *this, struct card *card)
|
||||||
|
|
||||||
static void process_card(struct impl *this, enum action action, struct card *card)
|
static void process_card(struct impl *this, enum action action, struct card *card)
|
||||||
{
|
{
|
||||||
switch (action) {
|
|
||||||
case ACTION_CHANGE: {
|
|
||||||
if (card->ignored)
|
if (card->ignored)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case ACTION_CHANGE: {
|
||||||
check_access(this, card);
|
check_access(this, card);
|
||||||
if (card->accessible && !card->emitted) {
|
if (card->accessible && !card->emitted) {
|
||||||
int res = emit_added_object_info(this, card);
|
int res = emit_added_object_info(this, card);
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
# This file is part of PulseAudio.
|
|
||||||
#
|
|
||||||
# PulseAudio is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as
|
|
||||||
# published by the Free Software Foundation; either version 2.1 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# PulseAudio is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
# General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
; USB gaming headset.
|
|
||||||
; These headsets usually have two output devices. The first one is meant
|
|
||||||
; for general audio, and the second one is meant for chat. There is also
|
|
||||||
; a single input device for chat.
|
|
||||||
; The purpose of this unusual design is to provide separate volume
|
|
||||||
; controls for voice and other audio, which can be useful in gaming.
|
|
||||||
;
|
|
||||||
; Works with:
|
|
||||||
; JBL Quantum 810 Wireless
|
|
||||||
; JBL Quantum One
|
|
||||||
;
|
|
||||||
; Based on usb-gaming-headset.conf.
|
|
||||||
;
|
|
||||||
; See default.conf for an explanation on the directives used here.
|
|
||||||
|
|
||||||
[General]
|
|
||||||
auto-profiles = yes
|
|
||||||
|
|
||||||
[Mapping mono-chat-output]
|
|
||||||
description-key = gaming-headset-chat
|
|
||||||
device-strings = hw:%f,1,0
|
|
||||||
channel-map = mono
|
|
||||||
paths-output = usb-gaming-headset-output-mono
|
|
||||||
intended-roles = phone
|
|
||||||
|
|
||||||
[Mapping stereo-chat-output]
|
|
||||||
description-key = gaming-headset-chat
|
|
||||||
device-strings = hw:%f,1,0
|
|
||||||
channel-map = left,right
|
|
||||||
paths-output = usb-gaming-headset-output-stereo
|
|
||||||
intended-roles = phone
|
|
||||||
|
|
||||||
[Mapping mono-chat-input]
|
|
||||||
description-key = gaming-headset-chat
|
|
||||||
device-strings = hw:%f,0,0
|
|
||||||
channel-map = mono
|
|
||||||
paths-input = usb-gaming-headset-input
|
|
||||||
intended-roles = phone
|
|
||||||
|
|
||||||
[Mapping stereo-game-output]
|
|
||||||
description-key = gaming-headset-game
|
|
||||||
device-strings = hw:%f,0,0
|
|
||||||
channel-map = left,right
|
|
||||||
paths-output = usb-gaming-headset-output-stereo
|
|
||||||
direction = output
|
|
||||||
|
|
||||||
[Profile output:mono-chat+output:stereo-game+input:mono-chat]
|
|
||||||
output-mappings = mono-chat-output stereo-game-output
|
|
||||||
input-mappings = mono-chat-input
|
|
||||||
priority = 5100
|
|
||||||
|
|
||||||
[Profile output:stereo-game+output:stereo-chat+input:mono-chat]
|
|
||||||
output-mappings = stereo-game-output stereo-chat-output
|
|
||||||
input-mappings = mono-chat-input
|
|
||||||
priority = 5100
|
|
||||||
|
|
@ -268,7 +268,6 @@ struct impl {
|
||||||
struct spa_list active_graphs;
|
struct spa_list active_graphs;
|
||||||
struct filter_graph graphs[MAX_GRAPH];
|
struct filter_graph graphs[MAX_GRAPH];
|
||||||
struct spa_process_latency_info latency;
|
struct spa_process_latency_info latency;
|
||||||
char *graph_descs[MAX_GRAPH];
|
|
||||||
|
|
||||||
int in_filter_props;
|
int in_filter_props;
|
||||||
int filter_props_count;
|
int filter_props_count;
|
||||||
|
|
@ -847,7 +846,6 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
|
||||||
{
|
{
|
||||||
struct props *p = &this->props;
|
struct props *p = &this->props;
|
||||||
struct spa_pod_frame f[2];
|
struct spa_pod_frame f[2];
|
||||||
struct filter_graph *g;
|
|
||||||
|
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
@ -920,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_bool(b, p->lock_volumes);
|
||||||
spa_pod_builder_string(b, "audioconvert.filter-graph.disable");
|
spa_pod_builder_string(b, "audioconvert.filter-graph.disable");
|
||||||
spa_pod_builder_bool(b, p->filter_graph_disabled);
|
spa_pod_builder_bool(b, p->filter_graph_disabled);
|
||||||
spa_list_for_each(g, &this->active_graphs, link) {
|
spa_pod_builder_string(b, "audioconvert.filter-graph");
|
||||||
char key[64];
|
spa_pod_builder_string(b, "");
|
||||||
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_pop(b, &f[1]);
|
spa_pod_builder_pop(b, &f[1]);
|
||||||
*param = spa_pod_builder_pop(b, &f[0]);
|
*param = spa_pod_builder_pop(b, &f[0]);
|
||||||
break;
|
break;
|
||||||
|
|
@ -959,7 +953,7 @@ static int impl_node_enum_params(void *object, int seq,
|
||||||
struct impl *this = object;
|
struct impl *this = object;
|
||||||
struct spa_pod *param;
|
struct spa_pod *param;
|
||||||
struct spa_pod_builder b = { 0 };
|
struct spa_pod_builder b = { 0 };
|
||||||
uint8_t buffer[16384];
|
uint8_t buffer[4096];
|
||||||
struct spa_result_node_params result;
|
struct spa_result_node_params result;
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
@ -1415,7 +1409,6 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order)
|
||||||
g->removing = true;
|
g->removing = true;
|
||||||
spa_log_info(impl->log, "removing filter-graph order:%d", order);
|
spa_log_info(impl->log, "removing filter-graph order:%d", order);
|
||||||
}
|
}
|
||||||
free(impl->graph_descs[order]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (graph != NULL && graph[0] != '\0') {
|
if (graph != NULL && graph[0] != '\0') {
|
||||||
|
|
@ -1441,8 +1434,6 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order)
|
||||||
spa_list_remove(&pending->link);
|
spa_list_remove(&pending->link);
|
||||||
insert_graph(&impl->active_graphs, pending);
|
insert_graph(&impl->active_graphs, pending);
|
||||||
|
|
||||||
impl->graph_descs[order] = strdup(graph);
|
|
||||||
|
|
||||||
spa_log_info(impl->log, "loading filter-graph order:%d", order);
|
spa_log_info(impl->log, "loading filter-graph order:%d", order);
|
||||||
}
|
}
|
||||||
if (impl->setup)
|
if (impl->setup)
|
||||||
|
|
@ -4243,7 +4234,6 @@ static void free_dir(struct dir *dir)
|
||||||
static int impl_clear(struct spa_handle *handle)
|
static int impl_clear(struct spa_handle *handle)
|
||||||
{
|
{
|
||||||
struct impl *this;
|
struct impl *this;
|
||||||
int i;
|
|
||||||
|
|
||||||
spa_return_val_if_fail(handle != NULL, -EINVAL);
|
spa_return_val_if_fail(handle != NULL, -EINVAL);
|
||||||
|
|
||||||
|
|
@ -4255,10 +4245,6 @@ static int impl_clear(struct spa_handle *handle)
|
||||||
free_tmp(this);
|
free_tmp(this);
|
||||||
|
|
||||||
clean_filter_handles(this, true);
|
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)
|
if (this->resample.free)
|
||||||
resample_free(&this->resample);
|
resample_free(&this->resample);
|
||||||
|
|
@ -4313,7 +4299,6 @@ impl_init(const struct spa_handle_factory *factory,
|
||||||
struct filter_graph *g = &this->graphs[i];
|
struct filter_graph *g = &this->graphs[i];
|
||||||
g->impl = this;
|
g->impl = this;
|
||||||
spa_list_append(&this->free_graphs, &g->link);
|
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.interval = 2 * SPA_NSEC_PER_SEC;
|
||||||
|
|
|
||||||
|
|
@ -720,7 +720,7 @@ done:
|
||||||
if (src_paired == 0)
|
if (src_paired == 0)
|
||||||
src_paired = ~0LU;
|
src_paired = ~0LU;
|
||||||
|
|
||||||
for (jc = 0, ic = 0, i = 0; ic < dst_chan; i++) {
|
for (jc = 0, ic = 0, i = 0; i < CHANNEL_BITS; i++) {
|
||||||
float sum = 0.0f;
|
float sum = 0.0f;
|
||||||
char str1[1024], str2[1024];
|
char str1[1024], str2[1024];
|
||||||
struct spa_strbuf sb1, sb2;
|
struct spa_strbuf sb1, sb2;
|
||||||
|
|
@ -728,10 +728,12 @@ done:
|
||||||
spa_strbuf_init(&sb1, str1, sizeof(str1));
|
spa_strbuf_init(&sb1, str1, sizeof(str1));
|
||||||
spa_strbuf_init(&sb2, str2, sizeof(str2));
|
spa_strbuf_init(&sb2, str2, sizeof(str2));
|
||||||
|
|
||||||
if (i < CHANNEL_BITS && (dst_paired & (1UL << i)) == 0)
|
if ((dst_paired & (1UL << i)) == 0)
|
||||||
continue;
|
continue;
|
||||||
for (jc = 0, j = 0; jc < src_chan; j++) {
|
for (jc = 0, j = 0; j < CHANNEL_BITS; j++) {
|
||||||
if (j < CHANNEL_BITS && (src_paired & (1UL << j)) == 0)
|
if ((src_paired & (1UL << j)) == 0)
|
||||||
|
continue;
|
||||||
|
if (ic >= dst_chan || jc >= src_chan)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (ic == 0)
|
if (ic == 0)
|
||||||
|
|
@ -750,7 +752,7 @@ done:
|
||||||
if (sb2.pos > 0)
|
if (sb2.pos > 0)
|
||||||
spa_log_info(mix->log, " %s", str2);
|
spa_log_info(mix->log, " %s", str2);
|
||||||
if (sb1.pos > 0) {
|
if (sb1.pos > 0) {
|
||||||
spa_log_info(mix->log, "%03d %-4.4s %s %f", ic,
|
spa_log_info(mix->log, "%-4.4s %s %f",
|
||||||
dst_mask == 0 ? "UNK" :
|
dst_mask == 0 ? "UNK" :
|
||||||
spa_debug_type_find_short_name(spa_type_audio_channel, i + _SH),
|
spa_debug_type_find_short_name(spa_type_audio_channel, i + _SH),
|
||||||
str1, sum);
|
str1, sum);
|
||||||
|
|
@ -823,6 +825,7 @@ static void impl_channelmix_set_volume(struct channelmix *mix, float volume, boo
|
||||||
for (i = 0; i < dst_chan; i++) {
|
for (i = 0; i < dst_chan; i++) {
|
||||||
for (j = 0; j < src_chan; j++) {
|
for (j = 0; j < src_chan; j++) {
|
||||||
float v = mix->matrix[i][j];
|
float v = mix->matrix[i][j];
|
||||||
|
spa_log_debug(mix->log, "%d %d: %f", i, j, v);
|
||||||
if (i == 0 && j == 0)
|
if (i == 0 && j == 0)
|
||||||
t = v;
|
t = v;
|
||||||
else if (t != v)
|
else if (t != v)
|
||||||
|
|
@ -837,32 +840,8 @@ static void impl_channelmix_set_volume(struct channelmix *mix, float volume, boo
|
||||||
SPA_FLAG_UPDATE(mix->flags, CHANNELMIX_FLAG_IDENTITY,
|
SPA_FLAG_UPDATE(mix->flags, CHANNELMIX_FLAG_IDENTITY,
|
||||||
dst_chan == src_chan && SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_COPY));
|
dst_chan == src_chan && SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_COPY));
|
||||||
|
|
||||||
if (SPA_UNLIKELY(spa_log_level_topic_enabled(mix->log,
|
|
||||||
SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_DEBUG))) {
|
|
||||||
char str1[1024], str2[1024];
|
|
||||||
struct spa_strbuf sb1, sb2;
|
|
||||||
spa_strbuf_init(&sb2, str2, sizeof(str2));
|
|
||||||
for (i = 0; i < dst_chan; i++) {
|
|
||||||
spa_strbuf_init(&sb1, str1, sizeof(str1));
|
|
||||||
for (j = 0; j < src_chan; j++) {
|
|
||||||
float v = mix->matrix[i][j];
|
|
||||||
if (i == 0)
|
|
||||||
spa_strbuf_append(&sb2, " %03d ", j);
|
|
||||||
if (v == 0.0f)
|
|
||||||
spa_strbuf_append(&sb1, " ");
|
|
||||||
else
|
|
||||||
spa_strbuf_append(&sb1, "%1.3f ", v);
|
|
||||||
}
|
|
||||||
if (i == 0 && sb2.pos > 0)
|
|
||||||
spa_log_debug(mix->log, " %s", str2);
|
|
||||||
if (sb1.pos > 0)
|
|
||||||
spa_log_debug(mix->log, "%03d %s %03d", i, str1, i);
|
|
||||||
}
|
|
||||||
if (sb2.pos > 0)
|
|
||||||
spa_log_debug(mix->log, " %s", str2);
|
|
||||||
spa_log_debug(mix->log, "flags:%08x", mix->flags);
|
spa_log_debug(mix->log, "flags:%08x", mix->flags);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void impl_channelmix_free(struct channelmix *mix)
|
static void impl_channelmix_free(struct channelmix *mix)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,6 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.native");
|
||||||
|
|
||||||
#define RFCOMM_MESSAGE_MAX_LENGTH 256
|
#define RFCOMM_MESSAGE_MAX_LENGTH 256
|
||||||
|
|
||||||
#define BT_CODEC_CVSD 0x02
|
|
||||||
#define BT_CODEC_MSBC 0x05
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
HFP_AG_INITIAL_CODEC_SETUP_NONE = 0,
|
HFP_AG_INITIAL_CODEC_SETUP_NONE = 0,
|
||||||
HFP_AG_INITIAL_CODEC_SETUP_SEND,
|
HFP_AG_INITIAL_CODEC_SETUP_SEND,
|
||||||
|
|
@ -115,7 +112,6 @@ struct impl {
|
||||||
int hfp_default_speaker_volume;
|
int hfp_default_speaker_volume;
|
||||||
|
|
||||||
struct spa_source sco;
|
struct spa_source sco;
|
||||||
unsigned int hfphsp_sco_datapath;
|
|
||||||
|
|
||||||
const struct spa_bt_quirks *quirks;
|
const struct spa_bt_quirks *quirks;
|
||||||
|
|
||||||
|
|
@ -301,33 +297,6 @@ static const struct media_codec *codec_list_best(struct impl *backend, struct sp
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sco_offload_btcodec(struct impl *backend, int sock, bool msbc)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
char buffer[255];
|
|
||||||
struct bt_codecs *codecs;
|
|
||||||
|
|
||||||
if (backend->hfphsp_sco_datapath == HFP_SCO_DEFAULT_DATAPATH)
|
|
||||||
return;
|
|
||||||
|
|
||||||
spa_log_info(backend->log, "sock(%d) msbc(%d)", sock, msbc);
|
|
||||||
|
|
||||||
memset(buffer, 0, sizeof(buffer));
|
|
||||||
codecs = (void *)buffer;
|
|
||||||
if (msbc)
|
|
||||||
codecs->codecs[0].id = BT_CODEC_MSBC;
|
|
||||||
else
|
|
||||||
codecs->codecs[0].id = BT_CODEC_CVSD;
|
|
||||||
codecs->num_codecs = 1;
|
|
||||||
codecs->codecs[0].data_path_id = backend->hfphsp_sco_datapath;
|
|
||||||
|
|
||||||
err = setsockopt(sock, SOL_BLUETOOTH, BT_CODEC, codecs, sizeof(buffer));
|
|
||||||
if (err < 0)
|
|
||||||
spa_log_error(backend->log, "ERROR: %s (%d)", strerror(errno), errno);
|
|
||||||
else
|
|
||||||
spa_log_info(backend->log, "set offload codec succeeded");
|
|
||||||
}
|
|
||||||
|
|
||||||
static DBusHandlerResult profile_release(DBusConnection *conn, DBusMessage *m, void *userdata)
|
static DBusHandlerResult profile_release(DBusConnection *conn, DBusMessage *m, void *userdata)
|
||||||
{
|
{
|
||||||
if (!reply_with_error(conn, m, BLUEZ_PROFILE_INTERFACE ".Error.NotImplemented", "Method not implemented"))
|
if (!reply_with_error(conn, m, BLUEZ_PROFILE_INTERFACE ".Error.NotImplemented", "Method not implemented"))
|
||||||
|
|
@ -2626,8 +2595,6 @@ static int sco_create_socket(struct impl *backend, struct spa_bt_adapter *adapte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sco_offload_btcodec(backend, sock, transparent);
|
|
||||||
|
|
||||||
return spa_steal_fd(sock);
|
return spa_steal_fd(sock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4134,14 +4101,6 @@ static void parse_hfp_default_volumes(struct impl *backend, const struct spa_dic
|
||||||
backend->hfp_default_speaker_volume = SPA_BT_VOLUME_HS_MAX;
|
backend->hfp_default_speaker_volume = SPA_BT_VOLUME_HS_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_sco_datapath(struct impl *backend, const struct spa_dict *info)
|
|
||||||
{
|
|
||||||
backend->hfphsp_sco_datapath = HFP_SCO_DEFAULT_DATAPATH;
|
|
||||||
|
|
||||||
spa_atou32(spa_dict_lookup(info, "bluez5.hw-offload-datapath"),
|
|
||||||
&backend->hfphsp_sco_datapath, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct spa_bt_backend_implementation backend_impl = {
|
static const struct spa_bt_backend_implementation backend_impl = {
|
||||||
SPA_VERSION_BT_BACKEND_IMPLEMENTATION,
|
SPA_VERSION_BT_BACKEND_IMPLEMENTATION,
|
||||||
.free = backend_native_free,
|
.free = backend_native_free,
|
||||||
|
|
@ -4204,7 +4163,6 @@ struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
|
||||||
parse_hfp_disable_nrec(backend, info);
|
parse_hfp_disable_nrec(backend, info);
|
||||||
parse_hfp_default_volumes(backend, info);
|
parse_hfp_default_volumes(backend, info);
|
||||||
parse_hfp_pts(backend, info);
|
parse_hfp_pts(backend, info);
|
||||||
parse_sco_datapath(backend, info);
|
|
||||||
|
|
||||||
#ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE
|
#ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE
|
||||||
if (!dbus_connection_register_object_path(backend->conn,
|
if (!dbus_connection_register_object_path(backend->conn,
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ struct settings {
|
||||||
int latency;
|
int latency;
|
||||||
int64_t delay;
|
int64_t delay;
|
||||||
int framing;
|
int framing;
|
||||||
char *force_target_latency;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pac_data {
|
struct pac_data {
|
||||||
|
|
@ -77,7 +76,6 @@ struct bap_qos {
|
||||||
uint16_t latency;
|
uint16_t latency;
|
||||||
uint32_t delay;
|
uint32_t delay;
|
||||||
unsigned int priority;
|
unsigned int priority;
|
||||||
char *tag;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -98,50 +96,50 @@ struct config_data {
|
||||||
struct settings settings;
|
struct settings settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define BAP_QOS(name_, rate_, duration_, framing_, framelen_, rtn_, latency_, delay_, priority_, tag_) \
|
#define BAP_QOS(name_, rate_, duration_, framing_, framelen_, rtn_, latency_, delay_, priority_) \
|
||||||
((struct bap_qos){ .name = (name_), .rate = (rate_), .frame_duration = (duration_), .framing = (framing_), \
|
((struct bap_qos){ .name = (name_), .rate = (rate_), .frame_duration = (duration_), .framing = (framing_), \
|
||||||
.framelen = (framelen_), .retransmission = (rtn_), .latency = (latency_), \
|
.framelen = (framelen_), .retransmission = (rtn_), .latency = (latency_), \
|
||||||
.delay = (delay_), .priority = (priority_), .tag = (tag_) })
|
.delay = (delay_), .priority = (priority_) })
|
||||||
|
|
||||||
static const struct bap_qos bap_qos_configs[] = {
|
static const struct bap_qos bap_qos_configs[] = {
|
||||||
/* Priority: low-latency > high-reliability, 7.5ms > 10ms,
|
/* Priority: low-latency > high-reliability, 7.5ms > 10ms,
|
||||||
* bigger frequency and sdu better */
|
* bigger frequency and sdu better */
|
||||||
|
|
||||||
/* BAP v1.0.1 Table 5.2; low-latency */
|
/* BAP v1.0.1 Table 5.2; low-latency */
|
||||||
BAP_QOS("8_1_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 2, 8, 40000, 30, "low-latency"), /* 8_1_1 */
|
BAP_QOS("8_1_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 2, 8, 40000, 30), /* 8_1_1 */
|
||||||
BAP_QOS("8_2_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 2, 10, 40000, 20, "low-latency"), /* 8_2_1 */
|
BAP_QOS("8_2_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 2, 10, 40000, 20), /* 8_2_1 */
|
||||||
BAP_QOS("16_1_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 2, 8, 40000, 31, "low-latency"), /* 16_1_1 */
|
BAP_QOS("16_1_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 2, 8, 40000, 31), /* 16_1_1 */
|
||||||
BAP_QOS("16_2_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 2, 10, 40000, 21, "low-latency"), /* 16_2_1 (mandatory) */
|
BAP_QOS("16_2_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 2, 10, 40000, 21), /* 16_2_1 (mandatory) */
|
||||||
BAP_QOS("24_1_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 2, 8, 40000, 32, "low-latency"), /* 24_1_1 */
|
BAP_QOS("24_1_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 2, 8, 40000, 32), /* 24_1_1 */
|
||||||
BAP_QOS("24_2_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 2, 10, 40000, 22, "low-latency"), /* 24_2_1 */
|
BAP_QOS("24_2_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 2, 10, 40000, 22), /* 24_2_1 */
|
||||||
BAP_QOS("32_1_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 2, 8, 40000, 33, "low-latency"), /* 32_1_1 */
|
BAP_QOS("32_1_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 2, 8, 40000, 33), /* 32_1_1 */
|
||||||
BAP_QOS("32_2_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 2, 10, 40000, 23, "low-latency"), /* 32_2_1 */
|
BAP_QOS("32_2_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 2, 10, 40000, 23), /* 32_2_1 */
|
||||||
BAP_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 5, 24, 40000, 34, "low-latency"), /* 441_1_1 */
|
BAP_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 5, 24, 40000, 34), /* 441_1_1 */
|
||||||
BAP_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 5, 31, 40000, 24, "low-latency"), /* 441_2_1 */
|
BAP_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 5, 31, 40000, 24), /* 441_2_1 */
|
||||||
BAP_QOS("48_1_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 5, 15, 40000, 35, "low-latency"), /* 48_1_1 */
|
BAP_QOS("48_1_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 5, 15, 40000, 35), /* 48_1_1 */
|
||||||
BAP_QOS("48_2_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 5, 20, 40000, 25, "low-latency"), /* 48_2_1 */
|
BAP_QOS("48_2_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 5, 20, 40000, 25), /* 48_2_1 */
|
||||||
BAP_QOS("48_3_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 5, 15, 40000, 36, "low-latency"), /* 48_3_1 */
|
BAP_QOS("48_3_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 5, 15, 40000, 36), /* 48_3_1 */
|
||||||
BAP_QOS("48_4_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 5, 20, 40000, 26, "low-latency"), /* 48_4_1 */
|
BAP_QOS("48_4_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 5, 20, 40000, 26), /* 48_4_1 */
|
||||||
BAP_QOS("48_5_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 5, 15, 40000, 37, "low-latency"), /* 48_5_1 */
|
BAP_QOS("48_5_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 5, 15, 40000, 37), /* 48_5_1 */
|
||||||
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_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 5, 20, 40000, 27), /* 48_6_1 */
|
||||||
|
|
||||||
/* BAP v1.0.1 Table 5.2; high-reliability */
|
/* 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-reliabilty"), /* 8_1_2 */
|
BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 13, 75, 40000, 10), /* 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("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 13, 95, 40000, 0), /* 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_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 13, 75, 40000, 11), /* 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("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 13, 95, 40000, 1), /* 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_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 13, 75, 40000, 12), /* 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("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2), /* 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_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13), /* 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("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3), /* 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_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 14), /* 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("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 4), /* 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_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 15), /* 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_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 5), /* 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_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 16), /* 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_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 6), /* 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_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 17), /* 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 */
|
BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 7), /* 48_6_2 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct bap_qos bap_bcast_qos_configs[] = {
|
static const struct bap_qos bap_bcast_qos_configs[] = {
|
||||||
|
|
@ -149,40 +147,40 @@ static const struct bap_qos bap_bcast_qos_configs[] = {
|
||||||
* bigger frequency and sdu better */
|
* bigger frequency and sdu better */
|
||||||
|
|
||||||
/* BAP v1.0.1 Table 6.4; low-latency */
|
/* BAP v1.0.1 Table 6.4; low-latency */
|
||||||
BAP_QOS("8_1_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 2, 8, 40000, 30, "low-latency"), /* 8_1_1 */
|
BAP_QOS("8_1_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 2, 8, 40000, 30), /* 8_1_1 */
|
||||||
BAP_QOS("8_2_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 2, 10, 40000, 20, "low-latency"), /* 8_2_1 */
|
BAP_QOS("8_2_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 2, 10, 40000, 20), /* 8_2_1 */
|
||||||
BAP_QOS("16_1_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 2, 8, 40000, 31, "low-latency"), /* 16_1_1 */
|
BAP_QOS("16_1_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 2, 8, 40000, 31), /* 16_1_1 */
|
||||||
BAP_QOS("16_2_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 2, 10, 40000, 21, "low-latency"), /* 16_2_1 (mandatory) */
|
BAP_QOS("16_2_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 2, 10, 40000, 21), /* 16_2_1 (mandatory) */
|
||||||
BAP_QOS("24_1_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 2, 8, 40000, 32, "low-latency"), /* 24_1_1 */
|
BAP_QOS("24_1_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 2, 8, 40000, 32), /* 24_1_1 */
|
||||||
BAP_QOS("24_2_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 2, 10, 40000, 22, "low-latency"), /* 24_2_1 */
|
BAP_QOS("24_2_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 2, 10, 40000, 22), /* 24_2_1 */
|
||||||
BAP_QOS("32_1_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 2, 8, 40000, 33, "low-latency"), /* 32_1_1 */
|
BAP_QOS("32_1_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 2, 8, 40000, 33), /* 32_1_1 */
|
||||||
BAP_QOS("32_2_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 2, 10, 40000, 23, "low-latency"), /* 32_2_1 */
|
BAP_QOS("32_2_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 2, 10, 40000, 23), /* 32_2_1 */
|
||||||
BAP_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 24, 40000, 34, "low-latency"), /* 441_1_1 */
|
BAP_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 24, 40000, 34), /* 441_1_1 */
|
||||||
BAP_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 31, 40000, 24, "low-latency"), /* 441_2_1 */
|
BAP_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 31, 40000, 24), /* 441_2_1 */
|
||||||
BAP_QOS("48_1_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 15, 40000, 35, "low-latency"), /* 48_1_1 */
|
BAP_QOS("48_1_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 15, 40000, 35), /* 48_1_1 */
|
||||||
BAP_QOS("48_2_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 20, 40000, 25, "low-latency"), /* 48_2_1 */
|
BAP_QOS("48_2_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 20, 40000, 25), /* 48_2_1 */
|
||||||
BAP_QOS("48_3_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 15, 40000, 36, "low-latency"), /* 48_3_1 */
|
BAP_QOS("48_3_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 15, 40000, 36), /* 48_3_1 */
|
||||||
BAP_QOS("48_4_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 20, 40000, 26, "low-latency"), /* 48_4_1 */
|
BAP_QOS("48_4_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 20, 40000, 26), /* 48_4_1 */
|
||||||
BAP_QOS("48_5_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 15, 40000, 37, "low-latency"), /* 48_5_1 */
|
BAP_QOS("48_5_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 15, 40000, 37), /* 48_5_1 */
|
||||||
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_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 20, 40000, 27), /* 48_6_1 */
|
||||||
|
|
||||||
/* BAP v1.0.1 Table 6.4; high-reliability */
|
/* 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-reliabilty"), /* 8_1_2 */
|
BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 4, 45, 40000, 10), /* 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("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 4, 60, 40000, 0), /* 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_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 4, 45, 40000, 11), /* 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("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 4, 60, 40000, 1), /* 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_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 4, 45, 40000, 12), /* 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("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 4, 60, 40000, 2), /* 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_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 4, 45, 40000, 13), /* 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("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 4, 60, 40000, 3), /* 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_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 54, 40000, 14), /* 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("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 60, 40000, 4), /* 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_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 50, 40000, 15), /* 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_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 65, 40000, 5), /* 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_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 50, 40000, 16), /* 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_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 65, 40000, 6), /* 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_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 50, 40000, 17), /* 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 */
|
BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 65, 40000, 7), /* 48_6_2 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned int get_rate_mask(uint8_t rate) {
|
static unsigned int get_rate_mask(uint8_t rate) {
|
||||||
|
|
@ -478,9 +476,6 @@ static bool select_bap_qos(struct bap_qos *conf,
|
||||||
else if (c.priority < conf->priority)
|
else if (c.priority < conf->priority)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (s->force_target_latency && !spa_streq(s->force_target_latency, c.tag))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (s->retransmission >= 0)
|
if (s->retransmission >= 0)
|
||||||
c.retransmission = s->retransmission;
|
c.retransmission = s->retransmission;
|
||||||
if (s->latency >= 0)
|
if (s->latency >= 0)
|
||||||
|
|
@ -849,9 +844,6 @@ static void parse_settings(struct settings *s, const struct spa_dict *settings,
|
||||||
if ((str = spa_dict_lookup(settings, "bluez5.bap.preset")))
|
if ((str = spa_dict_lookup(settings, "bluez5.bap.preset")))
|
||||||
s->qos_name = strdup(str);
|
s->qos_name = strdup(str);
|
||||||
|
|
||||||
if ((str = spa_dict_lookup(settings, "bluez5.bap.force-target-latency")))
|
|
||||||
s->force_target_latency = strdup(str);
|
|
||||||
|
|
||||||
if (spa_atou32(spa_dict_lookup(settings, "bluez5.bap.rtn"), &value, 0))
|
if (spa_atou32(spa_dict_lookup(settings, "bluez5.bap.rtn"), &value, 0))
|
||||||
s->retransmission = value;
|
s->retransmission = value;
|
||||||
|
|
||||||
|
|
@ -889,11 +881,11 @@ static void parse_settings(struct settings *s, const struct spa_dict *settings,
|
||||||
|
|
||||||
spa_debugc(&debug_ctx->ctx,
|
spa_debugc(&debug_ctx->ctx,
|
||||||
"BAP LC3 settings: preset:%s rtn:%d latency:%d delay:%d framing:%d "
|
"BAP LC3 settings: preset:%s rtn:%d latency:%d delay:%d framing:%d "
|
||||||
"locations:%x chnalloc:%x sink:%d duplex:%d force-target-latency:%s",
|
"locations:%x chnalloc:%x sink:%d duplex:%d",
|
||||||
s->qos_name ? s->qos_name : "auto",
|
s->qos_name ? s->qos_name : "auto",
|
||||||
s->retransmission, s->latency, (int)s->delay, s->framing,
|
s->retransmission, s->latency, (int)s->delay, s->framing,
|
||||||
(unsigned int)s->locations, (unsigned int)s->channel_allocation,
|
(unsigned int)s->locations, (unsigned int)s->channel_allocation,
|
||||||
(int)s->sink, (int)s->duplex, s->force_target_latency);
|
(int)s->sink, (int)s->duplex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_config_data(struct config_data *d)
|
static void free_config_data(struct config_data *d)
|
||||||
|
|
@ -901,7 +893,6 @@ static void free_config_data(struct config_data *d)
|
||||||
if (!d)
|
if (!d)
|
||||||
return;
|
return;
|
||||||
free(d->settings.qos_name);
|
free(d->settings.qos_name);
|
||||||
free(d->settings.force_target_latency);
|
|
||||||
free(d);
|
free(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,6 @@ struct spa_bt_monitor {
|
||||||
struct spa_bt_remote_endpoint {
|
struct spa_bt_remote_endpoint {
|
||||||
struct spa_list link;
|
struct spa_list link;
|
||||||
struct spa_list device_link;
|
struct spa_list device_link;
|
||||||
struct spa_list adapter_link;
|
|
||||||
struct spa_bt_monitor *monitor;
|
struct spa_bt_monitor *monitor;
|
||||||
char *path;
|
char *path;
|
||||||
char *transport_path;
|
char *transport_path;
|
||||||
|
|
@ -156,7 +155,6 @@ struct spa_bt_remote_endpoint {
|
||||||
char *uuid;
|
char *uuid;
|
||||||
unsigned int codec;
|
unsigned int codec;
|
||||||
struct spa_bt_device *device;
|
struct spa_bt_device *device;
|
||||||
struct spa_bt_adapter *adapter;
|
|
||||||
uint8_t *capabilities;
|
uint8_t *capabilities;
|
||||||
size_t capabilities_len;
|
size_t capabilities_len;
|
||||||
uint8_t *metadata;
|
uint8_t *metadata;
|
||||||
|
|
@ -196,7 +194,6 @@ struct spa_bt_bis {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define BROADCAST_CODE_LEN 16
|
#define BROADCAST_CODE_LEN 16
|
||||||
#define BD_ADDR_STR_LEN 17
|
|
||||||
|
|
||||||
struct spa_bt_big {
|
struct spa_bt_big {
|
||||||
struct spa_list link;
|
struct spa_list link;
|
||||||
|
|
@ -205,7 +202,6 @@ struct spa_bt_big {
|
||||||
struct spa_list bis_list;
|
struct spa_list bis_list;
|
||||||
int big_id;
|
int big_id;
|
||||||
int sync_factor;
|
int sync_factor;
|
||||||
char adapter[BD_ADDR_STR_LEN + 3];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -720,12 +716,14 @@ static const char *bap_features_get_uuid(struct bap_features *feat, size_t i)
|
||||||
/** Get feature name at \a i, or NULL if uuid doesn't match */
|
/** Get feature name at \a i, or NULL if uuid doesn't match */
|
||||||
static const char *bap_features_get_name(struct bap_features *feat, size_t i, const char *uuid)
|
static const char *bap_features_get_name(struct bap_features *feat, size_t i, const char *uuid)
|
||||||
{
|
{
|
||||||
|
char *pos;
|
||||||
|
|
||||||
if (i >= feat->dict.n_items)
|
if (i >= feat->dict.n_items)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (!spa_streq(feat->dict.items[i].value, uuid))
|
if (!spa_streq(feat->dict.items[i].value, uuid))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
const char *pos = strchr(feat->dict.items[i].key, ':');
|
pos = strchr(feat->dict.items[i].key, ':');
|
||||||
if (!pos)
|
if (!pos)
|
||||||
return NULL;
|
return NULL;
|
||||||
return pos + 1;
|
return pos + 1;
|
||||||
|
|
@ -736,11 +734,6 @@ static void bap_features_clear(struct bap_features *feat)
|
||||||
spa_zero(*feat);
|
spa_zero(*feat);
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct spa_dict *get_device_codec_settings(struct spa_bt_device *device, bool bap)
|
|
||||||
{
|
|
||||||
return bap ? device->settings : &device->monitor->global_settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata)
|
static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata)
|
||||||
{
|
{
|
||||||
struct spa_bt_monitor *monitor = userdata;
|
struct spa_bt_monitor *monitor = userdata;
|
||||||
|
|
@ -1334,6 +1327,7 @@ static struct spa_bt_adapter *adapter_find(struct spa_bt_monitor *monitor, const
|
||||||
static int parse_modalias(const char *modalias, uint16_t *source, uint16_t *vendor,
|
static int parse_modalias(const char *modalias, uint16_t *source, uint16_t *vendor,
|
||||||
uint16_t *product, uint16_t *version)
|
uint16_t *product, uint16_t *version)
|
||||||
{
|
{
|
||||||
|
char *pos;
|
||||||
unsigned int src, i, j, k;
|
unsigned int src, i, j, k;
|
||||||
|
|
||||||
if (spa_strstartswith(modalias, "bluetooth:"))
|
if (spa_strstartswith(modalias, "bluetooth:"))
|
||||||
|
|
@ -1343,7 +1337,7 @@ static int parse_modalias(const char *modalias, uint16_t *source, uint16_t *vend
|
||||||
else
|
else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
const char *pos = strchr(modalias, ':');
|
pos = strchr(modalias, ':');
|
||||||
if (pos == NULL)
|
if (pos == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
|
@ -1640,8 +1634,6 @@ static struct spa_bt_adapter *adapter_create(struct spa_bt_monitor *monitor, con
|
||||||
d->monitor = monitor;
|
d->monitor = monitor;
|
||||||
d->path = strdup(path);
|
d->path = strdup(path);
|
||||||
|
|
||||||
spa_list_init(&d->remote_endpoint_list);
|
|
||||||
|
|
||||||
spa_list_prepend(&monitor->adapter_list, &d->link);
|
spa_list_prepend(&monitor->adapter_list, &d->link);
|
||||||
|
|
||||||
adapter_init_bus_type(monitor, d);
|
adapter_init_bus_type(monitor, d);
|
||||||
|
|
@ -1656,7 +1648,6 @@ static void adapter_free(struct spa_bt_adapter *adapter)
|
||||||
{
|
{
|
||||||
struct spa_bt_monitor *monitor = adapter->monitor;
|
struct spa_bt_monitor *monitor = adapter->monitor;
|
||||||
struct spa_bt_device *d, *td;
|
struct spa_bt_device *d, *td;
|
||||||
struct spa_bt_remote_endpoint *ep, *tep;
|
|
||||||
|
|
||||||
spa_log_debug(monitor->log, "%p", adapter);
|
spa_log_debug(monitor->log, "%p", adapter);
|
||||||
|
|
||||||
|
|
@ -1665,13 +1656,6 @@ static void adapter_free(struct spa_bt_adapter *adapter)
|
||||||
if (d->adapter == adapter)
|
if (d->adapter == adapter)
|
||||||
device_free(d);
|
device_free(d);
|
||||||
|
|
||||||
spa_list_for_each_safe(ep, tep, &adapter->remote_endpoint_list, adapter_link) {
|
|
||||||
if (ep->adapter == adapter) {
|
|
||||||
spa_list_remove(&ep->adapter_link);
|
|
||||||
ep->adapter = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spa_bt_player_destroy(adapter->dummy_player);
|
spa_bt_player_destroy(adapter->dummy_player);
|
||||||
|
|
||||||
spa_list_remove(&adapter->link);
|
spa_list_remove(&adapter->link);
|
||||||
|
|
@ -2774,7 +2758,6 @@ bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const stru
|
||||||
{ SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM_DUPLEX, SPA_BT_FEATURE_A2DP_DUPLEX },
|
{ SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM_DUPLEX, SPA_BT_FEATURE_A2DP_DUPLEX },
|
||||||
};
|
};
|
||||||
bool is_a2dp = codec->kind == MEDIA_CODEC_A2DP;
|
bool is_a2dp = codec->kind == MEDIA_CODEC_A2DP;
|
||||||
bool is_bap = codec->kind == MEDIA_CODEC_BAP;
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
codec_target_profile = get_codec_target_profile(monitor, codec);
|
codec_target_profile = get_codec_target_profile(monitor, codec);
|
||||||
|
|
@ -2816,8 +2799,7 @@ bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const stru
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (media_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len,
|
if (media_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len,
|
||||||
&ep->monitor->default_audio_info,
|
&ep->monitor->default_audio_info, &monitor->global_settings))
|
||||||
get_device_codec_settings(device, is_bap)))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3033,31 +3015,20 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
|
||||||
}
|
}
|
||||||
else if (spa_streq(key, "Device")) {
|
else if (spa_streq(key, "Device")) {
|
||||||
struct spa_bt_device *device;
|
struct spa_bt_device *device;
|
||||||
struct spa_bt_adapter *adapter;
|
|
||||||
|
|
||||||
device = spa_bt_device_find(monitor, value);
|
device = spa_bt_device_find(monitor, value);
|
||||||
adapter = adapter_find(monitor, value);
|
if (device == NULL)
|
||||||
|
goto next;
|
||||||
|
|
||||||
if (device != NULL) {
|
|
||||||
spa_log_debug(monitor->log, "remote_endpoint %p: device -> %p", remote_endpoint, device);
|
spa_log_debug(monitor->log, "remote_endpoint %p: device -> %p", remote_endpoint, device);
|
||||||
|
|
||||||
if (remote_endpoint->device != device) {
|
if (remote_endpoint->device != device) {
|
||||||
if (remote_endpoint->device != NULL)
|
if (remote_endpoint->device != NULL)
|
||||||
spa_list_remove(&remote_endpoint->device_link);
|
spa_list_remove(&remote_endpoint->device_link);
|
||||||
remote_endpoint->device = device;
|
remote_endpoint->device = device;
|
||||||
|
if (device != NULL)
|
||||||
spa_list_append(&device->remote_endpoint_list, &remote_endpoint->device_link);
|
spa_list_append(&device->remote_endpoint_list, &remote_endpoint->device_link);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (adapter != NULL) {
|
|
||||||
spa_log_debug(monitor->log, "remote_endpoint %p: adapter -> %p", remote_endpoint, adapter);
|
|
||||||
|
|
||||||
if (remote_endpoint->adapter != adapter) {
|
|
||||||
if (remote_endpoint->adapter != NULL)
|
|
||||||
spa_list_remove(&remote_endpoint->adapter_link);
|
|
||||||
remote_endpoint->adapter = adapter;
|
|
||||||
spa_list_append(&adapter->remote_endpoint_list, &remote_endpoint->adapter_link);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (spa_streq(key, "Transport")) {
|
} else if (spa_streq(key, "Transport")) {
|
||||||
/* For ASHA */
|
/* For ASHA */
|
||||||
free(remote_endpoint->transport_path);
|
free(remote_endpoint->transport_path);
|
||||||
|
|
@ -3131,13 +3102,11 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
|
||||||
|
|
||||||
spa_log_debug(monitor->log, "remote_endpoint %p: %s=%"PRIu64, remote_endpoint, key, remote_endpoint->hisyncid);
|
spa_log_debug(monitor->log, "remote_endpoint %p: %s=%"PRIu64, remote_endpoint, key, remote_endpoint->hisyncid);
|
||||||
} else if (spa_streq(key, "SupportedFeatures")) {
|
} else if (spa_streq(key, "SupportedFeatures")) {
|
||||||
DBusMessageIter iter;
|
|
||||||
|
|
||||||
if (!check_iter_signature(&it[1], "a{sv}"))
|
if (!check_iter_signature(&it[1], "a{sv}"))
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
dbus_message_iter_recurse(&it[1], &iter);
|
dbus_message_iter_recurse(&it[1], &it[2]);
|
||||||
parse_supported_features(monitor, &iter, &remote_endpoint->bap_features);
|
parse_supported_features(monitor, &it[2], &remote_endpoint->bap_features);
|
||||||
} else {
|
} else {
|
||||||
unhandled:
|
unhandled:
|
||||||
spa_log_debug(monitor->log, "remote_endpoint %p: unhandled key %s", remote_endpoint, key);
|
spa_log_debug(monitor->log, "remote_endpoint %p: unhandled key %s", remote_endpoint, key);
|
||||||
|
|
@ -3193,9 +3162,6 @@ static void remote_endpoint_free(struct spa_bt_remote_endpoint *remote_endpoint)
|
||||||
if (remote_endpoint->device)
|
if (remote_endpoint->device)
|
||||||
spa_list_remove(&remote_endpoint->device_link);
|
spa_list_remove(&remote_endpoint->device_link);
|
||||||
|
|
||||||
if (remote_endpoint->adapter)
|
|
||||||
spa_list_remove(&remote_endpoint->adapter_link);
|
|
||||||
|
|
||||||
bap_features_clear(&remote_endpoint->bap_features);
|
bap_features_clear(&remote_endpoint->bap_features);
|
||||||
|
|
||||||
spa_list_remove(&remote_endpoint->link);
|
spa_list_remove(&remote_endpoint->link);
|
||||||
|
|
@ -3416,12 +3382,8 @@ int spa_bt_transport_acquire(struct spa_bt_transport *transport, bool optional)
|
||||||
|
|
||||||
if (!transport->acquired)
|
if (!transport->acquired)
|
||||||
res = spa_bt_transport_impl(transport, acquire, 0, optional);
|
res = spa_bt_transport_impl(transport, acquire, 0, optional);
|
||||||
else {
|
else
|
||||||
/* keepalive */
|
res = 0;
|
||||||
transport->acquire_refcount = 1;
|
|
||||||
spa_bt_transport_emit_state_changed(transport, transport->state, transport->state);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res >= 0) {
|
if (res >= 0) {
|
||||||
transport->acquire_refcount = 1;
|
transport->acquire_refcount = 1;
|
||||||
|
|
@ -4689,7 +4651,7 @@ static bool codec_switch_check_endpoint(struct spa_bt_remote_endpoint *ep,
|
||||||
|
|
||||||
if (!media_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len,
|
if (!media_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len,
|
||||||
&ep->device->monitor->default_audio_info,
|
&ep->device->monitor->default_audio_info,
|
||||||
get_device_codec_settings(ep->device, codec->kind == MEDIA_CODEC_BAP)))
|
&ep->device->monitor->global_settings))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (ep_profile & (SPA_BT_PROFILE_A2DP_SINK | SPA_BT_PROFILE_BAP_SINK)) {
|
if (ep_profile & (SPA_BT_PROFILE_A2DP_SINK | SPA_BT_PROFILE_BAP_SINK)) {
|
||||||
|
|
@ -6272,7 +6234,6 @@ static void configure_bis(struct spa_bt_monitor *monitor,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void configure_bcast_source(struct spa_bt_monitor *monitor,
|
static void configure_bcast_source(struct spa_bt_monitor *monitor,
|
||||||
struct spa_bt_remote_endpoint *ep,
|
|
||||||
const struct media_codec *codec,
|
const struct media_codec *codec,
|
||||||
DBusConnection *conn,
|
DBusConnection *conn,
|
||||||
const char *object_path,
|
const char *object_path,
|
||||||
|
|
@ -6281,24 +6242,8 @@ static void configure_bcast_source(struct spa_bt_monitor *monitor,
|
||||||
{
|
{
|
||||||
struct spa_bt_big *big;
|
struct spa_bt_big *big;
|
||||||
struct spa_bt_bis *bis;
|
struct spa_bt_bis *bis;
|
||||||
|
|
||||||
/* Configure each BIS from a BIG */
|
/* Configure each BIS from a BIG */
|
||||||
spa_list_for_each(big, &monitor->bcast_source_config_list, link) {
|
spa_list_for_each(big, &monitor->bcast_source_config_list, link) {
|
||||||
/* Apply per adapter configuration if BIG has an adapter value stated,
|
|
||||||
* otherwise apply the BIG config angnostically to each adapter
|
|
||||||
*/
|
|
||||||
if ((strlen(big->adapter) > 0) && (ep->adapter != NULL)) {
|
|
||||||
if (!ep->adapter->address) {
|
|
||||||
spa_log_warn(monitor->log, "this adapter is not associated with any BD address. BIG config will applied agnostically to any adapter!");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcasecmp(ep->adapter->address, big->adapter))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
spa_log_debug(monitor->log, "configuring BIG for adapter=%s", big->adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
spa_list_for_each(bis, &big->bis_list, link) {
|
spa_list_for_each(bis, &big->bis_list, link) {
|
||||||
configure_bis(monitor, codec, conn, object_path, interface_name,
|
configure_bis(monitor, codec, conn, object_path, interface_name,
|
||||||
big, bis, local_endpoint);
|
big, bis, local_endpoint);
|
||||||
|
|
@ -6422,7 +6367,7 @@ static void interface_added(struct spa_bt_monitor *monitor,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local_endpoint != NULL)
|
if (local_endpoint != NULL)
|
||||||
configure_bcast_source(monitor, ep, monitor->media_codecs[i], conn, object_path, interface_name, local_endpoint);
|
configure_bcast_source(monitor, monitor->media_codecs[i], conn, object_path, interface_name, local_endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7077,10 +7022,6 @@ static void parse_broadcast_source_config(struct spa_bt_monitor *monitor, const
|
||||||
goto parse_failed;
|
goto parse_failed;
|
||||||
memcpy(big_entry->broadcast_code, bcode, strlen(bcode));
|
memcpy(big_entry->broadcast_code, bcode, strlen(bcode));
|
||||||
spa_log_debug(monitor->log, "big_entry->broadcast_code %s", big_entry->broadcast_code);
|
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[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")) {
|
} else if (spa_streq(key, "encryption")) {
|
||||||
if (spa_json_get_bool(&it[0], &big_entry->encryption) <= 0)
|
if (spa_json_get_bool(&it[0], &big_entry->encryption) <= 0)
|
||||||
goto parse_failed;
|
goto parse_failed;
|
||||||
|
|
|
||||||
|
|
@ -1485,7 +1485,7 @@ static int set_profile(struct impl *this, uint32_t profile, enum spa_bluetooth_a
|
||||||
profiles = this->bt_dev->profiles & SPA_BT_PROFILE_BAP_DUPLEX;
|
profiles = this->bt_dev->profiles & SPA_BT_PROFILE_BAP_DUPLEX;
|
||||||
break;
|
break;
|
||||||
case DEVICE_PROFILE_A2DP:
|
case DEVICE_PROFILE_A2DP:
|
||||||
profiles = this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_DUPLEX;
|
profiles = this->bt_dev->profiles & SPA_BT_PROFILE_A2DP_DUPLEX;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
profiles = 0;
|
profiles = 0;
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,6 @@ extern "C" {
|
||||||
#define PROFILE_HFP_HF "/Profile/HFPHF"
|
#define PROFILE_HFP_HF "/Profile/HFPHF"
|
||||||
|
|
||||||
#define HSP_HS_DEFAULT_CHANNEL 3
|
#define HSP_HS_DEFAULT_CHANNEL 3
|
||||||
#define HFP_SCO_DEFAULT_DATAPATH 0
|
|
||||||
|
|
||||||
#define SOURCE_ID_BLUETOOTH 0x1 /* Bluetooth SIG */
|
#define SOURCE_ID_BLUETOOTH 0x1 /* Bluetooth SIG */
|
||||||
#define SOURCE_ID_USB 0x2 /* USB Implementer's Forum */
|
#define SOURCE_ID_USB 0x2 /* USB Implementer's Forum */
|
||||||
|
|
@ -379,7 +378,6 @@ struct spa_bt_adapter {
|
||||||
unsigned int has_media1_interface:1;
|
unsigned int has_media1_interface:1;
|
||||||
unsigned int le_audio_bcast_supported:1;
|
unsigned int le_audio_bcast_supported:1;
|
||||||
unsigned int tx_timestamping_supported:1;
|
unsigned int tx_timestamping_supported:1;
|
||||||
struct spa_list remote_endpoint_list;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum spa_bt_form_factor {
|
enum spa_bt_form_factor {
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,6 @@ struct spa_fga_descriptor {
|
||||||
|
|
||||||
void (*connect_port) (void *instance, unsigned long port, void *data);
|
void (*connect_port) (void *instance, unsigned long port, void *data);
|
||||||
void (*control_changed) (void *instance);
|
void (*control_changed) (void *instance);
|
||||||
void (*control_sync) (void *instance);
|
|
||||||
|
|
||||||
void (*activate) (void *instance);
|
void (*activate) (void *instance);
|
||||||
void (*deactivate) (void *instance);
|
void (*deactivate) (void *instance);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
#include <spa/utils/string.h>
|
#include <spa/utils/string.h>
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
#include <spa/support/cpu.h>
|
#include <spa/support/cpu.h>
|
||||||
#include <spa/support/loop.h>
|
|
||||||
#include <spa/support/plugin-loader.h>
|
#include <spa/support/plugin-loader.h>
|
||||||
#include <spa/param/latency-utils.h>
|
#include <spa/param/latency-utils.h>
|
||||||
#include <spa/param/tag-utils.h>
|
#include <spa/param/tag-utils.h>
|
||||||
|
|
@ -81,6 +80,7 @@ struct descriptor {
|
||||||
unsigned long *output;
|
unsigned long *output;
|
||||||
unsigned long *control;
|
unsigned long *control;
|
||||||
unsigned long *notify;
|
unsigned long *notify;
|
||||||
|
float *default_control;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct port {
|
struct port {
|
||||||
|
|
@ -94,9 +94,6 @@ struct port {
|
||||||
uint32_t n_links;
|
uint32_t n_links;
|
||||||
uint32_t external;
|
uint32_t external;
|
||||||
|
|
||||||
bool control_initialized;
|
|
||||||
|
|
||||||
float control_current;
|
|
||||||
float control_data[MAX_HNDL];
|
float control_data[MAX_HNDL];
|
||||||
float *audio_data[MAX_HNDL];
|
float *audio_data[MAX_HNDL];
|
||||||
void *audio_mem[MAX_HNDL];
|
void *audio_mem[MAX_HNDL];
|
||||||
|
|
@ -196,9 +193,6 @@ struct graph {
|
||||||
|
|
||||||
struct volume volume[2];
|
struct volume volume[2];
|
||||||
|
|
||||||
uint32_t default_inputs;
|
|
||||||
uint32_t default_outputs;
|
|
||||||
|
|
||||||
uint32_t n_inputs;
|
uint32_t n_inputs;
|
||||||
uint32_t n_outputs;
|
uint32_t n_outputs;
|
||||||
uint32_t inputs_position[MAX_CHANNELS];
|
uint32_t inputs_position[MAX_CHANNELS];
|
||||||
|
|
@ -222,7 +216,6 @@ struct impl {
|
||||||
struct spa_cpu *cpu;
|
struct spa_cpu *cpu;
|
||||||
struct spa_fga_dsp *dsp;
|
struct spa_fga_dsp *dsp;
|
||||||
struct spa_plugin_loader *loader;
|
struct spa_plugin_loader *loader;
|
||||||
struct spa_loop *data_loop;
|
|
||||||
|
|
||||||
uint64_t info_all;
|
uint64_t info_all;
|
||||||
struct spa_filter_graph_info info;
|
struct spa_filter_graph_info info;
|
||||||
|
|
@ -264,23 +257,16 @@ static void emit_filter_graph_info(struct impl *impl, bool full)
|
||||||
impl->info.change_mask = impl->info_all;
|
impl->info.change_mask = impl->info_all;
|
||||||
if (impl->info.change_mask || full) {
|
if (impl->info.change_mask || full) {
|
||||||
char n_inputs[64], n_outputs[64], latency[64];
|
char n_inputs[64], n_outputs[64], latency[64];
|
||||||
char n_default_inputs[64], n_default_outputs[64];
|
|
||||||
struct spa_dict_item items[6];
|
struct spa_dict_item items[6];
|
||||||
struct spa_dict dict = SPA_DICT(items, 0);
|
struct spa_dict dict = SPA_DICT(items, 0);
|
||||||
char in_pos[MAX_CHANNELS * 8];
|
char in_pos[MAX_CHANNELS * 8];
|
||||||
char out_pos[MAX_CHANNELS * 8];
|
char out_pos[MAX_CHANNELS * 8];
|
||||||
|
|
||||||
/* these are the current graph inputs/outputs */
|
|
||||||
snprintf(n_inputs, sizeof(n_inputs), "%d", impl->graph.n_inputs);
|
snprintf(n_inputs, sizeof(n_inputs), "%d", impl->graph.n_inputs);
|
||||||
snprintf(n_outputs, sizeof(n_outputs), "%d", impl->graph.n_outputs);
|
snprintf(n_outputs, sizeof(n_outputs), "%d", impl->graph.n_outputs);
|
||||||
/* these are the default number of graph inputs/outputs */
|
|
||||||
snprintf(n_default_inputs, sizeof(n_default_inputs), "%d", impl->graph.default_inputs);
|
|
||||||
snprintf(n_default_outputs, sizeof(n_default_outputs), "%d", impl->graph.default_outputs);
|
|
||||||
|
|
||||||
items[dict.n_items++] = SPA_DICT_ITEM("n_inputs", n_inputs);
|
items[dict.n_items++] = SPA_DICT_ITEM("n_inputs", n_inputs);
|
||||||
items[dict.n_items++] = SPA_DICT_ITEM("n_outputs", n_outputs);
|
items[dict.n_items++] = SPA_DICT_ITEM("n_outputs", n_outputs);
|
||||||
items[dict.n_items++] = SPA_DICT_ITEM("n_default_inputs", n_default_inputs);
|
|
||||||
items[dict.n_items++] = SPA_DICT_ITEM("n_default_outputs", n_default_outputs);
|
|
||||||
if (graph->n_inputs_position) {
|
if (graph->n_inputs_position) {
|
||||||
print_channels(in_pos, sizeof(in_pos),
|
print_channels(in_pos, sizeof(in_pos),
|
||||||
graph->n_inputs_position, graph->inputs_position);
|
graph->n_inputs_position, graph->inputs_position);
|
||||||
|
|
@ -353,6 +339,12 @@ static int impl_process(void *object,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static float get_default(struct impl *impl, struct descriptor *desc, uint32_t p)
|
||||||
|
{
|
||||||
|
struct spa_fga_port *port = &desc->desc->ports[p];
|
||||||
|
return port->def;
|
||||||
|
}
|
||||||
|
|
||||||
static struct node *find_node(struct graph *graph, const char *name)
|
static struct node *find_node(struct graph *graph, const char *name)
|
||||||
{
|
{
|
||||||
struct node *node;
|
struct node *node;
|
||||||
|
|
@ -441,20 +433,6 @@ static struct port *find_port(struct node *node, const char *name, int descripto
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_ranges(struct impl *impl, struct spa_fga_port *p,
|
|
||||||
float *def, float *min, float *max)
|
|
||||||
{
|
|
||||||
uint32_t rate = impl->rate ? impl->rate : DEFAULT_RATE;
|
|
||||||
*def = p->def;
|
|
||||||
*min = p->min;
|
|
||||||
*max = p->max;
|
|
||||||
if (p->hint & SPA_FGA_HINT_SAMPLE_RATE) {
|
|
||||||
*def *= rate;
|
|
||||||
*min *= rate;
|
|
||||||
*max *= rate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int impl_enum_prop_info(void *object, uint32_t idx, struct spa_pod_builder *b,
|
static int impl_enum_prop_info(void *object, uint32_t idx, struct spa_pod_builder *b,
|
||||||
struct spa_pod **param)
|
struct spa_pod **param)
|
||||||
{
|
{
|
||||||
|
|
@ -469,6 +447,7 @@ static int impl_enum_prop_info(void *object, uint32_t idx, struct spa_pod_builde
|
||||||
struct spa_fga_port *p;
|
struct spa_fga_port *p;
|
||||||
float def, min, max;
|
float def, min, max;
|
||||||
char name[512];
|
char name[512];
|
||||||
|
uint32_t rate = impl->rate ? impl->rate : DEFAULT_RATE;
|
||||||
|
|
||||||
if (idx >= graph->n_control)
|
if (idx >= graph->n_control)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -479,7 +458,15 @@ static int impl_enum_prop_info(void *object, uint32_t idx, struct spa_pod_builde
|
||||||
d = desc->desc;
|
d = desc->desc;
|
||||||
p = &d->ports[port->p];
|
p = &d->ports[port->p];
|
||||||
|
|
||||||
get_ranges(impl, p, &def, &min, &max);
|
if (p->hint & SPA_FGA_HINT_SAMPLE_RATE) {
|
||||||
|
def = p->def * rate;
|
||||||
|
min = p->min * rate;
|
||||||
|
max = p->max * rate;
|
||||||
|
} else {
|
||||||
|
def = p->def;
|
||||||
|
min = p->min;
|
||||||
|
max = p->max;
|
||||||
|
}
|
||||||
|
|
||||||
if (node->name[0] != '\0')
|
if (node->name[0] != '\0')
|
||||||
snprintf(name, sizeof(name), "%s:%s", node->name, p->name);
|
snprintf(name, sizeof(name), "%s:%s", node->name, p->name);
|
||||||
|
|
@ -578,58 +565,41 @@ static int impl_get_props(void *object, struct spa_pod_builder *b, struct spa_po
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int port_id_set_control_value(struct port *port, uint32_t id, float value)
|
static int port_set_control_value(struct port *port, float *value, uint32_t id)
|
||||||
{
|
{
|
||||||
struct node *node = port->node;
|
struct node *node = port->node;
|
||||||
struct impl *impl = node->graph->impl;
|
struct impl *impl = node->graph->impl;
|
||||||
|
|
||||||
struct descriptor *desc = node->desc;
|
struct descriptor *desc = node->desc;
|
||||||
struct spa_fga_port *p = &desc->desc->ports[port->p];
|
|
||||||
float old;
|
float old;
|
||||||
bool changed;
|
bool changed;
|
||||||
|
|
||||||
old = port->control_data[id];
|
old = port->control_data[id];
|
||||||
port->control_data[id] = value;
|
port->control_data[id] = value ? *value : desc->default_control[port->idx];
|
||||||
|
|
||||||
spa_log_info(impl->log, "control %d %d ('%s') from %f to %f", port->idx, id,
|
spa_log_info(impl->log, "control %d %d ('%s') from %f to %f", port->idx, id,
|
||||||
p->name, old, value);
|
desc->desc->ports[port->p].name, old, port->control_data[id]);
|
||||||
|
|
||||||
changed = old != port->control_data[id];
|
changed = old != port->control_data[id];
|
||||||
node->control_changed |= changed;
|
node->control_changed |= changed;
|
||||||
|
|
||||||
return changed ? 1 : 0;
|
return changed ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int port_set_control_value(struct port *port, float *value)
|
|
||||||
{
|
|
||||||
struct node *node = port->node;
|
|
||||||
struct impl *impl = node->graph->impl;
|
|
||||||
struct spa_fga_port *p;
|
|
||||||
float v, def, min, max;
|
|
||||||
uint32_t i;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
p = &node->desc->desc->ports[port->p];
|
|
||||||
get_ranges(impl, p, &def, &min, &max);
|
|
||||||
v = SPA_CLAMP(value ? *value : def, min, max);
|
|
||||||
|
|
||||||
port->control_current = v;
|
|
||||||
port->control_initialized = true;
|
|
||||||
|
|
||||||
for (i = 0; i < node->n_hndl; i++)
|
|
||||||
count += port_id_set_control_value(port, i, v);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int set_control_value(struct node *node, const char *name, float *value)
|
static int set_control_value(struct node *node, const char *name, float *value)
|
||||||
{
|
{
|
||||||
struct port *port;
|
struct port *port;
|
||||||
|
int count = 0;
|
||||||
|
uint32_t i, n_hndl;
|
||||||
|
|
||||||
port = find_port(node, name, SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL);
|
port = find_port(node, name, SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL);
|
||||||
if (port == NULL)
|
if (port == NULL)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
return port_set_control_value(port, value);
|
/* if we don't have any instances yet, set the first control value, we will
|
||||||
|
* copy to other instances later */
|
||||||
|
n_hndl = SPA_MAX(1u, port->node->n_hndl);
|
||||||
|
for (i = 0; i < n_hndl; i++)
|
||||||
|
count += port_set_control_value(port, value, i);
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_params(struct graph *graph, const struct spa_pod *pod)
|
static int parse_params(struct graph *graph, const struct spa_pod *pod)
|
||||||
|
|
@ -700,47 +670,22 @@ static int impl_reset(void *object)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static void node_control_changed(struct node *node)
|
||||||
do_emit_node_control_sync(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
|
|
||||||
size_t size, void *user_data)
|
|
||||||
{
|
{
|
||||||
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;
|
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;
|
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
spa_loop_locked(impl->data_loop, do_emit_node_control_sync, 1, NULL, 0, impl);
|
|
||||||
|
|
||||||
spa_list_for_each(node, &graph->node_list, link) {
|
|
||||||
const struct spa_fga_descriptor *d = node->desc->desc;
|
|
||||||
if (!node->control_changed)
|
if (!node->control_changed)
|
||||||
continue;
|
return;
|
||||||
if (d->control_changed != NULL) {
|
|
||||||
for (i = 0; i < node->n_hndl; i++) {
|
for (i = 0; i < node->n_hndl; i++) {
|
||||||
if (node->hndl[i] != NULL)
|
if (node->hndl[i] == NULL)
|
||||||
|
continue;
|
||||||
|
if (d->control_changed)
|
||||||
d->control_changed(node->hndl[i]);
|
d->control_changed(node->hndl[i]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
node->control_changed = false;
|
node->control_changed = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static int sync_volume(struct graph *graph, struct volume *vol)
|
static int sync_volume(struct graph *graph, struct volume *vol)
|
||||||
{
|
{
|
||||||
|
|
@ -761,7 +706,7 @@ static int sync_volume(struct graph *graph, struct volume *vol)
|
||||||
v = v * (vol->max[n_port] - vol->min[n_port]) + vol->min[n_port];
|
v = v * (vol->max[n_port] - vol->min[n_port]) + vol->min[n_port];
|
||||||
|
|
||||||
n_hndl = SPA_MAX(1u, p->node->n_hndl);
|
n_hndl = SPA_MAX(1u, p->node->n_hndl);
|
||||||
res += port_id_set_control_value(p, i % n_hndl, v);
|
res += port_set_control_value(p, &v, i % n_hndl);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -853,7 +798,11 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru
|
||||||
spa_pod_dynamic_builder_clean(&b);
|
spa_pod_dynamic_builder_clean(&b);
|
||||||
|
|
||||||
if (changed > 0) {
|
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);
|
spa_filter_graph_emit_props_changed(&impl->hooks, SPA_DIRECTION_INPUT);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -976,6 +925,7 @@ static void descriptor_unref(struct descriptor *desc)
|
||||||
free(desc->input);
|
free(desc->input);
|
||||||
free(desc->output);
|
free(desc->output);
|
||||||
free(desc->control);
|
free(desc->control);
|
||||||
|
free(desc->default_control);
|
||||||
free(desc->notify);
|
free(desc->notify);
|
||||||
free(desc);
|
free(desc);
|
||||||
}
|
}
|
||||||
|
|
@ -986,7 +936,7 @@ static struct descriptor *descriptor_load(struct impl *impl, const char *type,
|
||||||
struct plugin *pl;
|
struct plugin *pl;
|
||||||
struct descriptor *desc;
|
struct descriptor *desc;
|
||||||
const struct spa_fga_descriptor *d;
|
const struct spa_fga_descriptor *d;
|
||||||
uint32_t n_input, n_output, n_control, n_notify;
|
uint32_t i, n_input, n_output, n_control, n_notify;
|
||||||
unsigned long p;
|
unsigned long p;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
|
@ -1040,6 +990,7 @@ static struct descriptor *descriptor_load(struct impl *impl, const char *type,
|
||||||
desc->input = calloc(n_input, sizeof(unsigned long));
|
desc->input = calloc(n_input, sizeof(unsigned long));
|
||||||
desc->output = calloc(n_output, sizeof(unsigned long));
|
desc->output = calloc(n_output, sizeof(unsigned long));
|
||||||
desc->control = calloc(n_control, sizeof(unsigned long));
|
desc->control = calloc(n_control, sizeof(unsigned long));
|
||||||
|
desc->default_control = calloc(n_control, sizeof(float));
|
||||||
desc->notify = calloc(n_notify, sizeof(unsigned long));
|
desc->notify = calloc(n_notify, sizeof(unsigned long));
|
||||||
|
|
||||||
for (p = 0; p < d->n_ports; p++) {
|
for (p = 0; p < d->n_ports; p++) {
|
||||||
|
|
@ -1069,6 +1020,17 @@ static struct descriptor *descriptor_load(struct impl *impl, const char *type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (desc->n_input == 0 && desc->n_output == 0 && desc->n_control == 0 && desc->n_notify == 0) {
|
||||||
|
spa_log_error(impl->log, "plugin has no input and no output ports");
|
||||||
|
res = -ENOTSUP;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
for (i = 0; i < desc->n_control; i++) {
|
||||||
|
p = desc->control[i];
|
||||||
|
desc->default_control[i] = get_default(impl, desc, p);
|
||||||
|
spa_log_info(impl->log, "control %d ('%s') default to %f", i,
|
||||||
|
d->ports[p].name, desc->default_control[i]);
|
||||||
|
}
|
||||||
spa_list_append(&pl->descriptor_list, &desc->link);
|
spa_list_append(&pl->descriptor_list, &desc->link);
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
|
|
@ -1448,6 +1410,7 @@ static int load_node(struct graph *graph, struct spa_json *json)
|
||||||
port->external = SPA_ID_INVALID;
|
port->external = SPA_ID_INVALID;
|
||||||
port->p = desc->control[i];
|
port->p = desc->control[i];
|
||||||
spa_list_init(&port->link_list);
|
spa_list_init(&port->link_list);
|
||||||
|
port->control_data[0] = desc->default_control[i];
|
||||||
}
|
}
|
||||||
for (i = 0; i < desc->n_notify; i++) {
|
for (i = 0; i < desc->n_notify; i++) {
|
||||||
struct port *port = &node->notify_port[i];
|
struct port *port = &node->notify_port[i];
|
||||||
|
|
@ -1718,10 +1681,10 @@ static int impl_activate(void *object, const struct spa_dict *props)
|
||||||
for (i = 0; i < node->n_hndl; i++) {
|
for (i = 0; i < node->n_hndl; i++) {
|
||||||
if (d->activate)
|
if (d->activate)
|
||||||
d->activate(node->hndl[i]);
|
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 */
|
/* calculate latency */
|
||||||
sort_reset(graph);
|
sort_reset(graph);
|
||||||
while ((node = sort_next_node(graph)) != NULL) {
|
while ((node = sort_next_node(graph)) != NULL) {
|
||||||
|
|
@ -1821,7 +1784,7 @@ static int setup_graph(struct graph *graph)
|
||||||
struct port *port;
|
struct port *port;
|
||||||
struct graph_port *gp;
|
struct graph_port *gp;
|
||||||
struct graph_hndl *gh;
|
struct graph_hndl *gh;
|
||||||
uint32_t i, j, n, n_input, n_output, n_hndl = 0, n_out_hndl;
|
uint32_t i, j, n, n_input, n_output, n_hndl = 0;
|
||||||
int res;
|
int res;
|
||||||
struct descriptor *desc;
|
struct descriptor *desc;
|
||||||
const struct spa_fga_descriptor *d;
|
const struct spa_fga_descriptor *d;
|
||||||
|
|
@ -1833,8 +1796,19 @@ static int setup_graph(struct graph *graph)
|
||||||
first = spa_list_first(&graph->node_list, struct node, link);
|
first = spa_list_first(&graph->node_list, struct node, link);
|
||||||
last = spa_list_last(&graph->node_list, struct node, link);
|
last = spa_list_last(&graph->node_list, struct node, link);
|
||||||
|
|
||||||
n_input = graph->default_inputs;
|
/* calculate the number of inputs and outputs into the graph.
|
||||||
n_output = graph->default_outputs;
|
* If we have a list of inputs/outputs, just use them. Otherwise
|
||||||
|
* we count all input ports of the first node and all output
|
||||||
|
* ports of the last node */
|
||||||
|
if (graph->n_input_names != 0)
|
||||||
|
n_input = graph->n_input_names;
|
||||||
|
else
|
||||||
|
n_input = first->desc->n_input;
|
||||||
|
|
||||||
|
if (graph->n_output_names != 0)
|
||||||
|
n_output = graph->n_output_names;
|
||||||
|
else
|
||||||
|
n_output = last->desc->n_output;
|
||||||
|
|
||||||
/* we allow unconnected ports when not explicitly given and the nodes support
|
/* we allow unconnected ports when not explicitly given and the nodes support
|
||||||
* NULL data */
|
* NULL data */
|
||||||
|
|
@ -1842,11 +1816,16 @@ static int setup_graph(struct graph *graph)
|
||||||
SPA_FLAG_IS_SET(first->desc->desc->flags, SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA) &&
|
SPA_FLAG_IS_SET(first->desc->desc->flags, SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA) &&
|
||||||
SPA_FLAG_IS_SET(last->desc->desc->flags, SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA);
|
SPA_FLAG_IS_SET(last->desc->desc->flags, SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA);
|
||||||
|
|
||||||
if (n_input == 0)
|
if (n_input == 0) {
|
||||||
n_input = n_output;
|
spa_log_error(impl->log, "no inputs");
|
||||||
if (n_output == 0)
|
res = -EINVAL;
|
||||||
n_output = n_input;
|
goto error;
|
||||||
|
}
|
||||||
|
if (n_output == 0) {
|
||||||
|
spa_log_error(impl->log, "no outputs");
|
||||||
|
res = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
if (graph->n_inputs == 0)
|
if (graph->n_inputs == 0)
|
||||||
graph->n_inputs = impl->info.n_inputs;
|
graph->n_inputs = impl->info.n_inputs;
|
||||||
if (graph->n_inputs == 0)
|
if (graph->n_inputs == 0)
|
||||||
|
|
@ -1857,14 +1836,12 @@ static int setup_graph(struct graph *graph)
|
||||||
|
|
||||||
/* compare to the requested number of inputs and duplicate the
|
/* compare to the requested number of inputs and duplicate the
|
||||||
* graph n_hndl times when needed. */
|
* graph n_hndl times when needed. */
|
||||||
n_hndl = n_input ? graph->n_inputs / n_input : 1;
|
n_hndl = graph->n_inputs / n_input;
|
||||||
|
|
||||||
if (graph->n_outputs == 0)
|
if (graph->n_outputs == 0)
|
||||||
graph->n_outputs = n_output * n_hndl;
|
graph->n_outputs = n_output * n_hndl;
|
||||||
|
|
||||||
n_out_hndl = n_output ? graph->n_outputs / n_output : 1;
|
if (n_hndl != graph->n_outputs / n_output) {
|
||||||
|
|
||||||
if (n_hndl != n_out_hndl) {
|
|
||||||
spa_log_error(impl->log, "invalid ports. The input stream has %1$d ports and "
|
spa_log_error(impl->log, "invalid ports. The input stream has %1$d ports and "
|
||||||
"the filter has %2$d inputs. The output stream has %3$d ports "
|
"the filter has %2$d inputs. The output stream has %3$d ports "
|
||||||
"and the filter has %4$d outputs. input:%1$d / input:%2$d != "
|
"and the filter has %4$d outputs. input:%1$d / input:%2$d != "
|
||||||
|
|
@ -2052,9 +2029,11 @@ static int setup_graph(struct graph *graph)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < desc->n_control; i++) {
|
for (i = 0; i < desc->n_control; i++) {
|
||||||
|
/* any default values for the controls are set in the first instance
|
||||||
|
* of the control data. Duplicate this to the other instances now. */
|
||||||
struct port *port = &node->control_port[i];
|
struct port *port = &node->control_port[i];
|
||||||
port_set_control_value(port,
|
for (j = 1; j < n_hndl; j++)
|
||||||
port->control_initialized ? &port->control_current : NULL);
|
port->control_data[j] = port->control_data[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res = 0;
|
res = 0;
|
||||||
|
|
@ -2104,7 +2083,6 @@ static int load_graph(struct graph *graph, const struct spa_dict *props)
|
||||||
struct spa_json inputs, outputs, *pinputs = NULL, *poutputs = NULL;
|
struct spa_json inputs, outputs, *pinputs = NULL, *poutputs = NULL;
|
||||||
struct spa_json ivolumes, ovolumes, *pivolumes = NULL, *povolumes = NULL;
|
struct spa_json ivolumes, ovolumes, *pivolumes = NULL, *povolumes = NULL;
|
||||||
struct spa_json nodes, *pnodes = NULL, links, *plinks = NULL;
|
struct spa_json nodes, *pnodes = NULL, links, *plinks = NULL;
|
||||||
struct node *first, *last;
|
|
||||||
const char *json, *val;
|
const char *json, *val;
|
||||||
char key[256];
|
char key[256];
|
||||||
int res, len;
|
int res, len;
|
||||||
|
|
@ -2254,25 +2232,6 @@ static int load_graph(struct graph *graph, const struct spa_dict *props)
|
||||||
}
|
}
|
||||||
if ((res = setup_graph_controls(graph)) < 0)
|
if ((res = setup_graph_controls(graph)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
first = spa_list_first(&graph->node_list, struct node, link);
|
|
||||||
last = spa_list_last(&graph->node_list, struct node, link);
|
|
||||||
|
|
||||||
/* calculate the number of inputs and outputs into the graph.
|
|
||||||
* If we have a list of inputs/outputs, just use them. Otherwise
|
|
||||||
* we count all input ports of the first node and all output
|
|
||||||
* ports of the last node */
|
|
||||||
if (graph->n_input_names != 0)
|
|
||||||
graph->default_inputs = graph->n_input_names;
|
|
||||||
else
|
|
||||||
graph->default_inputs = first->desc->n_input;
|
|
||||||
|
|
||||||
if (graph->n_output_names != 0)
|
|
||||||
graph->default_outputs = graph->n_output_names;
|
|
||||||
else
|
|
||||||
graph->default_outputs = last->desc->n_output;
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2369,7 +2328,6 @@ impl_init(const struct spa_handle_factory *factory,
|
||||||
spa_log_topic_init(impl->log, &log_topic);
|
spa_log_topic_init(impl->log, &log_topic);
|
||||||
|
|
||||||
impl->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
|
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->max_align = spa_cpu_get_max_align(impl->cpu);
|
||||||
|
|
||||||
impl->dsp = spa_fga_dsp_new(impl->cpu ? spa_cpu_get_flags(impl->cpu) : 0);
|
impl->dsp = spa_fga_dsp_new(impl->cpu ? spa_cpu_get_flags(impl->cpu) : 0);
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,12 @@
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
#include <spa/utils/result.h>
|
#include <spa/utils/result.h>
|
||||||
#include <spa/utils/cleanup.h>
|
#include <spa/utils/cleanup.h>
|
||||||
#include <spa/support/cpu.h>
|
#include <spa/support/cpu.h>
|
||||||
#include <spa/support/log.h>
|
#include <spa/support/log.h>
|
||||||
#include <spa/support/loop.h>
|
|
||||||
#include <spa/plugins/audioconvert/resample.h>
|
#include <spa/plugins/audioconvert/resample.h>
|
||||||
#include <spa/debug/log.h>
|
#include <spa/debug/log.h>
|
||||||
|
|
||||||
|
|
@ -543,12 +541,7 @@ static void bq_run(void *Instance, unsigned long samples)
|
||||||
struct biquad *bq = &impl->bq;
|
struct biquad *bq = &impl->bq;
|
||||||
float *out = impl->port[0];
|
float *out = impl->port[0];
|
||||||
float *in = impl->port[1];
|
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) {
|
if (impl->type == BQ_NONE) {
|
||||||
float b0, b1, b2, a0, a1, a2;
|
float b0, b1, b2, a0, a1, a2;
|
||||||
b0 = impl->port[5][0];
|
b0 = impl->port[5][0];
|
||||||
|
|
@ -568,6 +561,7 @@ static void bq_control_sync(void * Instance)
|
||||||
if (impl->freq != freq || impl->Q != Q || impl->gain != gain)
|
if (impl->freq != freq || impl->Q != Q || impl->gain != gain)
|
||||||
bq_freq_update(impl, impl->type, freq, Q, 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 */
|
/** bq_lowpass */
|
||||||
|
|
@ -579,7 +573,6 @@ static const struct spa_fga_descriptor bq_lowpass_desc = {
|
||||||
|
|
||||||
.instantiate = bq_instantiate,
|
.instantiate = bq_instantiate,
|
||||||
.connect_port = builtin_connect_port,
|
.connect_port = builtin_connect_port,
|
||||||
.control_sync = bq_control_sync,
|
|
||||||
.activate = bq_activate,
|
.activate = bq_activate,
|
||||||
.run = bq_run,
|
.run = bq_run,
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
|
|
@ -594,7 +587,6 @@ static const struct spa_fga_descriptor bq_highpass_desc = {
|
||||||
|
|
||||||
.instantiate = bq_instantiate,
|
.instantiate = bq_instantiate,
|
||||||
.connect_port = builtin_connect_port,
|
.connect_port = builtin_connect_port,
|
||||||
.control_sync = bq_control_sync,
|
|
||||||
.activate = bq_activate,
|
.activate = bq_activate,
|
||||||
.run = bq_run,
|
.run = bq_run,
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
|
|
@ -609,7 +601,6 @@ static const struct spa_fga_descriptor bq_bandpass_desc = {
|
||||||
|
|
||||||
.instantiate = bq_instantiate,
|
.instantiate = bq_instantiate,
|
||||||
.connect_port = builtin_connect_port,
|
.connect_port = builtin_connect_port,
|
||||||
.control_sync = bq_control_sync,
|
|
||||||
.activate = bq_activate,
|
.activate = bq_activate,
|
||||||
.run = bq_run,
|
.run = bq_run,
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
|
|
@ -624,7 +615,6 @@ static const struct spa_fga_descriptor bq_lowshelf_desc = {
|
||||||
|
|
||||||
.instantiate = bq_instantiate,
|
.instantiate = bq_instantiate,
|
||||||
.connect_port = builtin_connect_port,
|
.connect_port = builtin_connect_port,
|
||||||
.control_sync = bq_control_sync,
|
|
||||||
.activate = bq_activate,
|
.activate = bq_activate,
|
||||||
.run = bq_run,
|
.run = bq_run,
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
|
|
@ -639,7 +629,6 @@ static const struct spa_fga_descriptor bq_highshelf_desc = {
|
||||||
|
|
||||||
.instantiate = bq_instantiate,
|
.instantiate = bq_instantiate,
|
||||||
.connect_port = builtin_connect_port,
|
.connect_port = builtin_connect_port,
|
||||||
.control_sync = bq_control_sync,
|
|
||||||
.activate = bq_activate,
|
.activate = bq_activate,
|
||||||
.run = bq_run,
|
.run = bq_run,
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
|
|
@ -654,7 +643,6 @@ static const struct spa_fga_descriptor bq_peaking_desc = {
|
||||||
|
|
||||||
.instantiate = bq_instantiate,
|
.instantiate = bq_instantiate,
|
||||||
.connect_port = builtin_connect_port,
|
.connect_port = builtin_connect_port,
|
||||||
.control_sync = bq_control_sync,
|
|
||||||
.activate = bq_activate,
|
.activate = bq_activate,
|
||||||
.run = bq_run,
|
.run = bq_run,
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
|
|
@ -669,7 +657,6 @@ static const struct spa_fga_descriptor bq_notch_desc = {
|
||||||
|
|
||||||
.instantiate = bq_instantiate,
|
.instantiate = bq_instantiate,
|
||||||
.connect_port = builtin_connect_port,
|
.connect_port = builtin_connect_port,
|
||||||
.control_sync = bq_control_sync,
|
|
||||||
.activate = bq_activate,
|
.activate = bq_activate,
|
||||||
.run = bq_run,
|
.run = bq_run,
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
|
|
@ -685,7 +672,6 @@ static const struct spa_fga_descriptor bq_allpass_desc = {
|
||||||
|
|
||||||
.instantiate = bq_instantiate,
|
.instantiate = bq_instantiate,
|
||||||
.connect_port = builtin_connect_port,
|
.connect_port = builtin_connect_port,
|
||||||
.control_sync = bq_control_sync,
|
|
||||||
.activate = bq_activate,
|
.activate = bq_activate,
|
||||||
.run = bq_run,
|
.run = bq_run,
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
|
|
@ -700,7 +686,6 @@ static const struct spa_fga_descriptor bq_raw_desc = {
|
||||||
|
|
||||||
.instantiate = bq_instantiate,
|
.instantiate = bq_instantiate,
|
||||||
.connect_port = builtin_connect_port,
|
.connect_port = builtin_connect_port,
|
||||||
.control_sync = bq_control_sync,
|
|
||||||
.activate = bq_activate,
|
.activate = bq_activate,
|
||||||
.run = bq_run,
|
.run = bq_run,
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
|
|
@ -3130,136 +3115,6 @@ static const struct spa_fga_descriptor noisegate_desc = {
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* busy */
|
|
||||||
struct busy_impl {
|
|
||||||
struct plugin *plugin;
|
|
||||||
|
|
||||||
struct spa_fga_dsp *dsp;
|
|
||||||
struct spa_log *log;
|
|
||||||
|
|
||||||
unsigned long rate;
|
|
||||||
|
|
||||||
float wait_scale;
|
|
||||||
float cpu_scale;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *busy_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor,
|
|
||||||
unsigned long SampleRate, int index, const char *config)
|
|
||||||
{
|
|
||||||
struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin);
|
|
||||||
struct busy_impl *impl;
|
|
||||||
struct spa_json it[1];
|
|
||||||
const char *val;
|
|
||||||
char key[256];
|
|
||||||
float wait_percent = 0.0f, cpu_percent = 0.0f;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if (config != NULL) {
|
|
||||||
if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) {
|
|
||||||
spa_log_error(pl->log, "busy:config must be an object");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
|
|
||||||
if (spa_streq(key, "wait-percent")) {
|
|
||||||
if (spa_json_parse_float(val, len, &wait_percent) <= 0) {
|
|
||||||
spa_log_error(pl->log, "busy:wait-percent requires a number");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else if (spa_streq(key, "cpu-percent")) {
|
|
||||||
if (spa_json_parse_float(val, len, &cpu_percent) <= 0) {
|
|
||||||
spa_log_error(pl->log, "busy:cpu-percent requires a number");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
spa_log_warn(pl->log, "busy: ignoring config key: '%s'", key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (wait_percent <= 0.0f)
|
|
||||||
wait_percent = 0.0f;
|
|
||||||
if (cpu_percent <= 0.0f)
|
|
||||||
cpu_percent = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl = calloc(1, sizeof(*impl));
|
|
||||||
if (impl == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
impl->plugin = pl;
|
|
||||||
impl->dsp = pl->dsp;
|
|
||||||
impl->log = pl->log;
|
|
||||||
impl->rate = SampleRate;
|
|
||||||
impl->wait_scale = wait_percent * SPA_NSEC_PER_SEC / (100.0f * SampleRate);
|
|
||||||
impl->cpu_scale = cpu_percent * SPA_NSEC_PER_SEC / (100.0f * SampleRate);
|
|
||||||
spa_log_info(impl->log, "wait-percent:%f cpu-percent:%f", wait_percent, cpu_percent);
|
|
||||||
|
|
||||||
return impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void busy_run(void * Instance, unsigned long SampleCount)
|
|
||||||
{
|
|
||||||
struct busy_impl *impl = Instance;
|
|
||||||
struct timespec ts;
|
|
||||||
uint64_t busy_nsec;
|
|
||||||
|
|
||||||
if (impl->wait_scale > 0.0f) {
|
|
||||||
busy_nsec = (uint64_t)(impl->wait_scale * SampleCount);
|
|
||||||
ts.tv_sec = busy_nsec / SPA_NSEC_PER_SEC;
|
|
||||||
ts.tv_nsec = busy_nsec % SPA_NSEC_PER_SEC;
|
|
||||||
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
|
|
||||||
}
|
|
||||||
if (impl->cpu_scale > 0.0f) {
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
busy_nsec = SPA_TIMESPEC_TO_NSEC(&ts);
|
|
||||||
busy_nsec += (uint64_t)(impl->cpu_scale * SampleCount);
|
|
||||||
do {
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
} while ((uint64_t)SPA_TIMESPEC_TO_NSEC(&ts) < busy_nsec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct spa_fga_descriptor busy_desc = {
|
|
||||||
.name = "busy",
|
|
||||||
.flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
|
||||||
|
|
||||||
.n_ports = 0,
|
|
||||||
.ports = NULL,
|
|
||||||
|
|
||||||
.instantiate = busy_instantiate,
|
|
||||||
.connect_port = builtin_connect_port,
|
|
||||||
.run = busy_run,
|
|
||||||
.cleanup = builtin_cleanup,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* null */
|
|
||||||
static void null_run(void * Instance, unsigned long SampleCount)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct spa_fga_port null_ports[] = {
|
|
||||||
{ .index = 0,
|
|
||||||
.name = "In",
|
|
||||||
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
|
||||||
},
|
|
||||||
{ .index = 1,
|
|
||||||
.name = "Control",
|
|
||||||
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct spa_fga_descriptor null_desc = {
|
|
||||||
.name = "null",
|
|
||||||
.flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
|
||||||
|
|
||||||
.n_ports = SPA_N_ELEMENTS(null_ports),
|
|
||||||
.ports = null_ports,
|
|
||||||
|
|
||||||
.instantiate = builtin_instantiate,
|
|
||||||
.connect_port = builtin_connect_port,
|
|
||||||
.run = null_run,
|
|
||||||
.cleanup = builtin_cleanup,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct spa_fga_descriptor * builtin_descriptor(unsigned long Index)
|
static const struct spa_fga_descriptor * builtin_descriptor(unsigned long Index)
|
||||||
{
|
{
|
||||||
switch(Index) {
|
switch(Index) {
|
||||||
|
|
@ -3325,10 +3180,6 @@ static const struct spa_fga_descriptor * builtin_descriptor(unsigned long Index)
|
||||||
return &zeroramp_desc;
|
return &zeroramp_desc;
|
||||||
case 30:
|
case 30:
|
||||||
return &noisegate_desc;
|
return &noisegate_desc;
|
||||||
case 31:
|
|
||||||
return &busy_desc;
|
|
||||||
case 32:
|
|
||||||
return &null_desc;
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ struct spatializer_impl {
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
float *port[7];
|
float *port[7];
|
||||||
int n_samples, blocksize, tailsize;
|
int n_samples, blocksize, tailsize;
|
||||||
float gain;
|
|
||||||
float *tmp[2];
|
float *tmp[2];
|
||||||
|
|
||||||
struct MYSOFA_EASY *sofa;
|
struct MYSOFA_EASY *sofa;
|
||||||
|
|
@ -72,7 +71,6 @@ static void * spatializer_instantiate(const struct spa_fga_plugin *plugin, const
|
||||||
impl->plugin = pl;
|
impl->plugin = pl;
|
||||||
impl->dsp = pl->dsp;
|
impl->dsp = pl->dsp;
|
||||||
impl->log = pl->log;
|
impl->log = pl->log;
|
||||||
impl->gain = 1.0f;
|
|
||||||
|
|
||||||
while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
|
while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
|
||||||
if (spa_streq(key, "blocksize")) {
|
if (spa_streq(key, "blocksize")) {
|
||||||
|
|
@ -96,13 +94,6 @@ static void * spatializer_instantiate(const struct spa_fga_plugin *plugin, const
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (spa_streq(key, "gain")) {
|
|
||||||
if (spa_json_parse_float(val, len, &impl->gain) <= 0) {
|
|
||||||
spa_log_error(impl->log, "spatializer:gain requires a number");
|
|
||||||
errno = EINVAL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!filename[0]) {
|
if (!filename[0]) {
|
||||||
spa_log_error(impl->log, "spatializer:filename was not given");
|
spa_log_error(impl->log, "spatializer:filename was not given");
|
||||||
|
|
@ -177,14 +168,11 @@ static void * spatializer_instantiate(const struct spa_fga_plugin *plugin, const
|
||||||
reason = "Only sources with MC supported";
|
reason = "Only sources with MC supported";
|
||||||
errno = ENOTSUP;
|
errno = ENOTSUP;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
case MYSOFA_INTERNAL_ERROR:
|
case MYSOFA_INTERNAL_ERROR:
|
||||||
errno = EIO;
|
errno = EIO;
|
||||||
reason = "Internal error";
|
reason = "Internal error";
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
errno = ret;
|
|
||||||
reason = strerror(errno);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
spa_log_error(impl->log, "Unable to load HRTF from %s: %s (%d)", filename, reason, ret);
|
spa_log_error(impl->log, "Unable to load HRTF from %s: %s (%d)", filename, reason, ret);
|
||||||
goto error;
|
goto error;
|
||||||
|
|
@ -195,8 +183,8 @@ static void * spatializer_instantiate(const struct spa_fga_plugin *plugin, const
|
||||||
if (impl->tailsize <= 0)
|
if (impl->tailsize <= 0)
|
||||||
impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768);
|
impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768);
|
||||||
|
|
||||||
spa_log_info(impl->log, "using n_samples:%u %d:%d blocksize gain:%f sofa:%s", impl->n_samples,
|
spa_log_info(impl->log, "using n_samples:%u %d:%d blocksize sofa:%s", impl->n_samples,
|
||||||
impl->blocksize, impl->tailsize, impl->gain, filename);
|
impl->blocksize, impl->tailsize, filename);
|
||||||
|
|
||||||
impl->tmp[0] = calloc(impl->plugin->quantum_limit, sizeof(float));
|
impl->tmp[0] = calloc(impl->plugin->quantum_limit, sizeof(float));
|
||||||
impl->tmp[1] = calloc(impl->plugin->quantum_limit, sizeof(float));
|
impl->tmp[1] = calloc(impl->plugin->quantum_limit, sizeof(float));
|
||||||
|
|
@ -262,13 +250,6 @@ static void spatializer_reload(void * Instance)
|
||||||
if (impl->r_conv[2])
|
if (impl->r_conv[2])
|
||||||
convolver_free(impl->r_conv[2]);
|
convolver_free(impl->r_conv[2]);
|
||||||
|
|
||||||
if (impl->gain != 1.0f) {
|
|
||||||
for (int i = 0; i < impl->n_samples; i++) {
|
|
||||||
left_ir[i] *= impl->gain;
|
|
||||||
right_ir[i] *= impl->gain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl->l_conv[2] = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
|
impl->l_conv[2] = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
|
||||||
left_ir, impl->n_samples);
|
left_ir, impl->n_samples);
|
||||||
impl->r_conv[2] = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
|
impl->r_conv[2] = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ impl_log_logtv(void *object,
|
||||||
char timestamp[18] = {0};
|
char timestamp[18] = {0};
|
||||||
char topicstr[32] = {0};
|
char topicstr[32] = {0};
|
||||||
char filename[64] = {0};
|
char filename[64] = {0};
|
||||||
char location[1000 + RESERVED_LENGTH], *p;
|
char location[1000 + RESERVED_LENGTH], *p, *s;
|
||||||
static const char * const levels[] = { "-", "E", "W", "I", "D", "T", "*T*" };
|
static const char * const levels[] = { "-", "E", "W", "I", "D", "T", "*T*" };
|
||||||
const char *prefix = "", *suffix = "";
|
const char *prefix = "", *suffix = "";
|
||||||
int size, len;
|
int size, len;
|
||||||
|
|
@ -118,7 +118,7 @@ impl_log_logtv(void *object,
|
||||||
|
|
||||||
|
|
||||||
if (impl->line && line != 0) {
|
if (impl->line && line != 0) {
|
||||||
const char *s = strrchr(file, '/');
|
s = strrchr(file, '/');
|
||||||
spa_scnprintf(filename, sizeof(filename), "[%16.16s:%5i %s()]",
|
spa_scnprintf(filename, sizeof(filename), "[%16.16s:%5i %s()]",
|
||||||
s ? s + 1 : file, line, func);
|
s ? s + 1 : file, line, func);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,6 @@ context.modules = [
|
||||||
name = spFL
|
name = spFL
|
||||||
config = {
|
config = {
|
||||||
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
||||||
# The gain depends on the .sofa file in use
|
|
||||||
gain = 0.5
|
|
||||||
}
|
}
|
||||||
control = {
|
control = {
|
||||||
"Azimuth" = 30.0
|
"Azimuth" = 30.0
|
||||||
|
|
@ -34,7 +32,6 @@ context.modules = [
|
||||||
name = spFR
|
name = spFR
|
||||||
config = {
|
config = {
|
||||||
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
||||||
gain = 0.5
|
|
||||||
}
|
}
|
||||||
control = {
|
control = {
|
||||||
"Azimuth" = 330.0
|
"Azimuth" = 330.0
|
||||||
|
|
@ -48,7 +45,6 @@ context.modules = [
|
||||||
name = spFC
|
name = spFC
|
||||||
config = {
|
config = {
|
||||||
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
||||||
gain = 0.5
|
|
||||||
}
|
}
|
||||||
control = {
|
control = {
|
||||||
"Azimuth" = 0.0
|
"Azimuth" = 0.0
|
||||||
|
|
@ -62,7 +58,6 @@ context.modules = [
|
||||||
name = spRL
|
name = spRL
|
||||||
config = {
|
config = {
|
||||||
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
||||||
gain = 0.5
|
|
||||||
}
|
}
|
||||||
control = {
|
control = {
|
||||||
"Azimuth" = 150.0
|
"Azimuth" = 150.0
|
||||||
|
|
@ -76,7 +71,6 @@ context.modules = [
|
||||||
name = spRR
|
name = spRR
|
||||||
config = {
|
config = {
|
||||||
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
||||||
gain = 0.5
|
|
||||||
}
|
}
|
||||||
control = {
|
control = {
|
||||||
"Azimuth" = 210.0
|
"Azimuth" = 210.0
|
||||||
|
|
@ -90,7 +84,6 @@ context.modules = [
|
||||||
name = spSL
|
name = spSL
|
||||||
config = {
|
config = {
|
||||||
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
||||||
gain = 0.5
|
|
||||||
}
|
}
|
||||||
control = {
|
control = {
|
||||||
"Azimuth" = 90.0
|
"Azimuth" = 90.0
|
||||||
|
|
@ -104,7 +97,6 @@ context.modules = [
|
||||||
name = spSR
|
name = spSR
|
||||||
config = {
|
config = {
|
||||||
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
||||||
gain = 0.5
|
|
||||||
}
|
}
|
||||||
control = {
|
control = {
|
||||||
"Azimuth" = 270.0
|
"Azimuth" = 270.0
|
||||||
|
|
@ -118,7 +110,6 @@ context.modules = [
|
||||||
name = spLFE
|
name = spLFE
|
||||||
config = {
|
config = {
|
||||||
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
|
||||||
gain = 0.5
|
|
||||||
}
|
}
|
||||||
control = {
|
control = {
|
||||||
"Azimuth" = 0.0
|
"Azimuth" = 0.0
|
||||||
|
|
@ -127,32 +118,8 @@ context.modules = [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{ type = builtin label = mixer name = mixL
|
{ type = builtin label = mixer name = mixL }
|
||||||
control = {
|
{ type = builtin label = mixer name = mixR }
|
||||||
# Set individual left mixer gain if needed
|
|
||||||
#"Gain 1" = 1.0
|
|
||||||
#"Gain 2" = 1.0
|
|
||||||
#"Gain 3" = 1.0
|
|
||||||
#"Gain 4" = 1.0
|
|
||||||
#"Gain 5" = 1.0
|
|
||||||
#"Gain 6" = 1.0
|
|
||||||
#"Gain 7" = 1.0
|
|
||||||
#"Gain 8" = 1.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{ type = builtin label = mixer name = mixR
|
|
||||||
control = {
|
|
||||||
# Set individual right mixer gain if needed
|
|
||||||
#"Gain 1" = 1.0
|
|
||||||
#"Gain 2" = 1.0
|
|
||||||
#"Gain 3" = 1.0
|
|
||||||
#"Gain 4" = 1.0
|
|
||||||
#"Gain 5" = 1.0
|
|
||||||
#"Gain 6" = 1.0
|
|
||||||
#"Gain 7" = 1.0
|
|
||||||
#"Gain 8" = 1.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
links = [
|
links = [
|
||||||
# output
|
# output
|
||||||
|
|
|
||||||
|
|
@ -100,10 +100,6 @@ context.modules = [
|
||||||
}
|
}
|
||||||
flags = [ ifexists nofail ]
|
flags = [ ifexists nofail ]
|
||||||
}
|
}
|
||||||
# the graph scheduler
|
|
||||||
{ name = libpipewire-module-scheduler-v1
|
|
||||||
condition = [ { module.scheduler-v1 = !false } ]
|
|
||||||
}
|
|
||||||
|
|
||||||
# The native communication protocol.
|
# The native communication protocol.
|
||||||
{ name = libpipewire-module-protocol-native }
|
{ name = libpipewire-module-protocol-native }
|
||||||
|
|
|
||||||
|
|
@ -121,10 +121,6 @@ context.modules = [
|
||||||
flags = [ ifexists nofail ]
|
flags = [ ifexists nofail ]
|
||||||
condition = [ { module.rt = !false } ]
|
condition = [ { module.rt = !false } ]
|
||||||
}
|
}
|
||||||
# the graph scheduler
|
|
||||||
{ name = libpipewire-module-scheduler-v1
|
|
||||||
condition = [ { module.scheduler-v1 = !false } ]
|
|
||||||
}
|
|
||||||
|
|
||||||
# The native communication protocol.
|
# The native communication protocol.
|
||||||
{ name = libpipewire-module-protocol-native
|
{ name = libpipewire-module-protocol-native
|
||||||
|
|
|
||||||
46
src/examples/base64.h
Normal file
46
src/examples/base64.h
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* PipeWire */
|
||||||
|
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
|
||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
static inline 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 inline 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;
|
||||||
|
}
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
/* PipeWire */
|
|
||||||
/* SPDX-FileCopyrightText: Copyright © 2026 Red Hat */
|
|
||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
static inline char *
|
|
||||||
encode_hex(const uint8_t *data, size_t size)
|
|
||||||
{
|
|
||||||
FILE *ms;
|
|
||||||
char *encoded = NULL;
|
|
||||||
size_t encoded_size = 0;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
ms = open_memstream(&encoded, &encoded_size);
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
fprintf(ms, "%02x", data[i]);
|
|
||||||
}
|
|
||||||
fclose(ms);
|
|
||||||
|
|
||||||
return encoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int8_t
|
|
||||||
ascii_hex_to_hex(uint8_t ascii_hex)
|
|
||||||
{
|
|
||||||
if (ascii_hex >= '0' && ascii_hex <= '9')
|
|
||||||
return ascii_hex - '0';
|
|
||||||
else if (ascii_hex >= 'a' && ascii_hex <= 'f')
|
|
||||||
return ascii_hex - 'a' + 10;
|
|
||||||
else if (ascii_hex >= 'A' && ascii_hex <= 'F')
|
|
||||||
return ascii_hex - 'A' + 10;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
decode_hex(const char *encoded, uint8_t *data, size_t size)
|
|
||||||
{
|
|
||||||
size_t length;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
length = strlen(encoded);
|
|
||||||
|
|
||||||
if (size < (length / 2) * sizeof(uint8_t))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (i < length) {
|
|
||||||
int8_t top = ascii_hex_to_hex(encoded[i]);
|
|
||||||
int8_t bottom = ascii_hex_to_hex(encoded[i + 1]);
|
|
||||||
|
|
||||||
if (top == -1 || bottom == -1)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
uint8_t el = top << 4 | bottom;
|
|
||||||
data[i / 2] = el;
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
#include <pipewire/capabilities.h>
|
#include <pipewire/capabilities.h>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "base64.h"
|
||||||
|
|
||||||
/* Comment out to test device ID negotation backward compatibility. */
|
/* Comment out to test device ID negotation backward compatibility. */
|
||||||
#define SUPPORT_DEVICE_ID_NEGOTIATION 1
|
#define SUPPORT_DEVICE_ID_NEGOTIATION 1
|
||||||
|
|
@ -372,28 +372,18 @@ collect_device_ids(struct data *data, const char *json)
|
||||||
int len;
|
int len;
|
||||||
const char *value;
|
const char *value;
|
||||||
struct spa_json sub;
|
struct spa_json sub;
|
||||||
char key[1024];
|
|
||||||
|
|
||||||
if ((len = spa_json_begin(&it, json, strlen(json), &value)) <= 0) {
|
if ((len = spa_json_begin(&it, json, strlen(json), &value)) <= 0) {
|
||||||
fprintf(stderr, "invalid device IDs value\n");
|
fprintf(stderr, "invalid device IDs value\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!spa_json_is_object(value, len)) {
|
if (!spa_json_is_array(value, len)) {
|
||||||
fprintf(stderr, "device IDs not object\n");
|
fprintf(stderr, "device IDs not array\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spa_json_enter(&it, &sub);
|
spa_json_enter(&it, &sub);
|
||||||
while ((len = spa_json_object_next(&sub, key, sizeof(key), &value)) > 0) {
|
while ((len = spa_json_next(&sub, &value)) > 0) {
|
||||||
struct spa_json devices_sub;
|
|
||||||
|
|
||||||
if (!spa_json_is_array(value, len)) {
|
|
||||||
fprintf(stderr, "available-devices not array\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
spa_json_enter(&sub, &devices_sub);
|
|
||||||
while ((len = spa_json_next(&devices_sub, &value)) > 0) {
|
|
||||||
char *string;
|
char *string;
|
||||||
union {
|
union {
|
||||||
dev_t device_id;
|
dev_t device_id;
|
||||||
|
|
@ -412,8 +402,9 @@ collect_device_ids(struct data *data, const char *json)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decode_hex(string, dec.buffer, sizeof (dec.buffer)) < 0) {
|
if (base64_decode(string, strlen(string),
|
||||||
fprintf(stderr, "invalid device ID string\n");
|
(uint8_t *)&dec.device_id) < sizeof(dev_t)) {
|
||||||
|
fprintf(stderr, "invalid device ID\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -423,7 +414,6 @@ collect_device_ids(struct data *data, const char *json)
|
||||||
data->device_ids[data->n_device_ids++] = dec.device_id;
|
data->device_ids[data->n_device_ids++] = dec.device_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
discover_capabilities(struct data *data, const struct spa_pod *param)
|
discover_capabilities(struct data *data, const struct spa_pod *param)
|
||||||
|
|
@ -448,9 +438,8 @@ discover_capabilities(struct data *data, const struct spa_pod *param)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
spa_dict_for_each(it, &dict) {
|
spa_dict_for_each(it, &dict) {
|
||||||
if (spa_streq(it->key, PW_CAPABILITY_DEVICE_ID_NEGOTIATION)) {
|
if (spa_streq(it->key, PW_CAPABILITY_DEVICE_ID_NEGOTIATION) &&
|
||||||
int version = atoi(it->value);
|
spa_streq(it->value, "true")) {
|
||||||
if (version >= 1)
|
|
||||||
data->device_negotiation_supported = true;
|
data->device_negotiation_supported = true;
|
||||||
} else if (spa_streq(it->key, PW_CAPABILITY_DEVICE_IDS)) {
|
} else if (spa_streq(it->key, PW_CAPABILITY_DEVICE_IDS)) {
|
||||||
collect_device_ids(data, it->value);
|
collect_device_ids(data, it->value);
|
||||||
|
|
@ -798,7 +787,7 @@ int main(int argc, char *argv[])
|
||||||
params[n_params++] =
|
params[n_params++] =
|
||||||
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
|
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
|
||||||
&SPA_DICT_ITEMS(
|
&SPA_DICT_ITEMS(
|
||||||
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1")));
|
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "true")));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* now connect the stream, we need a direction (input/output),
|
/* now connect the stream, we need a direction (input/output),
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
#include <pipewire/capabilities.h>
|
#include <pipewire/capabilities.h>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "base64.h"
|
||||||
|
|
||||||
/* Comment out to test device ID negotation backward compatibility. */
|
/* Comment out to test device ID negotation backward compatibility. */
|
||||||
#define SUPPORT_DEVICE_ID_NEGOTIATION 1
|
#define SUPPORT_DEVICE_ID_NEGOTIATION 1
|
||||||
|
|
@ -450,9 +450,8 @@ discover_capabilities(struct data *data, const struct spa_pod *param)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
spa_dict_for_each(it, &dict) {
|
spa_dict_for_each(it, &dict) {
|
||||||
if (spa_streq(it->key, PW_CAPABILITY_DEVICE_ID_NEGOTIATION)) {
|
if (spa_streq(it->key, PW_CAPABILITY_DEVICE_ID_NEGOTIATION) &&
|
||||||
int version = atoi(it->value);
|
spa_streq(it->value, "true")) {
|
||||||
if (version >= 1)
|
|
||||||
data->device_negotiation_supported = true;
|
data->device_negotiation_supported = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -784,26 +783,23 @@ int main(int argc, char *argv[])
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
ms = open_memstream(&device_ids, &device_ids_size);
|
ms = open_memstream(&device_ids, &device_ids_size);
|
||||||
fprintf(ms, "{\"available-devices\": [");
|
fprintf(ms, "[");
|
||||||
for (i = 0; i < SPA_N_ELEMENTS(devices); i++) {
|
for (i = 0; i < SPA_N_ELEMENTS(devices); i++) {
|
||||||
dev_t device_id = makedev(devices[i].major, devices[i].minor);
|
dev_t device_id = makedev(devices[i].major, devices[i].minor);
|
||||||
char *device_id_encoded;
|
char device_id_encoded[256];
|
||||||
|
|
||||||
device_id_encoded = encode_hex((const uint8_t *) &device_id, sizeof (device_id));
|
|
||||||
|
|
||||||
|
base64_encode((const uint8_t *) &device_id, sizeof (device_id), device_id_encoded, '\0');
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
fprintf(ms, ",");
|
fprintf(ms, ",");
|
||||||
fprintf(ms, "\"%s\"", device_id_encoded);
|
fprintf(ms, "\"%s\"", device_id_encoded);
|
||||||
|
|
||||||
free(device_id_encoded);
|
|
||||||
}
|
}
|
||||||
fprintf(ms, "]}");
|
fprintf(ms, "]");
|
||||||
fclose(ms);
|
fclose(ms);
|
||||||
#endif /* SUPPORT_DEVICE_IDS_LIST */
|
#endif /* SUPPORT_DEVICE_IDS_LIST */
|
||||||
|
|
||||||
params[n_params++] =
|
params[n_params++] =
|
||||||
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
|
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
|
||||||
&SPA_DICT_ITEMS(SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1"),
|
&SPA_DICT_ITEMS(SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "true"),
|
||||||
#ifdef SUPPORT_DEVICE_IDS_LIST
|
#ifdef SUPPORT_DEVICE_IDS_LIST
|
||||||
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_IDS, device_ids)
|
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_IDS, device_ids)
|
||||||
#endif /* SUPPORT_DEVICE_IDS_LIST */
|
#endif /* SUPPORT_DEVICE_IDS_LIST */
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,8 @@ void gst_pipewire_pool_wrap_buffer (GstPipeWirePool *pool, struct pw_buffer *b)
|
||||||
data->b = b;
|
data->b = b;
|
||||||
data->buf = buf;
|
data->buf = buf;
|
||||||
data->crop = spa_buffer_find_meta_data (b->buffer, SPA_META_VideoCrop, sizeof(*data->crop));
|
data->crop = spa_buffer_find_meta_data (b->buffer, SPA_META_VideoCrop, sizeof(*data->crop));
|
||||||
|
if (data->crop)
|
||||||
|
gst_buffer_add_video_crop_meta(buf);
|
||||||
data->videotransform =
|
data->videotransform =
|
||||||
spa_buffer_find_meta_data (b->buffer, SPA_META_VideoTransform, sizeof(*data->videotransform));
|
spa_buffer_find_meta_data (b->buffer, SPA_META_VideoTransform, sizeof(*data->videotransform));
|
||||||
data->cursor = spa_buffer_find_meta_data (b->buffer, SPA_META_Cursor, sizeof(*data->cursor));
|
data->cursor = spa_buffer_find_meta_data (b->buffer, SPA_META_Cursor, sizeof(*data->cursor));
|
||||||
|
|
@ -432,25 +434,26 @@ release_buffer (GstBufferPool * pool, GstBuffer *buffer)
|
||||||
GST_LOG_OBJECT (pool, "release buffer %p", buffer);
|
GST_LOG_OBJECT (pool, "release buffer %p", buffer);
|
||||||
|
|
||||||
GstPipeWirePoolData *data = gst_pipewire_pool_get_data(buffer);
|
GstPipeWirePoolData *data = gst_pipewire_pool_get_data(buffer);
|
||||||
GstPipeWirePool *p = GST_PIPEWIRE_POOL (pool);
|
|
||||||
g_autoptr (GstPipeWireStream) s = g_weak_ref_get (&p->stream);
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (pool);
|
GST_OBJECT_LOCK (pool);
|
||||||
pw_thread_loop_lock (s->core->loop);
|
|
||||||
|
|
||||||
if (!data->queued && data->b != NULL)
|
if (!data->queued && data->b != NULL)
|
||||||
{
|
{
|
||||||
|
GstPipeWirePool *p = GST_PIPEWIRE_POOL (pool);
|
||||||
|
g_autoptr (GstPipeWireStream) s = g_weak_ref_get (&p->stream);
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
pw_thread_loop_lock (s->core->loop);
|
||||||
|
|
||||||
if ((res = pw_stream_return_buffer (s->pwstream, data->b)) < 0) {
|
if ((res = pw_stream_return_buffer (s->pwstream, data->b)) < 0) {
|
||||||
GST_ERROR_OBJECT (pool,"can't return buffer %p; gstbuffer : %p, %s",data->b, buffer, spa_strerror(res));
|
GST_ERROR_OBJECT (pool,"can't return buffer %p; gstbuffer : %p, %s",data->b, buffer, spa_strerror(res));
|
||||||
} else {
|
} else {
|
||||||
data->queued = TRUE;
|
data->queued = TRUE;
|
||||||
GST_DEBUG_OBJECT (pool, "returned buffer %p; gstbuffer:%p", data->b, buffer);
|
GST_DEBUG_OBJECT (pool, "returned buffer %p; gstbuffer:%p", data->b, buffer);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pw_thread_loop_unlock (s->core->loop);
|
pw_thread_loop_unlock (s->core->loop);
|
||||||
|
}
|
||||||
GST_OBJECT_UNLOCK (pool);
|
GST_OBJECT_UNLOCK (pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -781,7 +781,7 @@ static GstBuffer *dequeue_buffer(GstPipeWireSrc *pwsrc)
|
||||||
|
|
||||||
crop = data->crop;
|
crop = data->crop;
|
||||||
if (crop) {
|
if (crop) {
|
||||||
GstVideoCropMeta *meta = gst_buffer_add_video_crop_meta(buf);
|
GstVideoCropMeta *meta = gst_buffer_get_video_crop_meta(buf);
|
||||||
if (meta) {
|
if (meta) {
|
||||||
meta->x = crop->region.position.x;
|
meta->x = crop->region.position.x;
|
||||||
meta->y = crop->region.position.y;
|
meta->y = crop->region.position.y;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ module_sources = [
|
||||||
'module-vban-recv.c',
|
'module-vban-recv.c',
|
||||||
'module-vban-send.c',
|
'module-vban-send.c',
|
||||||
'module-session-manager.c',
|
'module-session-manager.c',
|
||||||
'module-scheduler-v1.c',
|
|
||||||
'module-zeroconf-discover.c',
|
'module-zeroconf-discover.c',
|
||||||
'module-roc-source.c',
|
'module-roc-source.c',
|
||||||
'module-roc-sink.c',
|
'module-roc-sink.c',
|
||||||
|
|
@ -277,6 +276,10 @@ pipewire_module_link_factory = shared_library('pipewire-module-link-factory',
|
||||||
|
|
||||||
pipewire_module_protocol_deps = [mathlib, dl_lib, pipewire_dep]
|
pipewire_module_protocol_deps = [mathlib, dl_lib, pipewire_dep]
|
||||||
|
|
||||||
|
if systemd_dep.found()
|
||||||
|
pipewire_module_protocol_deps += systemd_dep
|
||||||
|
endif
|
||||||
|
|
||||||
if selinux_dep.found()
|
if selinux_dep.found()
|
||||||
pipewire_module_protocol_deps += selinux_dep
|
pipewire_module_protocol_deps += selinux_dep
|
||||||
endif
|
endif
|
||||||
|
|
@ -533,15 +536,6 @@ pipewire_module_adapter = shared_library('pipewire-module-adapter',
|
||||||
dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep],
|
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',
|
pipewire_module_session_manager = shared_library('pipewire-module-session-manager',
|
||||||
[ 'module-session-manager.c',
|
[ 'module-session-manager.c',
|
||||||
'module-session-manager/client-endpoint/client-endpoint.c',
|
'module-session-manager/client-endpoint/client-endpoint.c',
|
||||||
|
|
@ -579,22 +573,6 @@ if build_module_zeroconf_discover
|
||||||
endif
|
endif
|
||||||
summary({'zeroconf-discover': build_module_zeroconf_discover}, bool_yn: true, section: 'Optional Modules')
|
summary({'zeroconf-discover': build_module_zeroconf_discover}, bool_yn: true, section: 'Optional Modules')
|
||||||
|
|
||||||
# Several modules (rtp-sink, rtp-source, raop-sink) use the same code
|
|
||||||
# for actual RTP transport. To not have to recompile the same code
|
|
||||||
# multiple times, and to make the build script a little more robust
|
|
||||||
# (by avoiding build script code duplication), create a static library
|
|
||||||
# that contains that common code.
|
|
||||||
pipewire_module_rtp_common_lib = static_library('pipewire-module-rtp-common-lib',
|
|
||||||
[ 'module-rtp/stream.c' ],
|
|
||||||
include_directories : [configinc],
|
|
||||||
install : false,
|
|
||||||
dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep],
|
|
||||||
)
|
|
||||||
pipewire_module_rtp_common_dep = declare_dependency(
|
|
||||||
link_with: pipewire_module_rtp_common_lib,
|
|
||||||
dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep],
|
|
||||||
)
|
|
||||||
|
|
||||||
build_module_raop_discover = avahi_dep.found()
|
build_module_raop_discover = avahi_dep.found()
|
||||||
if build_module_raop_discover
|
if build_module_raop_discover
|
||||||
pipewire_module_raop_discover = shared_library('pipewire-module-raop-discover',
|
pipewire_module_raop_discover = shared_library('pipewire-module-raop-discover',
|
||||||
|
|
@ -627,12 +605,13 @@ build_module_raop = openssl_lib.found()
|
||||||
if build_module_raop
|
if build_module_raop
|
||||||
pipewire_module_raop_sink = shared_library('pipewire-module-raop-sink',
|
pipewire_module_raop_sink = shared_library('pipewire-module-raop-sink',
|
||||||
[ 'module-raop-sink.c',
|
[ 'module-raop-sink.c',
|
||||||
'module-raop/rtsp-client.c' ],
|
'module-raop/rtsp-client.c',
|
||||||
|
'module-rtp/stream.c' ],
|
||||||
include_directories : [configinc],
|
include_directories : [configinc],
|
||||||
install : true,
|
install : true,
|
||||||
install_dir : modules_install_dir,
|
install_dir : modules_install_dir,
|
||||||
install_rpath: modules_install_dir,
|
install_rpath: modules_install_dir,
|
||||||
dependencies : [pipewire_module_rtp_common_dep, openssl_lib],
|
dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep, openssl_lib],
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
summary({'raop-sink (requires OpenSSL)': build_module_raop}, bool_yn: true, section: 'Optional Modules')
|
summary({'raop-sink (requires OpenSSL)': build_module_raop}, bool_yn: true, section: 'Optional Modules')
|
||||||
|
|
@ -641,33 +620,36 @@ roc_dep = dependency('roc', version: '>= 0.4.0', required: get_option('roc'))
|
||||||
summary({'ROC': roc_dep.found()}, bool_yn: true, section: 'Streaming between daemons')
|
summary({'ROC': roc_dep.found()}, bool_yn: true, section: 'Streaming between daemons')
|
||||||
|
|
||||||
pipewire_module_rtp_source = shared_library('pipewire-module-rtp-source',
|
pipewire_module_rtp_source = shared_library('pipewire-module-rtp-source',
|
||||||
[ 'module-rtp-source.c' ],
|
[ 'module-rtp-source.c',
|
||||||
|
'module-rtp/stream.c' ],
|
||||||
include_directories : [configinc],
|
include_directories : [configinc],
|
||||||
install : true,
|
install : true,
|
||||||
install_dir : modules_install_dir,
|
install_dir : modules_install_dir,
|
||||||
install_rpath: modules_install_dir,
|
install_rpath: modules_install_dir,
|
||||||
dependencies : [pipewire_module_rtp_common_dep],
|
dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep],
|
||||||
)
|
)
|
||||||
|
|
||||||
pipewire_module_rtp_sink = shared_library('pipewire-module-rtp-sink',
|
pipewire_module_rtp_sink = shared_library('pipewire-module-rtp-sink',
|
||||||
[ 'module-rtp-sink.c' ],
|
[ 'module-rtp-sink.c',
|
||||||
|
'module-rtp/stream.c' ],
|
||||||
include_directories : [configinc],
|
include_directories : [configinc],
|
||||||
install : true,
|
install : true,
|
||||||
install_dir : modules_install_dir,
|
install_dir : modules_install_dir,
|
||||||
install_rpath: modules_install_dir,
|
install_rpath: modules_install_dir,
|
||||||
dependencies : [pipewire_module_rtp_common_dep],
|
dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep],
|
||||||
)
|
)
|
||||||
|
|
||||||
build_module_rtp_session = avahi_dep.found()
|
build_module_rtp_session = avahi_dep.found()
|
||||||
if build_module_rtp_session
|
if build_module_rtp_session
|
||||||
pipewire_module_rtp_session = shared_library('pipewire-module-rtp-session',
|
pipewire_module_rtp_session = shared_library('pipewire-module-rtp-session',
|
||||||
[ 'module-zeroconf-discover/avahi-poll.c',
|
[ 'module-rtp/stream.c',
|
||||||
|
'module-zeroconf-discover/avahi-poll.c',
|
||||||
'module-rtp-session.c' ],
|
'module-rtp-session.c' ],
|
||||||
include_directories : [configinc],
|
include_directories : [configinc],
|
||||||
install : true,
|
install : true,
|
||||||
install_dir : modules_install_dir,
|
install_dir : modules_install_dir,
|
||||||
install_rpath: modules_install_dir,
|
install_rpath: modules_install_dir,
|
||||||
dependencies : [pipewire_module_rtp_common_dep, avahi_dep],
|
dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep, opus_dep],
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -703,7 +685,7 @@ pipewire_module_vban_recv = shared_library('pipewire-module-vban-recv',
|
||||||
build_module_roc = roc_dep.found()
|
build_module_roc = roc_dep.found()
|
||||||
if build_module_roc
|
if build_module_roc
|
||||||
pipewire_module_roc_sink = shared_library('pipewire-module-roc-sink',
|
pipewire_module_roc_sink = shared_library('pipewire-module-roc-sink',
|
||||||
[ 'module-roc-sink.c', 'module-roc/common.c'],
|
[ 'module-roc-sink.c' ],
|
||||||
include_directories : [configinc],
|
include_directories : [configinc],
|
||||||
install : true,
|
install : true,
|
||||||
install_dir : modules_install_dir,
|
install_dir : modules_install_dir,
|
||||||
|
|
@ -712,7 +694,7 @@ if build_module_roc
|
||||||
)
|
)
|
||||||
|
|
||||||
pipewire_module_roc_source = shared_library('pipewire-module-roc-source',
|
pipewire_module_roc_source = shared_library('pipewire-module-roc-source',
|
||||||
[ 'module-roc-source.c', 'module-roc/common.c' ],
|
[ 'module-roc-source.c' ],
|
||||||
include_directories : [configinc],
|
include_directories : [configinc],
|
||||||
install : true,
|
install : true,
|
||||||
install_dir : modules_install_dir,
|
install_dir : modules_install_dir,
|
||||||
|
|
|
||||||
|
|
@ -193,10 +193,6 @@ extern struct spa_handle_factory spa_filter_graph_factory;
|
||||||
* graph will then be duplicated as many times to match the number of input/output
|
* graph will then be duplicated as many times to match the number of input/output
|
||||||
* channels of the streams.
|
* channels of the streams.
|
||||||
*
|
*
|
||||||
* If the graph has no inputs and the capture channels is set as 0, only the
|
|
||||||
* playback stream will be created. Likewise, if there are no outputs and the
|
|
||||||
* playback channels is 0, there will be no capture stream created.
|
|
||||||
*
|
|
||||||
* ### Volumes
|
* ### Volumes
|
||||||
*
|
*
|
||||||
* Normally the volume of the sink/source is handled by the stream software volume.
|
* Normally the volume of the sink/source is handled by the stream software volume.
|
||||||
|
|
@ -656,40 +652,6 @@ extern struct spa_handle_factory spa_filter_graph_factory;
|
||||||
* of "Attack (s)" seconds. The noise gate stays open for at least "Hold (s)"
|
* of "Attack (s)" seconds. The noise gate stays open for at least "Hold (s)"
|
||||||
* seconds before it can close again.
|
* seconds before it can close again.
|
||||||
*
|
*
|
||||||
* ### Busy
|
|
||||||
*
|
|
||||||
* The `busy` plugin has no input or output ports and it can be used to keep the
|
|
||||||
* CPU or graph busy for the given percent of time.
|
|
||||||
*
|
|
||||||
* The node requires a `config` section with extra configuration:
|
|
||||||
*
|
|
||||||
*\code{.unparsed}
|
|
||||||
* filter.graph = {
|
|
||||||
* nodes = [
|
|
||||||
* {
|
|
||||||
* type = builtin
|
|
||||||
* name = ...
|
|
||||||
* label = busy
|
|
||||||
* config = {
|
|
||||||
* wait-percent = 0.0
|
|
||||||
* cpu-percent = 50.0
|
|
||||||
* }
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
*\endcode
|
|
||||||
*
|
|
||||||
* - `wait-percent` the percentage of time to wait. This keeps the graph busy but
|
|
||||||
* not the CPU. Default 0.0
|
|
||||||
* - `cpu-percent` the percentage of time to keep the CPU busy. This keeps both the
|
|
||||||
* graph and CPU busy. Default 0.0
|
|
||||||
*
|
|
||||||
* ### Null
|
|
||||||
*
|
|
||||||
* The `null` plugin has one data input "In" and one control input "Control" that
|
|
||||||
* simply discards the data.
|
|
||||||
*
|
*
|
||||||
* ## SOFA filters
|
* ## SOFA filters
|
||||||
*
|
*
|
||||||
|
|
@ -717,7 +679,6 @@ extern struct spa_handle_factory spa_filter_graph_factory;
|
||||||
* blocksize = ...
|
* blocksize = ...
|
||||||
* tailsize = ...
|
* tailsize = ...
|
||||||
* filename = ...
|
* filename = ...
|
||||||
* gain = ...
|
|
||||||
* }
|
* }
|
||||||
* control = {
|
* control = {
|
||||||
* "Azimuth" = ...
|
* "Azimuth" = ...
|
||||||
|
|
@ -737,7 +698,6 @@ extern struct spa_handle_factory spa_filter_graph_factory;
|
||||||
* - `tailsize` specifies the size of the tail blocks to use in the FFT.
|
* - `tailsize` specifies the size of the tail blocks to use in the FFT.
|
||||||
* - `filename` The SOFA file to load. SOFA files usually end in the .sofa extension
|
* - `filename` The SOFA file to load. SOFA files usually end in the .sofa extension
|
||||||
* and contain the HRTF for the various spatial positions.
|
* and contain the HRTF for the various spatial positions.
|
||||||
* - `gain` the overall gain to apply to the IR file.
|
|
||||||
*
|
*
|
||||||
* - `Azimuth` controls the azimuth, this is the direction the sound is coming from
|
* - `Azimuth` controls the azimuth, this is the direction the sound is coming from
|
||||||
* in degrees between 0 and 360. 0 is straight ahead. 90 is left, 180
|
* in degrees between 0 and 360. 0 is straight ahead. 90 is left, 180
|
||||||
|
|
@ -1255,88 +1215,13 @@ static void capture_destroy(void *d)
|
||||||
impl->capture = NULL;
|
impl->capture = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_process(struct impl *impl)
|
|
||||||
{
|
|
||||||
struct pw_buffer *in, *out;
|
|
||||||
uint32_t i, n_in = 0, n_out = 0, data_size = 0;
|
|
||||||
struct spa_data *bd;
|
|
||||||
const void *cin[128];
|
|
||||||
void *cout[128];
|
|
||||||
|
|
||||||
in = out = NULL;
|
|
||||||
if (impl->capture) {
|
|
||||||
while (true) {
|
|
||||||
struct pw_buffer *t;
|
|
||||||
if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL)
|
|
||||||
break;
|
|
||||||
if (in)
|
|
||||||
pw_stream_queue_buffer(impl->capture, in);
|
|
||||||
in = t;
|
|
||||||
}
|
|
||||||
if (in == NULL) {
|
|
||||||
pw_log_debug("%p: out of capture buffers: %m", impl);
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < in->buffer->n_datas; i++) {
|
|
||||||
uint32_t offs, size;
|
|
||||||
|
|
||||||
bd = &in->buffer->datas[i];
|
|
||||||
|
|
||||||
offs = SPA_MIN(bd->chunk->offset, bd->maxsize);
|
|
||||||
size = SPA_MIN(bd->chunk->size, bd->maxsize - offs);
|
|
||||||
|
|
||||||
cin[n_in++] = SPA_PTROFF(bd->data, offs, void);
|
|
||||||
|
|
||||||
data_size = i == 0 ? size : SPA_MIN(data_size, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (impl->playback) {
|
|
||||||
out = pw_stream_dequeue_buffer(impl->playback);
|
|
||||||
if (out == NULL) {
|
|
||||||
pw_log_debug("%p: out of playback buffers: %m", impl);
|
|
||||||
} else {
|
|
||||||
if (data_size == 0)
|
|
||||||
data_size = out->requested * sizeof(float);
|
|
||||||
|
|
||||||
for (i = 0; i < out->buffer->n_datas; i++) {
|
|
||||||
bd = &out->buffer->datas[i];
|
|
||||||
|
|
||||||
data_size = SPA_MIN(data_size, bd->maxsize);
|
|
||||||
|
|
||||||
cout[n_out++] = bd->data;
|
|
||||||
|
|
||||||
bd->chunk->offset = 0;
|
|
||||||
bd->chunk->size = data_size;
|
|
||||||
bd->chunk->stride = sizeof(float);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pw_log_trace_fp("%p: size:%d requested:%"PRIu64, impl,
|
|
||||||
data_size, out->requested);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; n_in < impl->n_inputs; i++)
|
|
||||||
cin[n_in++] = NULL;
|
|
||||||
for (; n_out < impl->n_outputs; i++)
|
|
||||||
cout[n_out++] = NULL;
|
|
||||||
|
|
||||||
if (impl->graph_active)
|
|
||||||
spa_filter_graph_process(impl->graph, cin, cout, data_size / sizeof(float));
|
|
||||||
|
|
||||||
if (in != NULL)
|
|
||||||
pw_stream_queue_buffer(impl->capture, in);
|
|
||||||
if (out != NULL)
|
|
||||||
pw_stream_queue_buffer(impl->playback, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void capture_process(void *d)
|
static void capture_process(void *d)
|
||||||
{
|
{
|
||||||
struct impl *impl = d;
|
struct impl *impl = d;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if (impl->playback) {
|
|
||||||
if ((res = pw_stream_trigger_process(impl->playback)) < 0) {
|
if ((res = pw_stream_trigger_process(impl->playback)) < 0) {
|
||||||
pw_log_debug("playback trigger error: %s", spa_strerror(res));
|
pw_log_debug("playback trigger error: %s", spa_strerror(res));
|
||||||
while (impl->capture) {
|
while (true) {
|
||||||
struct pw_buffer *t;
|
struct pw_buffer *t;
|
||||||
if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL)
|
if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
@ -1345,15 +1230,77 @@ static void capture_process(void *d)
|
||||||
pw_stream_queue_buffer(impl->capture, t);
|
pw_stream_queue_buffer(impl->capture, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
do_process(impl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void playback_process(void *d)
|
static void playback_process(void *d)
|
||||||
{
|
{
|
||||||
struct impl *impl = d;
|
struct impl *impl = d;
|
||||||
do_process(impl);
|
struct pw_buffer *in, *out;
|
||||||
|
uint32_t i, data_size = 0;
|
||||||
|
int32_t stride = 0;
|
||||||
|
struct spa_data *bd;
|
||||||
|
const void *cin[128];
|
||||||
|
void *cout[128];
|
||||||
|
|
||||||
|
in = NULL;
|
||||||
|
while (true) {
|
||||||
|
struct pw_buffer *t;
|
||||||
|
if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL)
|
||||||
|
break;
|
||||||
|
if (in)
|
||||||
|
pw_stream_queue_buffer(impl->capture, in);
|
||||||
|
in = t;
|
||||||
|
}
|
||||||
|
if (in == NULL)
|
||||||
|
pw_log_debug("%p: out of capture buffers: %m", impl);
|
||||||
|
|
||||||
|
if ((out = pw_stream_dequeue_buffer(impl->playback)) == NULL)
|
||||||
|
pw_log_debug("%p: out of playback buffers: %m", impl);
|
||||||
|
|
||||||
|
if (in == NULL || out == NULL)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
for (i = 0; i < in->buffer->n_datas; i++) {
|
||||||
|
uint32_t offs, size;
|
||||||
|
|
||||||
|
bd = &in->buffer->datas[i];
|
||||||
|
|
||||||
|
offs = SPA_MIN(bd->chunk->offset, bd->maxsize);
|
||||||
|
size = SPA_MIN(bd->chunk->size, bd->maxsize - offs);
|
||||||
|
|
||||||
|
cin[i] = SPA_PTROFF(bd->data, offs, void);
|
||||||
|
|
||||||
|
data_size = i == 0 ? size : SPA_MIN(data_size, size);
|
||||||
|
stride = SPA_MAX(stride, bd->chunk->stride);
|
||||||
|
}
|
||||||
|
for (; i < impl->n_inputs; i++)
|
||||||
|
cin[i] = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < out->buffer->n_datas; i++) {
|
||||||
|
bd = &out->buffer->datas[i];
|
||||||
|
|
||||||
|
data_size = SPA_MIN(data_size, bd->maxsize);
|
||||||
|
|
||||||
|
cout[i] = bd->data;
|
||||||
|
|
||||||
|
bd->chunk->offset = 0;
|
||||||
|
bd->chunk->size = data_size;
|
||||||
|
bd->chunk->stride = stride;
|
||||||
|
}
|
||||||
|
for (; i < impl->n_outputs; i++)
|
||||||
|
cout[i] = NULL;
|
||||||
|
|
||||||
|
pw_log_trace_fp("%p: stride:%d size:%d requested:%"PRIu64" (%"PRIu64")", impl,
|
||||||
|
stride, data_size, out->requested, out->requested * stride);
|
||||||
|
|
||||||
|
if (impl->graph_active)
|
||||||
|
spa_filter_graph_process(impl->graph, cin, cout, data_size / sizeof(float));
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (in != NULL)
|
||||||
|
pw_stream_queue_buffer(impl->capture, in);
|
||||||
|
if (out != NULL)
|
||||||
|
pw_stream_queue_buffer(impl->playback, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int activate_graph(struct impl *impl)
|
static int activate_graph(struct impl *impl)
|
||||||
|
|
@ -1422,9 +1369,6 @@ static void update_latency(struct impl *impl, enum spa_direction direction, bool
|
||||||
struct pw_stream *s = direction == SPA_DIRECTION_OUTPUT ?
|
struct pw_stream *s = direction == SPA_DIRECTION_OUTPUT ?
|
||||||
impl->playback : impl->capture;
|
impl->playback : impl->capture;
|
||||||
|
|
||||||
if (s == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||||
latency = impl->latency[direction];
|
latency = impl->latency[direction];
|
||||||
spa_process_latency_info_add(&impl->process_latency, &latency);
|
spa_process_latency_info_add(&impl->process_latency, &latency);
|
||||||
|
|
@ -1481,14 +1425,11 @@ static void param_tag_changed(struct impl *impl, const struct spa_pod *param,
|
||||||
if (param == 0 || spa_tag_parse(param, &tag, &state) < 0)
|
if (param == 0 || spa_tag_parse(param, &tag, &state) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (tag.direction == SPA_DIRECTION_INPUT) {
|
if (tag.direction == SPA_DIRECTION_INPUT)
|
||||||
if (impl->capture)
|
|
||||||
pw_stream_update_params(impl->capture, params, 1);
|
pw_stream_update_params(impl->capture, params, 1);
|
||||||
} else {
|
else
|
||||||
if (impl->playback)
|
|
||||||
pw_stream_update_params(impl->playback, params, 1);
|
pw_stream_update_params(impl->playback, params, 1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void capture_state_changed(void *data, enum pw_stream_state old,
|
static void capture_state_changed(void *data, enum pw_stream_state old,
|
||||||
enum pw_stream_state state, const char *error)
|
enum pw_stream_state state, const char *error)
|
||||||
|
|
@ -1564,7 +1505,8 @@ static void param_changed(struct impl *impl, uint32_t id, const struct spa_pod *
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
pw_stream_set_error(stream, res, "can't start graph: %s", spa_strerror(res));
|
pw_stream_set_error(direction == SPA_DIRECTION_INPUT ? impl->capture : impl->playback,
|
||||||
|
res, "can't start graph: %s", spa_strerror(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void capture_param_changed(void *data, uint32_t id, const struct spa_pod *param)
|
static void capture_param_changed(void *data, uint32_t id, const struct spa_pod *param)
|
||||||
|
|
@ -1623,7 +1565,7 @@ static void playback_state_changed(void *data, enum pw_stream_state old,
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
error:
|
error:
|
||||||
pw_stream_set_error(impl->playback, res, "can't start graph: %s",
|
pw_stream_set_error(impl->capture, res, "can't start graph: %s",
|
||||||
spa_strerror(res));
|
spa_strerror(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1652,13 +1594,12 @@ static const struct pw_stream_events out_stream_events = {
|
||||||
static int setup_streams(struct impl *impl)
|
static int setup_streams(struct impl *impl)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
uint32_t i, n_params, *offs, flags;
|
uint32_t i, n_params, *offs;
|
||||||
struct pw_array offsets;
|
struct pw_array offsets;
|
||||||
const struct spa_pod **params = NULL;
|
const struct spa_pod **params = NULL;
|
||||||
struct spa_pod_dynamic_builder b;
|
struct spa_pod_dynamic_builder b;
|
||||||
struct spa_filter_graph *graph = impl->graph;
|
struct spa_filter_graph *graph = impl->graph;
|
||||||
|
|
||||||
if (impl->capture_info.channels > 0) {
|
|
||||||
impl->capture = pw_stream_new(impl->core,
|
impl->capture = pw_stream_new(impl->core,
|
||||||
"filter capture", impl->capture_props);
|
"filter capture", impl->capture_props);
|
||||||
impl->capture_props = NULL;
|
impl->capture_props = NULL;
|
||||||
|
|
@ -1668,9 +1609,7 @@ static int setup_streams(struct impl *impl)
|
||||||
pw_stream_add_listener(impl->capture,
|
pw_stream_add_listener(impl->capture,
|
||||||
&impl->capture_listener,
|
&impl->capture_listener,
|
||||||
&in_stream_events, impl);
|
&in_stream_events, impl);
|
||||||
}
|
|
||||||
|
|
||||||
if (impl->playback_info.channels > 0) {
|
|
||||||
impl->playback = pw_stream_new(impl->core,
|
impl->playback = pw_stream_new(impl->core,
|
||||||
"filter playback", impl->playback_props);
|
"filter playback", impl->playback_props);
|
||||||
impl->playback_props = NULL;
|
impl->playback_props = NULL;
|
||||||
|
|
@ -1680,11 +1619,18 @@ static int setup_streams(struct impl *impl)
|
||||||
pw_stream_add_listener(impl->playback,
|
pw_stream_add_listener(impl->playback,
|
||||||
&impl->playback_listener,
|
&impl->playback_listener,
|
||||||
&out_stream_events, impl);
|
&out_stream_events, impl);
|
||||||
}
|
|
||||||
|
|
||||||
spa_pod_dynamic_builder_init(&b, NULL, 0, 4096);
|
spa_pod_dynamic_builder_init(&b, NULL, 0, 4096);
|
||||||
pw_array_init(&offsets, 512);
|
pw_array_init(&offsets, 512);
|
||||||
|
|
||||||
|
if ((offs = pw_array_add(&offsets, sizeof(uint32_t))) == NULL) {
|
||||||
|
res = -errno;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
*offs = b.b.state.offset;
|
||||||
|
spa_format_audio_raw_build(&b.b,
|
||||||
|
SPA_PARAM_EnumFormat, &impl->capture_info);
|
||||||
|
|
||||||
for (i = 0;; i++) {
|
for (i = 0;; i++) {
|
||||||
uint32_t save = b.b.state.offset;
|
uint32_t save = b.b.state.offset;
|
||||||
if (spa_filter_graph_enum_prop_info(graph, i, &b.b, NULL) != 1)
|
if (spa_filter_graph_enum_prop_info(graph, i, &b.b, NULL) != 1)
|
||||||
|
|
@ -1707,7 +1653,7 @@ static int setup_streams(struct impl *impl)
|
||||||
res = -ENOMEM;
|
res = -ENOMEM;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((params = calloc(n_params+1, sizeof(struct spa_pod*))) == NULL) {
|
if ((params = calloc(n_params, sizeof(struct spa_pod*))) == NULL) {
|
||||||
res = -errno;
|
res = -errno;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -1716,19 +1662,13 @@ static int setup_streams(struct impl *impl)
|
||||||
for (i = 0; i < n_params; i++)
|
for (i = 0; i < n_params; i++)
|
||||||
params[i] = spa_pod_builder_deref(&b.b, offs[i]);
|
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;
|
|
||||||
if (impl->playback)
|
|
||||||
flags |= PW_STREAM_FLAG_ASYNC;
|
|
||||||
|
|
||||||
res = pw_stream_connect(impl->capture,
|
res = pw_stream_connect(impl->capture,
|
||||||
PW_DIRECTION_INPUT,
|
PW_DIRECTION_INPUT,
|
||||||
PW_ID_ANY,
|
PW_ID_ANY,
|
||||||
flags,
|
PW_STREAM_FLAG_AUTOCONNECT |
|
||||||
|
PW_STREAM_FLAG_MAP_BUFFERS |
|
||||||
|
PW_STREAM_FLAG_RT_PROCESS |
|
||||||
|
PW_STREAM_FLAG_ASYNC,
|
||||||
params, n_params);
|
params, n_params);
|
||||||
|
|
||||||
spa_pod_dynamic_builder_clean(&b);
|
spa_pod_dynamic_builder_clean(&b);
|
||||||
|
|
@ -1737,23 +1677,17 @@ static int setup_streams(struct impl *impl)
|
||||||
|
|
||||||
n_params = 0;
|
n_params = 0;
|
||||||
spa_pod_dynamic_builder_init(&b, NULL, 0, 4096);
|
spa_pod_dynamic_builder_init(&b, NULL, 0, 4096);
|
||||||
}
|
|
||||||
if (impl->playback) {
|
|
||||||
params[n_params++] = spa_format_audio_raw_build(&b.b,
|
params[n_params++] = spa_format_audio_raw_build(&b.b,
|
||||||
SPA_PARAM_EnumFormat, &impl->playback_info);
|
SPA_PARAM_EnumFormat, &impl->playback_info);
|
||||||
|
|
||||||
flags = PW_STREAM_FLAG_AUTOCONNECT |
|
|
||||||
PW_STREAM_FLAG_MAP_BUFFERS |
|
|
||||||
PW_STREAM_FLAG_RT_PROCESS;
|
|
||||||
if (impl->capture)
|
|
||||||
flags |= PW_STREAM_FLAG_TRIGGER;
|
|
||||||
|
|
||||||
res = pw_stream_connect(impl->playback,
|
res = pw_stream_connect(impl->playback,
|
||||||
PW_DIRECTION_OUTPUT,
|
PW_DIRECTION_OUTPUT,
|
||||||
PW_ID_ANY,
|
PW_ID_ANY,
|
||||||
flags,
|
PW_STREAM_FLAG_AUTOCONNECT |
|
||||||
|
PW_STREAM_FLAG_MAP_BUFFERS |
|
||||||
|
PW_STREAM_FLAG_RT_PROCESS |
|
||||||
|
PW_STREAM_FLAG_TRIGGER,
|
||||||
params, n_params);
|
params, n_params);
|
||||||
}
|
|
||||||
spa_pod_dynamic_builder_clean(&b);
|
spa_pod_dynamic_builder_clean(&b);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
@ -1777,11 +1711,20 @@ static void graph_info(void *object, const struct spa_filter_graph_info *info)
|
||||||
{
|
{
|
||||||
struct impl *impl = object;
|
struct impl *impl = object;
|
||||||
struct spa_dict *props = info->props;
|
struct spa_dict *props = info->props;
|
||||||
uint32_t i, val = 0;
|
uint32_t i;
|
||||||
|
|
||||||
|
if (impl->capture_info.channels == 0)
|
||||||
|
impl->capture_info.channels = info->n_inputs;
|
||||||
|
if (impl->playback_info.channels == 0)
|
||||||
|
impl->playback_info.channels = info->n_outputs;
|
||||||
|
|
||||||
impl->n_inputs = info->n_inputs;
|
impl->n_inputs = info->n_inputs;
|
||||||
impl->n_outputs = info->n_outputs;
|
impl->n_outputs = info->n_outputs;
|
||||||
|
|
||||||
|
if (impl->capture_info.channels == impl->playback_info.channels) {
|
||||||
|
copy_position(&impl->capture_info, &impl->playback_info);
|
||||||
|
copy_position(&impl->playback_info, &impl->capture_info);
|
||||||
|
}
|
||||||
for (i = 0; props && i < props->n_items; i++) {
|
for (i = 0; props && i < props->n_items; i++) {
|
||||||
const char *k = props->items[i].key;
|
const char *k = props->items[i].key;
|
||||||
const char *s = props->items[i].value;
|
const char *s = props->items[i].value;
|
||||||
|
|
@ -1795,22 +1738,6 @@ static void graph_info(void *object, const struct spa_filter_graph_info *info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (spa_streq(k, "n_default_inputs") &&
|
|
||||||
impl->capture_info.channels == 0 &&
|
|
||||||
spa_atou32(s, &val, 0)) {
|
|
||||||
pw_log_info("using default inputs %d", val);
|
|
||||||
impl->capture_info.channels = val;
|
|
||||||
}
|
|
||||||
else if (spa_streq(k, "n_default_outputs") &&
|
|
||||||
impl->playback_info.channels == 0 &&
|
|
||||||
spa_atou32(s, &val, 0)) {
|
|
||||||
pw_log_info("using default outputs %d", val);
|
|
||||||
impl->playback_info.channels = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (impl->capture_info.channels == impl->playback_info.channels) {
|
|
||||||
copy_position(&impl->capture_info, &impl->playback_info);
|
|
||||||
copy_position(&impl->playback_info, &impl->capture_info);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1833,11 +1760,7 @@ static void graph_props_changed(void *object, enum spa_direction direction)
|
||||||
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
|
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
|
||||||
spa_filter_graph_get_props(graph, &b.b, (struct spa_pod **)¶ms[0]);
|
spa_filter_graph_get_props(graph, &b.b, (struct spa_pod **)¶ms[0]);
|
||||||
|
|
||||||
if (impl->capture)
|
|
||||||
pw_stream_update_params(impl->capture, params, 1);
|
pw_stream_update_params(impl->capture, params, 1);
|
||||||
else if (impl->playback)
|
|
||||||
pw_stream_update_params(impl->playback, params, 1);
|
|
||||||
|
|
||||||
spa_pod_dynamic_builder_clean(&b);
|
spa_pod_dynamic_builder_clean(&b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1157,13 +1157,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
||||||
|
|
||||||
pw_properties_set(impl->sink.props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
|
pw_properties_set(impl->sink.props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
|
||||||
pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_DRIVER, "30001");
|
pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_DRIVER, "30001");
|
||||||
pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_SESSION, "2001");
|
|
||||||
pw_properties_set(impl->sink.props, PW_KEY_NODE_NAME, "jack_sink");
|
pw_properties_set(impl->sink.props, PW_KEY_NODE_NAME, "jack_sink");
|
||||||
pw_properties_set(impl->sink.props, PW_KEY_NODE_DESCRIPTION, "JACK Sink");
|
pw_properties_set(impl->sink.props, PW_KEY_NODE_DESCRIPTION, "JACK Sink");
|
||||||
|
|
||||||
pw_properties_set(impl->source.props, PW_KEY_MEDIA_CLASS, "Audio/Source");
|
pw_properties_set(impl->source.props, PW_KEY_MEDIA_CLASS, "Audio/Source");
|
||||||
pw_properties_set(impl->source.props, PW_KEY_PRIORITY_DRIVER, "30000");
|
pw_properties_set(impl->source.props, PW_KEY_PRIORITY_DRIVER, "30000");
|
||||||
pw_properties_set(impl->source.props, PW_KEY_PRIORITY_SESSION, "2000");
|
|
||||||
pw_properties_set(impl->source.props, PW_KEY_NODE_NAME, "jack_source");
|
pw_properties_set(impl->source.props, PW_KEY_NODE_NAME, "jack_source");
|
||||||
pw_properties_set(impl->source.props, PW_KEY_NODE_DESCRIPTION, "JACK Source");
|
pw_properties_set(impl->source.props, PW_KEY_NODE_DESCRIPTION, "JACK Source");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1319,11 +1319,9 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
||||||
pw_properties_set(props, PW_KEY_NODE_ALWAYS_PROCESS, "true");
|
pw_properties_set(props, PW_KEY_NODE_ALWAYS_PROCESS, "true");
|
||||||
|
|
||||||
pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_DRIVER, "40000");
|
pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_DRIVER, "40000");
|
||||||
pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_SESSION, "2000");
|
|
||||||
pw_properties_set(impl->sink.props, PW_KEY_NODE_NAME, "netjack2_driver_send");
|
pw_properties_set(impl->sink.props, PW_KEY_NODE_NAME, "netjack2_driver_send");
|
||||||
|
|
||||||
pw_properties_set(impl->source.props, PW_KEY_PRIORITY_DRIVER, "40001");
|
pw_properties_set(impl->source.props, PW_KEY_PRIORITY_DRIVER, "40001");
|
||||||
pw_properties_set(impl->source.props, PW_KEY_PRIORITY_SESSION, "2001");
|
|
||||||
pw_properties_set(impl->source.props, PW_KEY_NODE_NAME, "netjack2_driver_receive");
|
pw_properties_set(impl->source.props, PW_KEY_NODE_NAME, "netjack2_driver_receive");
|
||||||
|
|
||||||
if ((str = pw_properties_get(props, "sink.props")) != NULL)
|
if ((str = pw_properties_get(props, "sink.props")) != NULL)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
#include <spa/debug/log.h>
|
#include <spa/debug/log.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_SYSTEMD
|
||||||
|
#include <systemd/sd-daemon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SELINUX
|
#ifdef HAVE_SELINUX
|
||||||
#include <selinux/selinux.h>
|
#include <selinux/selinux.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -41,7 +45,6 @@
|
||||||
#include <pipewire/impl.h>
|
#include <pipewire/impl.h>
|
||||||
#include <pipewire/extensions/protocol-native.h>
|
#include <pipewire/extensions/protocol-native.h>
|
||||||
|
|
||||||
#include "network-utils.h"
|
|
||||||
#include "pipewire/private.h"
|
#include "pipewire/private.h"
|
||||||
|
|
||||||
#include "modules/module-protocol-native/connection.h"
|
#include "modules/module-protocol-native/connection.h"
|
||||||
|
|
@ -906,12 +909,13 @@ static int add_socket(struct pw_protocol *protocol, struct server *s, struct soc
|
||||||
int fd = -1, res;
|
int fd = -1, res;
|
||||||
bool activated = false;
|
bool activated = false;
|
||||||
|
|
||||||
|
#ifdef HAVE_SYSTEMD
|
||||||
{
|
{
|
||||||
int i, n = listen_fd();
|
int i, n = sd_listen_fds(0);
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
if (is_socket_unix(LISTEN_FDS_START + i, SOCK_STREAM,
|
if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM,
|
||||||
s->addr.sun_path) > 0) {
|
1, s->addr.sun_path, 0) > 0) {
|
||||||
fd = LISTEN_FDS_START + i;
|
fd = SD_LISTEN_FDS_START + i;
|
||||||
activated = true;
|
activated = true;
|
||||||
pw_log_info("server %p: Found socket activation socket for '%s'",
|
pw_log_info("server %p: Found socket activation socket for '%s'",
|
||||||
s, s->addr.sun_path);
|
s, s->addr.sun_path);
|
||||||
|
|
@ -919,6 +923,7 @@ static int add_socket(struct pw_protocol *protocol, struct server *s, struct soc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
struct stat socket_stat;
|
struct stat socket_stat;
|
||||||
|
|
|
||||||
|
|
@ -62,12 +62,8 @@ struct client {
|
||||||
|
|
||||||
struct pw_manager_object *metadata_schema_sm_settings;
|
struct pw_manager_object *metadata_schema_sm_settings;
|
||||||
bool have_force_mono_audio;
|
bool have_force_mono_audio;
|
||||||
bool default_force_mono_audio;
|
|
||||||
bool have_bluetooth_headset_autoswitch;
|
|
||||||
bool default_bluetooth_headset_autoswitch;
|
|
||||||
struct pw_manager_object *metadata_sm_settings;
|
struct pw_manager_object *metadata_sm_settings;
|
||||||
bool force_mono_audio;
|
bool force_mono_audio;
|
||||||
bool bluetooth_headset_autoswitch;
|
|
||||||
|
|
||||||
uint32_t connect_tag;
|
uint32_t connect_tag;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,6 @@
|
||||||
#define MODULE_INDEX_MASK 0xfffffffu
|
#define MODULE_INDEX_MASK 0xfffffffu
|
||||||
#define MODULE_FLAG (1u << 29)
|
#define MODULE_FLAG (1u << 29)
|
||||||
|
|
||||||
#define STREAM_CREATE_TIMEOUT (35 * SPA_NSEC_PER_SEC)
|
|
||||||
|
|
||||||
#define DEFAULT_SINK "@DEFAULT_SINK@"
|
#define DEFAULT_SINK "@DEFAULT_SINK@"
|
||||||
#define DEFAULT_SOURCE "@DEFAULT_SOURCE@"
|
#define DEFAULT_SOURCE "@DEFAULT_SOURCE@"
|
||||||
#define DEFAULT_MONITOR "@DEFAULT_MONITOR@"
|
#define DEFAULT_MONITOR "@DEFAULT_MONITOR@"
|
||||||
|
|
@ -326,6 +324,5 @@ static inline uint32_t port_type_value(const char *port_type)
|
||||||
#define METADATA_TARGET_NODE "target.node"
|
#define METADATA_TARGET_NODE "target.node"
|
||||||
#define METADATA_TARGET_OBJECT "target.object"
|
#define METADATA_TARGET_OBJECT "target.object"
|
||||||
#define METADATA_FEATURES_AUDIO_MONO "node.features.audio.mono"
|
#define METADATA_FEATURES_AUDIO_MONO "node.features.audio.mono"
|
||||||
#define METADATA_BLUETOOTH_HEADSET_AUTOSWITCH "bluetooth.autoswitch-to-headset-profile"
|
|
||||||
|
|
||||||
#endif /* PULSE_SERVER_DEFS_H */
|
#endif /* PULSE_SERVER_DEFS_H */
|
||||||
|
|
|
||||||
|
|
@ -718,7 +718,7 @@ static void on_core_error(void *data, uint32_t id, int seq, int res, const char
|
||||||
{
|
{
|
||||||
struct manager *m = data;
|
struct manager *m = data;
|
||||||
|
|
||||||
if (id == PW_ID_CORE && (res == -EPIPE || res == -EPROTO)) {
|
if (id == PW_ID_CORE && res == -EPIPE) {
|
||||||
pw_log_debug("connection error: %d, %s", res, message);
|
pw_log_debug("connection error: %d, %s", res, message);
|
||||||
manager_emit_disconnect(m);
|
manager_emit_disconnect(m);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,59 +110,14 @@ static int core_object_force_mono_output(struct client *client, const char *para
|
||||||
if (spa_streq(params, "true")) {
|
if (spa_streq(params, "true")) {
|
||||||
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
|
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
|
||||||
METADATA_FEATURES_AUDIO_MONO, "Spa:String:JSON", "true");
|
METADATA_FEATURES_AUDIO_MONO, "Spa:String:JSON", "true");
|
||||||
client->force_mono_audio = true;
|
|
||||||
} else if (spa_streq(params, "false")) {
|
} else if (spa_streq(params, "false")) {
|
||||||
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
|
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
|
||||||
METADATA_FEATURES_AUDIO_MONO, "Spa:String:JSON", "false");
|
METADATA_FEATURES_AUDIO_MONO, "Spa:String:JSON", "false");
|
||||||
client->force_mono_audio = false;
|
|
||||||
} else if (spa_streq(params, "null")) {
|
} else if (spa_streq(params, "null")) {
|
||||||
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
|
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
|
||||||
METADATA_FEATURES_AUDIO_MONO, NULL, NULL);
|
METADATA_FEATURES_AUDIO_MONO, NULL, NULL);
|
||||||
client->force_mono_audio = client->default_force_mono_audio;
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(response, "Value must be true, false, or null");
|
fprintf(response, "Value must be true, false, or clear");
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret < 0)
|
|
||||||
fprintf(response, "Could not set metadata: %s", spa_strerror(ret));
|
|
||||||
else
|
|
||||||
fprintf(response, "%s", params);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int core_object_bluetooth_headset_autoswitch(struct client *client, const char *params, FILE *response)
|
|
||||||
{
|
|
||||||
if (!client->have_bluetooth_headset_autoswitch) {
|
|
||||||
/* Not supported, return a null value to indicate that */
|
|
||||||
fprintf(response, "null");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!params || params[0] == '\0') {
|
|
||||||
/* No parameter => query the current value */
|
|
||||||
fprintf(response, "%s", client->bluetooth_headset_autoswitch ? "true" : "false");
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
/* The caller is trying to set a value or clear with a null */
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (spa_streq(params, "true")) {
|
|
||||||
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
|
|
||||||
METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, "Spa:String:JSON", "true");
|
|
||||||
client->bluetooth_headset_autoswitch = true;
|
|
||||||
} else if (spa_streq(params, "false")) {
|
|
||||||
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
|
|
||||||
METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, "Spa:String:JSON", "false");
|
|
||||||
client->bluetooth_headset_autoswitch = false;
|
|
||||||
} else if (spa_streq(params, "null")) {
|
|
||||||
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
|
|
||||||
METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, NULL, NULL);
|
|
||||||
client->bluetooth_headset_autoswitch = client->default_bluetooth_headset_autoswitch;
|
|
||||||
} else {
|
|
||||||
fprintf(response, "Value must be true, false, or null");
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,8 +145,7 @@ static int core_object_message_handler(struct client *client, struct pw_manager_
|
||||||
" pipewire-pulse:log-level update log level with <params>\n"
|
" pipewire-pulse:log-level update log level with <params>\n"
|
||||||
" pipewire-pulse:list-modules list all module names\n"
|
" pipewire-pulse:list-modules list all module names\n"
|
||||||
" pipewire-pulse:describe-module describe module info for <params>\n"
|
" pipewire-pulse:describe-module describe module info for <params>\n"
|
||||||
" pipewire-pulse:force-mono-output force mono mixdown on all hardware outputs\n"
|
" pipewire-pulse:force-mono-output force mono mixdown on all hardware outputs"
|
||||||
" pipewire-pulse:bluetooth-headset-autoswitch use bluetooth headset mic if available"
|
|
||||||
);
|
);
|
||||||
} else if (spa_streq(message, "list-handlers")) {
|
} else if (spa_streq(message, "list-handlers")) {
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
@ -254,8 +208,6 @@ static int core_object_message_handler(struct client *client, struct pw_manager_
|
||||||
}
|
}
|
||||||
} else if (spa_streq(message, "pipewire-pulse:force-mono-output")) {
|
} else if (spa_streq(message, "pipewire-pulse:force-mono-output")) {
|
||||||
return core_object_force_mono_output(client, params, response);
|
return core_object_force_mono_output(client, params, response);
|
||||||
} else if (spa_streq(message, "pipewire-pulse:bluetooth-headset-autoswitch")) {
|
|
||||||
return core_object_bluetooth_headset_autoswitch(client, params, response);
|
|
||||||
} else {
|
} else {
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -172,8 +172,6 @@ static int module_pipe_source_prepare(struct module * const module)
|
||||||
pw_properties_set(stream_props, PW_KEY_NODE_DRIVER, "true");
|
pw_properties_set(stream_props, PW_KEY_NODE_DRIVER, "true");
|
||||||
if ((str = pw_properties_get(stream_props, PW_KEY_PRIORITY_DRIVER)) == NULL)
|
if ((str = pw_properties_get(stream_props, PW_KEY_PRIORITY_DRIVER)) == NULL)
|
||||||
pw_properties_set(stream_props, PW_KEY_PRIORITY_DRIVER, "50000");
|
pw_properties_set(stream_props, PW_KEY_PRIORITY_DRIVER, "50000");
|
||||||
if ((str = pw_properties_get(stream_props, PW_KEY_PRIORITY_SESSION)) == NULL)
|
|
||||||
pw_properties_set(stream_props, PW_KEY_PRIORITY_SESSION, "2000");
|
|
||||||
|
|
||||||
d->module = module;
|
d->module = module;
|
||||||
d->stream_props = stream_props;
|
d->stream_props = stream_props;
|
||||||
|
|
|
||||||
|
|
@ -973,33 +973,12 @@ static void manager_metadata(void *data, struct pw_manager_object *o,
|
||||||
if (subject == PW_ID_CORE && o == client->metadata_routes)
|
if (subject == PW_ID_CORE && o == client->metadata_routes)
|
||||||
client_update_routes(client, key, value);
|
client_update_routes(client, key, value);
|
||||||
if (subject == PW_ID_CORE && o == client->metadata_schema_sm_settings) {
|
if (subject == PW_ID_CORE && o == client->metadata_schema_sm_settings) {
|
||||||
char default_[16];
|
if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO))
|
||||||
|
|
||||||
if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO)) {
|
|
||||||
client->have_force_mono_audio = true;
|
client->have_force_mono_audio = true;
|
||||||
|
|
||||||
if (spa_json_str_object_find(value, strlen(value),
|
|
||||||
"default", default_, sizeof(default_)) < 0)
|
|
||||||
client->default_force_mono_audio = false;
|
|
||||||
else
|
|
||||||
client->default_force_mono_audio = spa_streq(default_, "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spa_streq(key, METADATA_BLUETOOTH_HEADSET_AUTOSWITCH)) {
|
|
||||||
client->have_bluetooth_headset_autoswitch = true;
|
|
||||||
|
|
||||||
if (spa_json_str_object_find(value, strlen(value),
|
|
||||||
"default", default_, sizeof(default_)) < 0)
|
|
||||||
client->default_bluetooth_headset_autoswitch = false;
|
|
||||||
else
|
|
||||||
client->default_bluetooth_headset_autoswitch = spa_streq(default_, "true");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (subject == PW_ID_CORE && o == client->metadata_sm_settings) {
|
if (subject == PW_ID_CORE && o == client->metadata_sm_settings) {
|
||||||
if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO))
|
if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO))
|
||||||
client->force_mono_audio = spa_streq(value, "true");
|
client->force_mono_audio = spa_streq(value, "true");
|
||||||
if (spa_streq(key, METADATA_BLUETOOTH_HEADSET_AUTOSWITCH))
|
|
||||||
client->bluetooth_headset_autoswitch = spa_streq(value, "true");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1621,7 +1600,7 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui
|
||||||
struct pw_manager_object *o;
|
struct pw_manager_object *o;
|
||||||
bool is_monitor;
|
bool is_monitor;
|
||||||
|
|
||||||
props = pw_properties_new(NULL, NULL);
|
props = pw_properties_copy(client->props);
|
||||||
if (props == NULL)
|
if (props == NULL)
|
||||||
goto error_errno;
|
goto error_errno;
|
||||||
|
|
||||||
|
|
@ -1907,7 +1886,7 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint
|
||||||
struct pw_manager_object *o;
|
struct pw_manager_object *o;
|
||||||
bool is_monitor = false;
|
bool is_monitor = false;
|
||||||
|
|
||||||
props = pw_properties_new(NULL, NULL);
|
props = pw_properties_copy(client->props);
|
||||||
if (props == NULL)
|
if (props == NULL)
|
||||||
goto error_errno;
|
goto error_errno;
|
||||||
|
|
||||||
|
|
@ -2298,7 +2277,7 @@ static int do_create_upload_stream(struct client *client, uint32_t command, uint
|
||||||
struct message *reply;
|
struct message *reply;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if ((props = pw_properties_new(NULL, NULL)) == NULL)
|
if ((props = pw_properties_copy(client->props)) == NULL)
|
||||||
goto error_errno;
|
goto error_errno;
|
||||||
|
|
||||||
if ((res = message_get(m,
|
if ((res = message_get(m,
|
||||||
|
|
@ -4068,45 +4047,6 @@ static const char *get_media_name(struct pw_node_info *info)
|
||||||
return media_name;
|
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,
|
static int fill_sink_input_info(struct client *client, struct message *m,
|
||||||
struct pw_manager_object *o)
|
struct pw_manager_object *o)
|
||||||
{
|
{
|
||||||
|
|
@ -4167,16 +4107,10 @@ static int fill_sink_input_info(struct client *client, struct message *m,
|
||||||
message_put(m,
|
message_put(m,
|
||||||
TAG_BOOLEAN, dev_info.volume_info.mute, /* muted */
|
TAG_BOOLEAN, dev_info.volume_info.mute, /* muted */
|
||||||
TAG_INVALID);
|
TAG_INVALID);
|
||||||
if (client->version >= 13) {
|
if (client->version >= 13)
|
||||||
int res;
|
message_put(m,
|
||||||
struct pw_manager_object *c = NULL;
|
TAG_PROPLIST, info->props,
|
||||||
if (client_id != SPA_ID_INVALID) {
|
TAG_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 >= 19)
|
if (client->version >= 19)
|
||||||
message_put(m,
|
message_put(m,
|
||||||
TAG_BOOLEAN, corked, /* corked */
|
TAG_BOOLEAN, corked, /* corked */
|
||||||
|
|
@ -4252,16 +4186,10 @@ static int fill_source_output_info(struct client *client, struct message *m,
|
||||||
TAG_STRING, "PipeWire", /* resample method */
|
TAG_STRING, "PipeWire", /* resample method */
|
||||||
TAG_STRING, "PipeWire", /* driver */
|
TAG_STRING, "PipeWire", /* driver */
|
||||||
TAG_INVALID);
|
TAG_INVALID);
|
||||||
if (client->version >= 13) {
|
if (client->version >= 13)
|
||||||
int res;
|
message_put(m,
|
||||||
struct pw_manager_object *c = NULL;
|
TAG_PROPLIST, info->props,
|
||||||
if (client_id != SPA_ID_INVALID) {
|
TAG_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 >= 19)
|
if (client->version >= 19)
|
||||||
message_put(m,
|
message_put(m,
|
||||||
TAG_BOOLEAN, corked, /* corked */
|
TAG_BOOLEAN, corked, /* corked */
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,10 @@
|
||||||
#include <pipewire/properties.h>
|
#include <pipewire/properties.h>
|
||||||
#include <pipewire/stream.h>
|
#include <pipewire/stream.h>
|
||||||
|
|
||||||
#include "defs.h"
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "sample.h"
|
#include "sample.h"
|
||||||
#include "sample-play.h"
|
#include "sample-play.h"
|
||||||
#include "internal.h"
|
|
||||||
|
|
||||||
static void sample_play_stream_state_changed(void *data, enum pw_stream_state old,
|
static void sample_play_stream_state_changed(void *data, enum pw_stream_state old,
|
||||||
enum pw_stream_state state, const char *error)
|
enum pw_stream_state state, const char *error)
|
||||||
|
|
@ -32,32 +30,17 @@ static void sample_play_stream_state_changed(void *data, enum pw_stream_state ol
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case PW_STREAM_STATE_UNCONNECTED:
|
case PW_STREAM_STATE_UNCONNECTED:
|
||||||
case PW_STREAM_STATE_ERROR:
|
case PW_STREAM_STATE_ERROR:
|
||||||
pw_timer_queue_cancel(&p->timer);
|
|
||||||
sample_play_emit_done(p, -EIO);
|
sample_play_emit_done(p, -EIO);
|
||||||
break;
|
break;
|
||||||
case PW_STREAM_STATE_PAUSED:
|
case PW_STREAM_STATE_PAUSED:
|
||||||
p->id = pw_stream_get_node_id(p->stream);
|
p->id = pw_stream_get_node_id(p->stream);
|
||||||
sample_play_emit_ready(p, p->id);
|
sample_play_emit_ready(p, p->id);
|
||||||
break;
|
break;
|
||||||
case PW_STREAM_STATE_STREAMING:
|
|
||||||
pw_timer_queue_cancel(&p->timer);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sample_play_start_timeout(void *user_data)
|
|
||||||
{
|
|
||||||
struct sample_play *p = user_data;
|
|
||||||
|
|
||||||
pw_log_info("timeout on sample %s", p->sample->name);
|
|
||||||
|
|
||||||
if (p->stream)
|
|
||||||
pw_stream_set_active(p->stream, false);
|
|
||||||
sample_play_emit_done(p, -ETIMEDOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sample_play_stream_destroy(void *data)
|
static void sample_play_stream_destroy(void *data)
|
||||||
{
|
{
|
||||||
struct sample_play *p = data;
|
struct sample_play *p = data;
|
||||||
|
|
@ -180,10 +163,6 @@ struct sample_play *sample_play_new(struct pw_core *core,
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
goto error_cleanup;
|
goto error_cleanup;
|
||||||
|
|
||||||
/* Time out if we don't get a link; same timeout as for normal streams */
|
|
||||||
pw_timer_queue_add(sample->impl->timer_queue, &p->timer, NULL,
|
|
||||||
STREAM_CREATE_TIMEOUT, sample_play_start_timeout, p);
|
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
|
|
||||||
error_cleanup:
|
error_cleanup:
|
||||||
|
|
@ -202,8 +181,6 @@ void sample_play_destroy(struct sample_play *p)
|
||||||
|
|
||||||
spa_hook_list_clean(&p->hooks);
|
spa_hook_list_clean(&p->hooks);
|
||||||
|
|
||||||
pw_timer_queue_cancel(&p->timer);
|
|
||||||
|
|
||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@
|
||||||
#include <spa/utils/list.h>
|
#include <spa/utils/list.h>
|
||||||
#include <spa/utils/hook.h>
|
#include <spa/utils/hook.h>
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
|
||||||
|
|
||||||
struct sample;
|
struct sample;
|
||||||
struct pw_core;
|
struct pw_core;
|
||||||
struct pw_loop;
|
struct pw_loop;
|
||||||
|
|
@ -43,7 +41,6 @@ struct sample_play {
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
uint32_t stride;
|
uint32_t stride;
|
||||||
struct spa_hook_list hooks;
|
struct spa_hook_list hooks;
|
||||||
struct pw_timer timer;
|
|
||||||
void *user_data;
|
void *user_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@
|
||||||
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_SYSTEMD
|
||||||
|
#include <systemd/sd-daemon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <spa/utils/cleanup.h>
|
#include <spa/utils/cleanup.h>
|
||||||
#include <spa/utils/defs.h>
|
#include <spa/utils/defs.h>
|
||||||
|
|
@ -574,19 +577,26 @@ static bool is_stale_socket(int fd, const struct sockaddr_un *addr_un)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int check_socket_activation(const char *path)
|
#ifdef HAVE_SYSTEMD
|
||||||
|
static int check_systemd_activation(const char *path)
|
||||||
{
|
{
|
||||||
const int n = listen_fd();
|
const int n = sd_listen_fds(0);
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
const int fd = LISTEN_FDS_START + i;
|
const int fd = SD_LISTEN_FDS_START + i;
|
||||||
|
|
||||||
if (is_socket_unix(fd, SOCK_STREAM, path) > 0)
|
if (sd_is_socket_unix(fd, SOCK_STREAM, 1, path, 0) > 0)
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
static inline int check_systemd_activation(SPA_UNUSED const char *path)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int start_unix_server(struct server *server, const struct sockaddr_storage *addr)
|
static int start_unix_server(struct server *server, const struct sockaddr_storage *addr)
|
||||||
{
|
{
|
||||||
|
|
@ -596,10 +606,10 @@ static int start_unix_server(struct server *server, const struct sockaddr_storag
|
||||||
|
|
||||||
spa_assert(addr_un->sun_family == AF_UNIX);
|
spa_assert(addr_un->sun_family == AF_UNIX);
|
||||||
|
|
||||||
fd = check_socket_activation(addr_un->sun_path);
|
fd = check_systemd_activation(addr_un->sun_path);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
server->activated = true;
|
server->activated = true;
|
||||||
pw_log_info("server %p: found socket activation socket for '%s'",
|
pw_log_info("server %p: found systemd socket activation socket for '%s'",
|
||||||
server, addr_un->sun_path);
|
server, addr_un->sun_path);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ struct stream *stream_new(struct client *client, enum stream_type type, uint32_t
|
||||||
/* Time out if we don't get a link and can't send a reply to create in 35s. Client will time out in
|
/* Time out if we don't get a link and can't send a reply to create in 35s. Client will time out in
|
||||||
* 30s and clean up its stream anyway. */
|
* 30s and clean up its stream anyway. */
|
||||||
pw_timer_queue_add(stream->impl->timer_queue, &stream->timer, NULL,
|
pw_timer_queue_add(stream->impl->timer_queue, &stream->timer, NULL,
|
||||||
STREAM_CREATE_TIMEOUT, create_stream_timeout, stream);
|
35 * SPA_NSEC_PER_SEC, create_stream_timeout, stream);
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -384,8 +384,10 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr
|
||||||
}
|
}
|
||||||
|
|
||||||
avahi_address_snprint(at, sizeof(at), a);
|
avahi_address_snprint(at, sizeof(at), a);
|
||||||
if (spa_strstartswith(at, link_local_range))
|
if (spa_strstartswith(at, link_local_range)) {
|
||||||
pw_log_info("found link-local ip address %s for '%s'", at, name);
|
pw_log_info("found link-local ip address %s - skipping tunnel creation", at);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
tinfo = TUNNEL_INFO(.name = name);
|
tinfo = TUNNEL_INFO(.name = name);
|
||||||
|
|
||||||
|
|
@ -412,11 +414,6 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr
|
||||||
(a->data.ipv6.address[1] & 0xc0) == 0x80)
|
(a->data.ipv6.address[1] & 0xc0) == 0x80)
|
||||||
snprintf(if_suffix, sizeof(if_suffix), "%%%d", interface);
|
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.ip", "%s%s", at, if_suffix);
|
||||||
pw_properties_setf(props, "raop.ifindex", "%d", interface);
|
pw_properties_setf(props, "raop.ifindex", "%d", interface);
|
||||||
pw_properties_setf(props, "raop.port", "%u", port);
|
pw_properties_setf(props, "raop.port", "%u", port);
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,6 @@
|
||||||
* - `remote.repair.port = <str>`: remote receiver TCP/UDP port for receiver packets
|
* - `remote.repair.port = <str>`: remote receiver TCP/UDP port for receiver packets
|
||||||
* - `remote.control.port = <str>`: remote receiver TCP/UDP port for control packets
|
* - `remote.control.port = <str>`: remote receiver TCP/UDP port for control packets
|
||||||
* - `fec.code = <str>`: Possible values: `disable`, `rs8m`, `ldpc`
|
* - `fec.code = <str>`: Possible values: `disable`, `rs8m`, `ldpc`
|
||||||
* - `log.level = <str>`: log level for roc-toolkit. Possible values: `DEFAULT`,
|
|
||||||
* `NONE`, `ERROR`, `INFO`, `DEBUG`, `TRACE`; `DEFAULT` follows the log
|
|
||||||
* level of the PipeWire context.
|
|
||||||
*
|
*
|
||||||
* ## General options
|
* ## General options
|
||||||
*
|
*
|
||||||
|
|
@ -78,7 +75,6 @@
|
||||||
* node.name = "roc-sink"
|
* node.name = "roc-sink"
|
||||||
* }
|
* }
|
||||||
* audio.position = [ FL FR ]
|
* audio.position = [ FL FR ]
|
||||||
* log.level = DEFAULT
|
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*]
|
*]
|
||||||
|
|
@ -88,9 +84,8 @@
|
||||||
|
|
||||||
#define NAME "roc-sink"
|
#define NAME "roc-sink"
|
||||||
|
|
||||||
PW_LOG_TOPIC(mod_topic, "mod." NAME);
|
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
|
||||||
#define PW_LOG_TOPIC_DEFAULT mod_topic
|
#define PW_LOG_TOPIC_DEFAULT mod_topic
|
||||||
PW_LOG_TOPIC_EXTERN(roc_log_topic);
|
|
||||||
|
|
||||||
struct module_roc_sink_data {
|
struct module_roc_sink_data {
|
||||||
struct pw_impl_module *module;
|
struct pw_impl_module *module;
|
||||||
|
|
@ -305,8 +300,6 @@ static int roc_sink_setup(struct module_roc_sink_data *data)
|
||||||
|
|
||||||
pw_properties_setf(data->capture_props, PW_KEY_NODE_RATE, "1/%d", info.rate);
|
pw_properties_setf(data->capture_props, PW_KEY_NODE_RATE, "1/%d", info.rate);
|
||||||
|
|
||||||
pw_roc_log_init();
|
|
||||||
|
|
||||||
res = roc_sender_open(data->context, &sender_config, &data->sender);
|
res = roc_sender_open(data->context, &sender_config, &data->sender);
|
||||||
if (res) {
|
if (res) {
|
||||||
pw_log_error("failed to create roc sender: %d", res);
|
pw_log_error("failed to create roc sender: %d", res);
|
||||||
|
|
@ -403,7 +396,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
PW_LOG_TOPIC_INIT(mod_topic);
|
PW_LOG_TOPIC_INIT(mod_topic);
|
||||||
PW_LOG_TOPIC_INIT(roc_log_topic);
|
|
||||||
|
|
||||||
data = calloc(1, sizeof(struct module_roc_sink_data));
|
data = calloc(1, sizeof(struct module_roc_sink_data));
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,6 @@
|
||||||
* - `fec.code = <str>`: Possible values: `default`, `disable`, `rs8m`, `ldpc`
|
* - `fec.code = <str>`: Possible values: `default`, `disable`, `rs8m`, `ldpc`
|
||||||
*
|
*
|
||||||
* - `resampler.profile = <str>`: Deprecated, use roc.resampler.profile
|
* - `resampler.profile = <str>`: Deprecated, use roc.resampler.profile
|
||||||
* - `log.level = <str>`: log level for roc-toolkit. Possible values: `DEFAULT`,
|
|
||||||
* `NONE`, `ERROR`, `INFO`, `DEBUG`, `TRACE`; `DEFAULT` follows the log
|
|
||||||
* level of the PipeWire context.
|
|
||||||
*
|
*
|
||||||
* ## General options
|
* ## General options
|
||||||
*
|
*
|
||||||
|
|
@ -92,7 +89,6 @@
|
||||||
* node.name = "roc-source"
|
* node.name = "roc-source"
|
||||||
* }
|
* }
|
||||||
* audio.position = [ FL FR ]
|
* audio.position = [ FL FR ]
|
||||||
* log.level = DEFAULT
|
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*]
|
*]
|
||||||
|
|
@ -102,9 +98,8 @@
|
||||||
|
|
||||||
#define NAME "roc-source"
|
#define NAME "roc-source"
|
||||||
|
|
||||||
PW_LOG_TOPIC(mod_topic, "mod." NAME);
|
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
|
||||||
#define PW_LOG_TOPIC_DEFAULT mod_topic
|
#define PW_LOG_TOPIC_DEFAULT mod_topic
|
||||||
PW_LOG_TOPIC_EXTERN(roc_log_topic);
|
|
||||||
|
|
||||||
struct module_roc_source_data {
|
struct module_roc_source_data {
|
||||||
struct pw_impl_module *module;
|
struct pw_impl_module *module;
|
||||||
|
|
@ -338,8 +333,6 @@ static int roc_source_setup(struct module_roc_source_data *data)
|
||||||
*/
|
*/
|
||||||
receiver_config.target_latency = (unsigned long long)data->sess_latency_msec * SPA_NSEC_PER_MSEC;
|
receiver_config.target_latency = (unsigned long long)data->sess_latency_msec * SPA_NSEC_PER_MSEC;
|
||||||
|
|
||||||
pw_roc_log_init();
|
|
||||||
|
|
||||||
res = roc_receiver_open(data->context, &receiver_config, &data->receiver);
|
res = roc_receiver_open(data->context, &receiver_config, &data->receiver);
|
||||||
if (res) {
|
if (res) {
|
||||||
pw_log_error("failed to create roc receiver: %d", res);
|
pw_log_error("failed to create roc receiver: %d", res);
|
||||||
|
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
#include <pipewire/log.h>
|
|
||||||
#include <roc/log.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
#include <roc/endpoint.h>
|
#include <roc/endpoint.h>
|
||||||
|
|
||||||
#include <spa/utils/string.h>
|
#include <spa/utils/string.h>
|
||||||
#include <spa/support/log.h>
|
|
||||||
|
|
||||||
#define PW_ROC_DEFAULT_IP "0.0.0.0"
|
#define PW_ROC_DEFAULT_IP "0.0.0.0"
|
||||||
#define PW_ROC_DEFAULT_SOURCE_PORT 10001
|
#define PW_ROC_DEFAULT_SOURCE_PORT 10001
|
||||||
|
|
@ -19,8 +18,6 @@
|
||||||
#define PW_ROC_MULTITRACK_ENCODING_ID 100
|
#define PW_ROC_MULTITRACK_ENCODING_ID 100
|
||||||
#define PW_ROC_STEREO_POSITIONS "[ FL FR ]"
|
#define PW_ROC_STEREO_POSITIONS "[ FL FR ]"
|
||||||
|
|
||||||
void pw_roc_log_init(void);
|
|
||||||
|
|
||||||
static inline int pw_roc_parse_fec_encoding(roc_fec_encoding *out, const char *str)
|
static inline int pw_roc_parse_fec_encoding(roc_fec_encoding *out, const char *str)
|
||||||
{
|
{
|
||||||
if (!str || !*str || spa_streq(str, "default"))
|
if (!str || !*str || spa_streq(str, "default"))
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@ struct impl {
|
||||||
/* Monotonic timestamp of the last time a packet was
|
/* Monotonic timestamp of the last time a packet was
|
||||||
* received. This is accessed with atomic accessors
|
* received. This is accessed with atomic accessors
|
||||||
* to avoid race conditions. */
|
* to avoid race conditions. */
|
||||||
SPA_ALIGNED(8) uint64_t last_packet_time;
|
uint64_t last_packet_time;
|
||||||
|
|
||||||
struct pw_timer standby_timer;
|
struct pw_timer standby_timer;
|
||||||
/* This timer is used when the first stream_start() call fails because
|
/* This timer is used when the first stream_start() call fails because
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,6 @@ static void ringbuffer_clear(struct spa_ringbuffer *rbuf SPA_UNUSED,
|
||||||
memset(iov[1].iov_base, 0, iov[1].iov_len);
|
memset(iov[1].iov_base, 0, iov[1].iov_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint64_t scale_u64(uint64_t val, uint32_t num, uint32_t denom)
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
return ((__uint128_t)val * num) / denom;
|
|
||||||
#else
|
|
||||||
return (uint64_t)((double)val / denom * num);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rtp_audio_process_playback(void *data)
|
static void rtp_audio_process_playback(void *data)
|
||||||
{
|
{
|
||||||
struct impl *impl = data;
|
struct impl *impl = data;
|
||||||
|
|
@ -70,9 +61,6 @@ static void rtp_audio_process_playback(void *data)
|
||||||
* read or write index itself.) */
|
* read or write index itself.) */
|
||||||
|
|
||||||
if (impl->direct_timestamp) {
|
if (impl->direct_timestamp) {
|
||||||
uint32_t num_samples_to_read;
|
|
||||||
uint32_t read_index;
|
|
||||||
|
|
||||||
/* In direct timestamp mode, the focus lies on synchronized playback, not
|
/* In direct timestamp mode, the focus lies on synchronized playback, not
|
||||||
* on a constant latency. The ring buffer fill level is not of interest
|
* on a constant latency. The ring buffer fill level is not of interest
|
||||||
* here. The code in rtp_audio_receive() writes to the ring buffer at
|
* here. The code in rtp_audio_receive() writes to the ring buffer at
|
||||||
|
|
@ -101,32 +89,22 @@ static void rtp_audio_process_playback(void *data)
|
||||||
* timestamp mode, since all of them shift the timestamp by the same
|
* timestamp mode, since all of them shift the timestamp by the same
|
||||||
* `sess.latency.msec` into the future.
|
* `sess.latency.msec` into the future.
|
||||||
*
|
*
|
||||||
* Since in this mode, a constant latency is not important, tracking
|
* "Fill level" makes no sense in this mode, since a constant latency
|
||||||
* the fill level to keep it steady makes no sense. Consequently,
|
* is not important in this mode, so no DLL is needed. Also, matching
|
||||||
* no DLL is needed. Also, matching the pace of the synchronized clock
|
* the pace of the synchronized clock is done by having the graph
|
||||||
* is done by having the graph driver be synchronized to that clock,
|
* driver be synchronized to that clock, which will in turn cause
|
||||||
* which will in turn cause any output sinks to adjust their DLLs
|
* any output sinks to adjust their DLLs (or similar control loop
|
||||||
* (or similar control loop mechanisms) to match the pace of their
|
* mechanisms) to match the pace of their data consumption with the
|
||||||
* data consumption with the pace of the driver.
|
* pace of the driver. */
|
||||||
*
|
|
||||||
* The fill level is still important though to correctly handle corner
|
|
||||||
* cases where the ring buffer is (almost) empty. If fewer samples
|
|
||||||
* are available than what the read operation wants, the deficit
|
|
||||||
* has to be compensated with nullbytes. To that end, the "avail"
|
|
||||||
* quantity tracks how many samples are actually available. */
|
|
||||||
|
|
||||||
if (impl->io_position) {
|
if (impl->io_position) {
|
||||||
uint32_t clock_rate = impl->io_position->clock.rate.denom;
|
/* Use the clock position directly as the read index.
|
||||||
|
* Do NOT add device_delay here - the sink's DLL handles
|
||||||
/* Translate the clock position to an RTP timestamp and
|
* matching its hardware clock to the driver pace. Adding
|
||||||
* shift it to compensate for device delay and ASRC delay.
|
* device_delay would create a feedback loop since rate
|
||||||
* The device delay is scaled along with the clock position,
|
* adjustments affect both ringbuffer and device buffer. */
|
||||||
* since both are expressed in clock sample units, while
|
timestamp = impl->io_position->clock.position;
|
||||||
* pwt.buffered is expressed in stream time. */
|
|
||||||
timestamp = scale_u64(impl->io_position->clock.position + device_delay,
|
|
||||||
impl->rate, clock_rate) + pwt.buffered;
|
|
||||||
spa_ringbuffer_read_update(&impl->ring, timestamp);
|
spa_ringbuffer_read_update(&impl->ring, timestamp);
|
||||||
avail = spa_ringbuffer_get_read_index(&impl->ring, &read_index);
|
|
||||||
} else {
|
} else {
|
||||||
/* In the unlikely case that no spa_io_position pointer
|
/* In the unlikely case that no spa_io_position pointer
|
||||||
* was passed yet by PipeWire to this node, resort to a
|
* was passed yet by PipeWire to this node, resort to a
|
||||||
|
|
@ -134,50 +112,14 @@ static void rtp_audio_process_playback(void *data)
|
||||||
* This most likely is not in sync with other nodes,
|
* This most likely is not in sync with other nodes,
|
||||||
* but _something_ is needed as read index until the
|
* but _something_ is needed as read index until the
|
||||||
* spa_io_position is available. */
|
* spa_io_position is available. */
|
||||||
avail = spa_ringbuffer_get_read_index(&impl->ring, ×tamp);
|
spa_ringbuffer_get_read_index(&impl->ring, ×tamp);
|
||||||
read_index = timestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If avail is 0, it means that the ring buffer is empty. <0 means
|
|
||||||
* that there is an underrun, typically because the PTP time now
|
|
||||||
* is ahead of the RTP data (this can happen when the PTP master
|
|
||||||
* changes for example). And in cases where only a little bit of
|
|
||||||
* data is left, it is important to not try to use more than what
|
|
||||||
* is actually available.
|
|
||||||
* Overruns would happen if the write pointer is further ahead than
|
|
||||||
* what the ringbuffer size actually allows. This too can happen
|
|
||||||
* if the PTP time jumps. No actual buffer overflow would happen
|
|
||||||
* then, since the write operations always apply modulo to the
|
|
||||||
* timestamps to wrap around the ringbuffer borders.
|
|
||||||
*/
|
|
||||||
bool has_underrun = (avail < 0);
|
|
||||||
bool has_overrun = !has_underrun && ((uint32_t)avail) > impl->actual_max_buffer_size;
|
|
||||||
num_samples_to_read = has_underrun ? 0 : SPA_MIN((uint32_t)avail, wanted);
|
|
||||||
|
|
||||||
/* Do some additional logging in the under/overrun cases. */
|
|
||||||
if (SPA_UNLIKELY(pw_log_topic_enabled(SPA_LOG_LEVEL_TRACE, PW_LOG_TOPIC_DEFAULT)))
|
|
||||||
{
|
|
||||||
uint32_t write_index;
|
|
||||||
int32_t filled = spa_ringbuffer_get_write_index(&impl->ring, &write_index);
|
|
||||||
|
|
||||||
if (has_underrun) {
|
|
||||||
pw_log_trace("Direct timestamp mode: Read index underrun: write_index: %"
|
|
||||||
PRIu32 ", read_index: %" PRIu32 ", wanted: %u - filled: %" PRIi32,
|
|
||||||
write_index, read_index, wanted, filled);
|
|
||||||
} else if (has_overrun) {
|
|
||||||
pw_log_trace("Direct timestamp mode: Read index overrun: write_index: %"
|
|
||||||
PRIu32 ", read_index: %" PRIu32 ", wanted: %u - filled: %" PRIi32
|
|
||||||
", buffer size: %u", write_index, read_index, wanted, filled,
|
|
||||||
impl->actual_max_buffer_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num_samples_to_read > 0) {
|
|
||||||
spa_ringbuffer_read_data(&impl->ring,
|
spa_ringbuffer_read_data(&impl->ring,
|
||||||
impl->buffer,
|
impl->buffer,
|
||||||
impl->actual_max_buffer_size,
|
impl->actual_max_buffer_size,
|
||||||
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
|
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
|
||||||
d[0].data, num_samples_to_read * stride);
|
d[0].data, wanted * stride);
|
||||||
|
|
||||||
/* Clear the bytes that were just retrieved. Since the fill level
|
/* Clear the bytes that were just retrieved. Since the fill level
|
||||||
* is not tracked in this buffer mode, it is possible that as soon
|
* is not tracked in this buffer mode, it is possible that as soon
|
||||||
|
|
@ -189,17 +131,7 @@ static void rtp_audio_process_playback(void *data)
|
||||||
impl->buffer,
|
impl->buffer,
|
||||||
impl->actual_max_buffer_size,
|
impl->actual_max_buffer_size,
|
||||||
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
|
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
|
||||||
num_samples_to_read * stride);
|
wanted * stride);
|
||||||
}
|
|
||||||
|
|
||||||
if (num_samples_to_read < wanted) {
|
|
||||||
/* If fewer samples were available than what was wanted,
|
|
||||||
* fill the remaining space in the destination memory
|
|
||||||
* with nullsamples. */
|
|
||||||
void *bytes_to_clear = SPA_PTROFF(d[0].data, num_samples_to_read * stride, void);
|
|
||||||
size_t num_bytes_to_clear = (wanted - num_samples_to_read) * stride;
|
|
||||||
spa_memzero(bytes_to_clear, num_bytes_to_clear);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!impl->io_position) {
|
if (!impl->io_position) {
|
||||||
/* In the unlikely case that no spa_io_position pointer
|
/* In the unlikely case that no spa_io_position pointer
|
||||||
|
|
@ -290,25 +222,6 @@ static void rtp_audio_process_playback(void *data)
|
||||||
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
|
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
|
||||||
d[0].data, wanted * stride);
|
d[0].data, wanted * stride);
|
||||||
|
|
||||||
/* Clear the bytes that were just retrieved. Unlike in the
|
|
||||||
* direct timestamp mode, here, bytes are always read out
|
|
||||||
* of the ring buffer in sequence - the read pointer does
|
|
||||||
* not "jump around" (which can happen in direct timestamp
|
|
||||||
* mode if the last iteration has been a while ago and the
|
|
||||||
* driver clock time advanced significantly, or if the driver
|
|
||||||
* time experienced a discontinuity). However, should there
|
|
||||||
* be packet loss, it could lead to segments in the ring
|
|
||||||
* buffer that should have been written to but weren't written
|
|
||||||
* to. These segments would then contain old stale data. By
|
|
||||||
* clearing data out of the ring buffer after reading it, it
|
|
||||||
* is ensured that no stale data can exist - in the packet loss
|
|
||||||
* case, the outcome would be a gap made of nullsamples instead. */
|
|
||||||
ringbuffer_clear(&impl->ring,
|
|
||||||
impl->buffer,
|
|
||||||
impl->actual_max_buffer_size,
|
|
||||||
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
|
|
||||||
wanted * stride);
|
|
||||||
|
|
||||||
timestamp += wanted;
|
timestamp += wanted;
|
||||||
spa_ringbuffer_read_update(&impl->ring, timestamp);
|
spa_ringbuffer_read_update(&impl->ring, timestamp);
|
||||||
}
|
}
|
||||||
|
|
@ -421,43 +334,17 @@ static int rtp_audio_receive(struct impl *impl, uint8_t *buffer, ssize_t len,
|
||||||
* and not _appended_. In this example, `expected_write` would
|
* and not _appended_. In this example, `expected_write` would
|
||||||
* be 100 (since `expected_write` is the current write index),
|
* be 100 (since `expected_write` is the current write index),
|
||||||
* `write` would be 90, `samples` would be 10. In this case,
|
* `write` would be 90, `samples` would be 10. In this case,
|
||||||
* the (expected_write < (write + samples)) inequality does
|
* the inequality below does not hold, so data is being
|
||||||
* not hold, so data is being _inserted_. By contrast, during
|
* _inserted_. By contrast, during normal operation, `write`
|
||||||
* normal operation, `write` and `expected_write` are equal,
|
* and `expected_write` are equal, so the inequality below
|
||||||
* so the aforementioned inequality _does_ hold, meaning that
|
* _does_ hold, meaning that data is being appended.
|
||||||
* data is being appended.
|
|
||||||
*
|
|
||||||
* The code below handles this, and also handles a 32-bit
|
|
||||||
* integer overflow corner case where the comparison has
|
|
||||||
* to be done differently to account for the wrap-around.
|
|
||||||
*
|
*
|
||||||
* (Note that this write index update is only important if
|
* (Note that this write index update is only important if
|
||||||
* the constant delay mode is active, or if no spa_io_position
|
* the constant delay mode is active, or if no spa_io_position
|
||||||
* was not provided yet. See the rtp_audio_process_playback()
|
* was not provided yet. See the rtp_audio_process_playback()
|
||||||
* code for more about this.) */
|
* code for more about this.) */
|
||||||
|
if (expected_write < (write + samples)) {
|
||||||
/* Compute new_write, handling potential 32-bit overflow.
|
write += samples;
|
||||||
* In unsigned arithmetic, if write + samples exceeds UINT32_MAX,
|
|
||||||
* it wraps around to a smaller value. We detect this by checking
|
|
||||||
* if new_write < write (which can only happen on overflow). */
|
|
||||||
const uint32_t new_write = write + samples;
|
|
||||||
const bool wrapped_around = new_write < write;
|
|
||||||
|
|
||||||
/* Determine if new_write is ahead of expected_write.
|
|
||||||
* We're appending (ahead) if:
|
|
||||||
*
|
|
||||||
* 1. Normal case: new_write > expected_write (forward progress)
|
|
||||||
* 2. Wrap-around case: new_write wrapped around (wrapped_around == true),
|
|
||||||
* meaning we've cycled through the 32-bit index space and are
|
|
||||||
* continuing from the beginning. In this case, we're always ahead.
|
|
||||||
*
|
|
||||||
* We're NOT appending (inserting/behind) if:
|
|
||||||
* - new_write <= expected_write AND no wrap-around occurred
|
|
||||||
* (we're filling a gap or writing behind the current position) */
|
|
||||||
const bool is_appending = wrapped_around || (new_write > expected_write);
|
|
||||||
|
|
||||||
if (is_appending) {
|
|
||||||
write = new_write;
|
|
||||||
spa_ringbuffer_write_update(&impl->ring, write);
|
spa_ringbuffer_write_update(&impl->ring, write);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -539,27 +426,20 @@ static void rtp_audio_flush_packets(struct impl *impl, uint32_t num_packets, uin
|
||||||
iov[0].iov_len = sizeof(header);
|
iov[0].iov_len = sizeof(header);
|
||||||
|
|
||||||
while (num_packets > 0) {
|
while (num_packets > 0) {
|
||||||
uint32_t rtp_timestamp;
|
|
||||||
|
|
||||||
if (impl->marker_on_first && impl->first)
|
if (impl->marker_on_first && impl->first)
|
||||||
header.m = 1;
|
header.m = 1;
|
||||||
else
|
else
|
||||||
header.m = 0;
|
header.m = 0;
|
||||||
|
|
||||||
rtp_timestamp = impl->ts_offset + (set_timestamp ? set_timestamp : timestamp);
|
|
||||||
|
|
||||||
header.sequence_number = htons(impl->seq);
|
header.sequence_number = htons(impl->seq);
|
||||||
header.timestamp = htonl(rtp_timestamp);
|
header.timestamp = htonl(impl->ts_offset + (set_timestamp ? set_timestamp : timestamp));
|
||||||
|
|
||||||
set_iovec(&impl->ring,
|
set_iovec(&impl->ring,
|
||||||
impl->buffer, impl->actual_max_buffer_size,
|
impl->buffer, impl->actual_max_buffer_size,
|
||||||
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
|
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
|
||||||
&iov[1], tosend * stride);
|
&iov[1], tosend * stride);
|
||||||
|
|
||||||
pw_log_trace("sending %d packet:%d ts_offset:%d timestamp:%u (%f s)",
|
pw_log_trace("sending %d packet:%d ts_offset:%d timestamp:%d",
|
||||||
tosend, num_packets, impl->ts_offset, timestamp,
|
tosend, num_packets, impl->ts_offset, timestamp);
|
||||||
(double)timestamp * impl->io_position->clock.rate.num /
|
|
||||||
impl->io_position->clock.rate.denom);
|
|
||||||
|
|
||||||
rtp_stream_emit_send_packet(impl, iov, 3);
|
rtp_stream_emit_send_packet(impl, iov, 3);
|
||||||
|
|
||||||
|
|
@ -620,7 +500,6 @@ static void rtp_audio_process_capture(void *data)
|
||||||
uint32_t pending, num_queued;
|
uint32_t pending, num_queued;
|
||||||
struct spa_io_position *pos;
|
struct spa_io_position *pos;
|
||||||
uint64_t next_nsec, quantum;
|
uint64_t next_nsec, quantum;
|
||||||
struct pw_time pwt;
|
|
||||||
|
|
||||||
if (impl->separate_sender) {
|
if (impl->separate_sender) {
|
||||||
/* apply the DLL rate */
|
/* apply the DLL rate */
|
||||||
|
|
@ -638,8 +517,6 @@ static void rtp_audio_process_capture(void *data)
|
||||||
stride = impl->stride;
|
stride = impl->stride;
|
||||||
wanted = size / stride;
|
wanted = size / stride;
|
||||||
|
|
||||||
pw_stream_get_time_n(impl->stream, &pwt, sizeof(pwt));
|
|
||||||
|
|
||||||
filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_timestamp);
|
filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_timestamp);
|
||||||
|
|
||||||
pos = impl->io_position;
|
pos = impl->io_position;
|
||||||
|
|
@ -656,21 +533,6 @@ static void rtp_audio_process_capture(void *data)
|
||||||
impl->sink_resamp_delay = impl->io_rate_match->delay;
|
impl->sink_resamp_delay = impl->io_rate_match->delay;
|
||||||
impl->sink_quantum = (uint64_t)(pos->clock.duration * SPA_NSEC_PER_SEC / rate);
|
impl->sink_quantum = (uint64_t)(pos->clock.duration * SPA_NSEC_PER_SEC / rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compensate for the stream resampler's delay. */
|
|
||||||
actual_timestamp -= pwt.buffered;
|
|
||||||
|
|
||||||
/* If we got a request for less than quantum worth of samples, it indicates that there
|
|
||||||
* is a gap created by the resampler. We have to skip it to avoid timestamp discontinuity. */
|
|
||||||
if (pwt.buffered > 0) {
|
|
||||||
int32_t ideal_quantum = (int32_t)scale_u64(pos->clock.duration, impl->rate, rate);
|
|
||||||
if (wanted < ideal_quantum) {
|
|
||||||
int32_t num_samples_to_skip = ideal_quantum - wanted;
|
|
||||||
pw_log_info("wanted: %" PRId32 " < ideal quantum: %" PRId32 " - skipping %"
|
|
||||||
PRId32" samples", wanted, ideal_quantum, num_samples_to_skip);
|
|
||||||
actual_timestamp += num_samples_to_skip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
actual_timestamp = expected_timestamp;
|
actual_timestamp = expected_timestamp;
|
||||||
next_nsec = 0;
|
next_nsec = 0;
|
||||||
|
|
@ -707,8 +569,7 @@ static void rtp_audio_process_capture(void *data)
|
||||||
if (!impl->have_sync) {
|
if (!impl->have_sync) {
|
||||||
pw_log_info("(re)sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u",
|
pw_log_info("(re)sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u",
|
||||||
actual_timestamp, impl->seq, impl->ts_offset, impl->ssrc);
|
actual_timestamp, impl->seq, impl->ts_offset, impl->ssrc);
|
||||||
spa_ringbuffer_read_update(&impl->ring, actual_timestamp);
|
impl->ring.readindex = impl->ring.writeindex = actual_timestamp;
|
||||||
spa_ringbuffer_write_update(&impl->ring, actual_timestamp);
|
|
||||||
memset(impl->buffer, 0, BUFFER_SIZE);
|
memset(impl->buffer, 0, BUFFER_SIZE);
|
||||||
impl->have_sync = true;
|
impl->have_sync = true;
|
||||||
expected_timestamp = actual_timestamp;
|
expected_timestamp = actual_timestamp;
|
||||||
|
|
|
||||||
|
|
@ -454,10 +454,6 @@ static int stream_stop(struct impl *impl)
|
||||||
* meaning that the timer was no longer running, and the connection
|
* meaning that the timer was no longer running, and the connection
|
||||||
* could be closed. */
|
* could be closed. */
|
||||||
if (!timer_running) {
|
if (!timer_running) {
|
||||||
/* Clear the ringbuffer to prevent old invalid packets from being
|
|
||||||
* sent when processing resumes via rtp_audio_flush_packets() */
|
|
||||||
if (impl->reset_ringbuffer)
|
|
||||||
impl->reset_ringbuffer(impl);
|
|
||||||
set_internal_stream_state(impl, RTP_STREAM_INTERNAL_STATE_STOPPED);
|
set_internal_stream_state(impl, RTP_STREAM_INTERNAL_STATE_STOPPED);
|
||||||
pw_log_info("stream stopped");
|
pw_log_info("stream stopped");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -162,8 +162,7 @@ static const struct spa_dict_item module_props[] = {
|
||||||
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
|
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SERVICE_TYPE_JSONRPC "_snapcast-jsonrpc._tcp"
|
#define SERVICE_TYPE_CONTROL "_snapcast-jsonrpc._tcp"
|
||||||
#define SERVICE_TYPE_CONTROL "_snapcast-ctrl._tcp"
|
|
||||||
|
|
||||||
struct impl {
|
struct impl {
|
||||||
struct pw_context *context;
|
struct pw_context *context;
|
||||||
|
|
@ -177,8 +176,7 @@ struct impl {
|
||||||
|
|
||||||
AvahiPoll *avahi_poll;
|
AvahiPoll *avahi_poll;
|
||||||
AvahiClient *client;
|
AvahiClient *client;
|
||||||
AvahiServiceBrowser *jsonrpc_browser;
|
AvahiServiceBrowser *sink_browser;
|
||||||
AvahiServiceBrowser *ctrl_browser;
|
|
||||||
|
|
||||||
struct spa_list tunnel_list;
|
struct spa_list tunnel_list;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
|
@ -254,10 +252,8 @@ static void impl_free(struct impl *impl)
|
||||||
spa_list_consume(t, &impl->tunnel_list, link)
|
spa_list_consume(t, &impl->tunnel_list, link)
|
||||||
free_tunnel(t);
|
free_tunnel(t);
|
||||||
|
|
||||||
if (impl->jsonrpc_browser)
|
if (impl->sink_browser)
|
||||||
avahi_service_browser_free(impl->jsonrpc_browser);
|
avahi_service_browser_free(impl->sink_browser);
|
||||||
if (impl->ctrl_browser)
|
|
||||||
avahi_service_browser_free(impl->ctrl_browser);
|
|
||||||
if (impl->client)
|
if (impl->client)
|
||||||
avahi_client_free(impl->client);
|
avahi_client_free(impl->client);
|
||||||
if (impl->avahi_poll)
|
if (impl->avahi_poll)
|
||||||
|
|
@ -640,9 +636,10 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr
|
||||||
}
|
}
|
||||||
|
|
||||||
avahi_address_snprint(at, sizeof(at), a);
|
avahi_address_snprint(at, sizeof(at), a);
|
||||||
if (spa_strstartswith(at, link_local_range))
|
if (spa_strstartswith(at, link_local_range)) {
|
||||||
pw_log_info("found link-local ip address %s for '%s'", at, name);
|
pw_log_info("found link-local ip address %s - skipping tunnel creation", at);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
pw_log_info("%s %s", name, at);
|
pw_log_info("%s %s", name, at);
|
||||||
|
|
||||||
tinfo = TUNNEL_INFO(.name = name, .port = port);
|
tinfo = TUNNEL_INFO(.name = name, .port = port);
|
||||||
|
|
@ -670,11 +667,6 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr
|
||||||
(a->data.ipv6.address[1] & 0xc0) == 0x80)
|
(a->data.ipv6.address[1] & 0xc0) == 0x80)
|
||||||
snprintf(if_suffix, sizeof(if_suffix), "%%%d", interface);
|
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, "snapcast.ip", "%s%s", at, if_suffix);
|
pw_properties_setf(props, "snapcast.ip", "%s%s", at, if_suffix);
|
||||||
pw_properties_setf(props, "snapcast.ifindex", "%d", interface);
|
pw_properties_setf(props, "snapcast.ifindex", "%d", interface);
|
||||||
pw_properties_setf(props, "snapcast.port", "%u", port);
|
pw_properties_setf(props, "snapcast.port", "%u", port);
|
||||||
|
|
@ -826,13 +818,9 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
|
||||||
case AVAHI_CLIENT_S_REGISTERING:
|
case AVAHI_CLIENT_S_REGISTERING:
|
||||||
case AVAHI_CLIENT_S_RUNNING:
|
case AVAHI_CLIENT_S_RUNNING:
|
||||||
case AVAHI_CLIENT_S_COLLISION:
|
case AVAHI_CLIENT_S_COLLISION:
|
||||||
if (impl->ctrl_browser == NULL)
|
if (impl->sink_browser == NULL)
|
||||||
impl->ctrl_browser = make_browser(impl, SERVICE_TYPE_CONTROL);
|
impl->sink_browser = make_browser(impl, SERVICE_TYPE_CONTROL);
|
||||||
if (impl->ctrl_browser == NULL)
|
if (impl->sink_browser == NULL)
|
||||||
goto error;
|
|
||||||
if (impl->jsonrpc_browser == NULL)
|
|
||||||
impl->jsonrpc_browser = make_browser(impl, SERVICE_TYPE_JSONRPC);
|
|
||||||
if (impl->jsonrpc_browser == NULL)
|
|
||||||
goto error;
|
goto error;
|
||||||
break;
|
break;
|
||||||
case AVAHI_CLIENT_FAILURE:
|
case AVAHI_CLIENT_FAILURE:
|
||||||
|
|
@ -841,13 +829,9 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
|
||||||
|
|
||||||
SPA_FALLTHROUGH;
|
SPA_FALLTHROUGH;
|
||||||
case AVAHI_CLIENT_CONNECTING:
|
case AVAHI_CLIENT_CONNECTING:
|
||||||
if (impl->ctrl_browser) {
|
if (impl->sink_browser) {
|
||||||
avahi_service_browser_free(impl->ctrl_browser);
|
avahi_service_browser_free(impl->sink_browser);
|
||||||
impl->ctrl_browser = NULL;
|
impl->sink_browser = NULL;
|
||||||
}
|
|
||||||
if (impl->jsonrpc_browser) {
|
|
||||||
avahi_service_browser_free(impl->jsonrpc_browser);
|
|
||||||
impl->jsonrpc_browser = NULL;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,6 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
|
|
||||||
#include <spa/utils/string.h>
|
|
||||||
|
|
||||||
#ifdef __FreeBSD__
|
#ifdef __FreeBSD__
|
||||||
#define ifr_ifindex ifr_index
|
#define ifr_ifindex ifr_index
|
||||||
|
|
@ -137,70 +131,5 @@ static inline bool pw_net_addr_is_any(struct sockaddr_storage *addr)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef LISTEN_FDS_START
|
|
||||||
#define LISTEN_FDS_START 3
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Returns the number of file descriptors passed for socket activation.
|
|
||||||
* Returns 0 if none, -1 on error. */
|
|
||||||
static inline int listen_fd(void)
|
|
||||||
{
|
|
||||||
uint32_t n;
|
|
||||||
int i, flags;
|
|
||||||
|
|
||||||
if (!spa_atou32(getenv("LISTEN_FDS"), &n, 10) || n > INT_MAX - LISTEN_FDS_START) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < (int)n; i++) {
|
|
||||||
flags = fcntl(LISTEN_FDS_START + i, F_GETFD);
|
|
||||||
if (flags == -1)
|
|
||||||
return -1;
|
|
||||||
if (fcntl(LISTEN_FDS_START + i, F_SETFD, flags | FD_CLOEXEC) == -1)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsetenv("LISTEN_FDS");
|
|
||||||
|
|
||||||
return (int)n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the fd is a listening unix socket of the given type,
|
|
||||||
* optionally bound to the given path. */
|
|
||||||
static inline int is_socket_unix(int fd, int type, const char *path)
|
|
||||||
{
|
|
||||||
struct sockaddr_un addr;
|
|
||||||
int val;
|
|
||||||
socklen_t len = sizeof(val);
|
|
||||||
|
|
||||||
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &val, &len) < 0)
|
|
||||||
return -errno;
|
|
||||||
if (val != type)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &val, &len) < 0)
|
|
||||||
return -errno;
|
|
||||||
if (!val)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
len = sizeof(addr);
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
|
||||||
if (getsockname(fd, (struct sockaddr *)&addr, &len) < 0)
|
|
||||||
return -errno;
|
|
||||||
if (addr.sun_family != AF_UNIX)
|
|
||||||
return 0;
|
|
||||||
size_t length = strlen(path);
|
|
||||||
if (length > 0) {
|
|
||||||
if (len < offsetof(struct sockaddr_un, sun_path) + length)
|
|
||||||
return 0;
|
|
||||||
if (memcmp(addr.sun_path, path, length) != 0)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* NETWORK_UTILS_H */
|
#endif /* NETWORK_UTILS_H */
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@
|
||||||
PW_LOG_TOPIC_EXTERN(log_context);
|
PW_LOG_TOPIC_EXTERN(log_context);
|
||||||
#define PW_LOG_TOPIC_DEFAULT log_context
|
#define PW_LOG_TOPIC_DEFAULT log_context
|
||||||
|
|
||||||
|
#define MAX_HOPS 64
|
||||||
|
#define MAX_SYNC 4u
|
||||||
#define MAX_LOOPS 64u
|
#define MAX_LOOPS 64u
|
||||||
|
|
||||||
#define DEFAULT_DATA_LOOPS 1
|
#define DEFAULT_DATA_LOOPS 1
|
||||||
|
|
@ -110,17 +112,13 @@ static void fill_core_properties(struct pw_context *context)
|
||||||
pw_properties_set(properties, PW_KEY_CORE_NAME, context->core->info.name);
|
pw_properties_set(properties, PW_KEY_CORE_NAME, context->core->info.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPA_EXPORT
|
static int context_set_freewheel(struct pw_context *context, bool freewheel)
|
||||||
int pw_context_set_freewheel(struct pw_context *context, bool freewheel)
|
|
||||||
{
|
{
|
||||||
struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
|
struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
|
||||||
struct spa_thread *thr;
|
struct spa_thread *thr;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
if (context->freewheeling == freewheel)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (i = 0; i < impl->n_data_loops; i++) {
|
for (i = 0; i < impl->n_data_loops; i++) {
|
||||||
if (impl->data_loops[i].impl == NULL ||
|
if (impl->data_loops[i].impl == NULL ||
|
||||||
(thr = pw_data_loop_get_thread(impl->data_loops[i].impl)) == NULL)
|
(thr = pw_data_loop_get_thread(impl->data_loops[i].impl)) == NULL)
|
||||||
|
|
@ -984,9 +982,468 @@ SPA_PRINTF_FUNC(7, 8) int pw_context_debug_port_params(struct pw_context *this,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ensure_state(struct pw_impl_node *node, bool running)
|
||||||
|
{
|
||||||
|
enum pw_node_state state = node->info.state;
|
||||||
|
if (node->active && node->runnable &&
|
||||||
|
!SPA_FLAG_IS_SET(node->spa_flags, SPA_NODE_FLAG_NEED_CONFIGURE) && running)
|
||||||
|
state = PW_NODE_STATE_RUNNING;
|
||||||
|
else if (state > PW_NODE_STATE_IDLE)
|
||||||
|
state = PW_NODE_STATE_IDLE;
|
||||||
|
return pw_impl_node_set_state(node, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* From a node (that is runnable) follow all prepared links in the given direction
|
||||||
|
* and groups to active nodes and make them recursively runnable as well.
|
||||||
|
*/
|
||||||
|
static inline int run_nodes(struct pw_context *context, struct pw_impl_node *node,
|
||||||
|
struct spa_list *nodes, enum pw_direction direction, int hop)
|
||||||
|
{
|
||||||
|
struct pw_impl_node *t;
|
||||||
|
struct pw_impl_port *p;
|
||||||
|
struct pw_impl_link *l;
|
||||||
|
|
||||||
|
if (hop == MAX_HOPS) {
|
||||||
|
pw_log_warn("exceeded hops (%d)", hop);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_log_debug("node %p: '%s' direction:%s", node, node->name,
|
||||||
|
pw_direction_as_string(direction));
|
||||||
|
|
||||||
|
SPA_FLAG_SET(node->checked, 1u<<direction);
|
||||||
|
|
||||||
|
if (direction == PW_DIRECTION_INPUT) {
|
||||||
|
spa_list_for_each(p, &node->input_ports, link) {
|
||||||
|
spa_list_for_each(l, &p->links, input_link) {
|
||||||
|
t = l->output->node;
|
||||||
|
|
||||||
|
if (!t->active || !l->prepared ||
|
||||||
|
(!t->driving && SPA_FLAG_IS_SET(t->checked, 1u<<direction)))
|
||||||
|
continue;
|
||||||
|
if (t->driving && p->node == t)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pw_log_debug(" peer %p: '%s'", t, t->name);
|
||||||
|
t->runnable = true;
|
||||||
|
run_nodes(context, t, nodes, direction, hop + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spa_list_for_each(p, &node->output_ports, link) {
|
||||||
|
spa_list_for_each(l, &p->links, output_link) {
|
||||||
|
t = l->input->node;
|
||||||
|
|
||||||
|
if (!t->active || !l->prepared ||
|
||||||
|
(!t->driving && SPA_FLAG_IS_SET(t->checked, 1u<<direction)))
|
||||||
|
continue;
|
||||||
|
if (t->driving && p->node == t)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pw_log_debug(" peer %p: '%s'", t, t->name);
|
||||||
|
t->runnable = true;
|
||||||
|
run_nodes(context, t, nodes, direction, hop + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* now go through all the nodes that have the same link group and
|
||||||
|
* that are not yet visited. Note how nodes with the same group
|
||||||
|
* don't get included here. They were added to the same driver but
|
||||||
|
* need to otherwise stay idle unless some non-passive link activates
|
||||||
|
* them. */
|
||||||
|
if (node->link_groups != NULL) {
|
||||||
|
spa_list_for_each(t, nodes, sort_link) {
|
||||||
|
if (t->exported || !t->active ||
|
||||||
|
SPA_FLAG_IS_SET(t->checked, 1u<<direction))
|
||||||
|
continue;
|
||||||
|
if (pw_strv_find_common(t->link_groups, node->link_groups) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pw_log_debug(" group %p: '%s'", t, t->name);
|
||||||
|
t->runnable = true;
|
||||||
|
if (!t->driving)
|
||||||
|
run_nodes(context, t, nodes, direction, hop + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Follow all prepared links and groups from node, activate the links.
|
||||||
|
* If a non-passive link is found, we set the peer runnable flag.
|
||||||
|
*
|
||||||
|
* After this is done, we end up with a list of nodes in collect that are all
|
||||||
|
* linked to node.
|
||||||
|
* Some of the nodes have the runnable flag set. We then start from those nodes
|
||||||
|
* and make all linked nodes and groups runnable as well. (see run_nodes).
|
||||||
|
*
|
||||||
|
* This ensures that we only activate the paths from the runnable nodes to the
|
||||||
|
* driver nodes and leave the other nodes idle.
|
||||||
|
*/
|
||||||
|
static int collect_nodes(struct pw_context *context, struct pw_impl_node *node, struct spa_list *collect)
|
||||||
|
{
|
||||||
|
struct spa_list queue;
|
||||||
|
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->active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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->active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pw_impl_link_prepare(l);
|
||||||
|
|
||||||
|
if (!l->prepared)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!l->passive)
|
||||||
|
t->runnable = true;
|
||||||
|
|
||||||
|
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->active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pw_impl_link_prepare(l);
|
||||||
|
|
||||||
|
if (!l->prepared)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!l->passive)
|
||||||
|
t->runnable = true;
|
||||||
|
|
||||||
|
if (!t->visited) {
|
||||||
|
t->visited = true;
|
||||||
|
spa_list_append(&queue, &t->sort_link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* now go through all the nodes that have the same group and
|
||||||
|
* that are not yet visited */
|
||||||
|
if (n->groups != NULL || n->link_groups != NULL || sync[0] != NULL) {
|
||||||
|
spa_list_for_each(t, &context->node_list, link) {
|
||||||
|
if (t->exported || !t->active || t->visited)
|
||||||
|
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(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);
|
||||||
|
}
|
||||||
|
/* All non-driver runnable nodes (ie. reachable with a non-passive link) now make
|
||||||
|
* all linked nodes up and downstream runnable as well */
|
||||||
|
spa_list_for_each(n, collect, sort_link) {
|
||||||
|
if (!n->driver && n->runnable) {
|
||||||
|
run_nodes(context, n, collect, PW_DIRECTION_OUTPUT, 0);
|
||||||
|
run_nodes(context, n, collect, PW_DIRECTION_INPUT, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* now we might have made a driver runnable, if the node is not runnable at this point
|
||||||
|
* it means it was linked to the driver with passives links and some other node
|
||||||
|
* made the driver active. If the node is a leaf it can not be activated in any other
|
||||||
|
* way and we will also make it, and all its peers, runnable */
|
||||||
|
spa_list_for_each(n, collect, sort_link) {
|
||||||
|
if (!n->driver && n->driver_node->runnable && !n->runnable && n->leaf && n->active) {
|
||||||
|
n->runnable = true;
|
||||||
|
run_nodes(context, n, collect, PW_DIRECTION_OUTPUT, 0);
|
||||||
|
run_nodes(context, n, collect, PW_DIRECTION_INPUT, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 3 stages:
|
||||||
|
*
|
||||||
|
* 1. 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.
|
||||||
|
*
|
||||||
|
* 2. 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 1.
|
||||||
|
*
|
||||||
|
* 3. 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.
|
||||||
|
*/
|
||||||
int pw_context_recalc_graph(struct pw_context *context, const char *reason)
|
int pw_context_recalc_graph(struct pw_context *context, const char *reason)
|
||||||
{
|
{
|
||||||
struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
|
struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
|
||||||
|
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;
|
||||||
|
|
||||||
pw_log_info("%p: busy:%d reason:%s", context, impl->recalc, reason);
|
pw_log_info("%p: busy:%d reason:%s", context, impl->recalc, reason);
|
||||||
|
|
||||||
|
|
@ -997,14 +1454,389 @@ int pw_context_recalc_graph(struct pw_context *context, const char *reason)
|
||||||
|
|
||||||
again:
|
again:
|
||||||
impl->recalc = true;
|
impl->recalc = true;
|
||||||
|
freewheel = false;
|
||||||
|
|
||||||
pw_context_emit_recalc_graph(context);
|
/* clean up the flags first */
|
||||||
|
spa_list_for_each(n, &context->node_list, link) {
|
||||||
|
n->visited = false;
|
||||||
|
n->checked = 0;
|
||||||
|
n->runnable = n->always_process && n->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
if (context->freewheeling != freewheel)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
impl->recalc = false;
|
impl->recalc = false;
|
||||||
if (impl->recalc_pending) {
|
if (impl->recalc_pending) {
|
||||||
impl->recalc_pending = false;
|
impl->recalc_pending = false;
|
||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ struct pw_impl_node;
|
||||||
|
|
||||||
/** context events emitted by the context object added with \ref pw_context_add_listener */
|
/** context events emitted by the context object added with \ref pw_context_add_listener */
|
||||||
struct pw_context_events {
|
struct pw_context_events {
|
||||||
#define PW_VERSION_CONTEXT_EVENTS 2
|
#define PW_VERSION_CONTEXT_EVENTS 1
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
|
|
||||||
/** The context is being destroyed */
|
/** The context is being destroyed */
|
||||||
|
|
@ -69,9 +69,6 @@ struct pw_context_events {
|
||||||
void (*driver_added) (void *data, struct pw_impl_node *node);
|
void (*driver_added) (void *data, struct pw_impl_node *node);
|
||||||
/** a driver was removed, since 0.3.75 version:1 */
|
/** a driver was removed, since 0.3.75 version:1 */
|
||||||
void (*driver_removed) (void *data, struct pw_impl_node *node);
|
void (*driver_removed) (void *data, struct pw_impl_node *node);
|
||||||
|
|
||||||
/** recalculate the graph state, since 1.7.0 version:2 */
|
|
||||||
void (*recalc_graph) (void *data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Make a new context object for a given main_loop. Ownership of the properties is taken, even
|
/** Make a new context object for a given main_loop. Ownership of the properties is taken, even
|
||||||
|
|
|
||||||
|
|
@ -1986,8 +1986,7 @@ int pw_filter_get_time(struct pw_filter *filter, struct pw_time *time)
|
||||||
pw_log_trace("%p: %"PRIi64" %"PRIi64" %"PRIu64" %d/%d ", filter,
|
pw_log_trace("%p: %"PRIi64" %"PRIi64" %"PRIu64" %d/%d ", filter,
|
||||||
time->now, time->delay, time->ticks,
|
time->now, time->delay, time->ticks,
|
||||||
time->rate.num, time->rate.denom);
|
time->rate.num, time->rate.denom);
|
||||||
|
return 0;
|
||||||
return filter->state == PW_FILTER_STATE_STREAMING ? 0 : -EIO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SPA_EXPORT
|
SPA_EXPORT
|
||||||
|
|
|
||||||
|
|
@ -969,7 +969,6 @@ static void output_remove(struct pw_impl_link *this)
|
||||||
this->output = NULL;
|
this->output = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SPA_EXPORT
|
|
||||||
int pw_impl_link_prepare(struct pw_impl_link *this)
|
int pw_impl_link_prepare(struct pw_impl_link *this)
|
||||||
{
|
{
|
||||||
struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
|
struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
|
||||||
|
|
@ -978,6 +977,9 @@ int pw_impl_link_prepare(struct pw_impl_link *this)
|
||||||
this, this->prepared, this->preparing,
|
this, this->prepared, this->preparing,
|
||||||
impl->input.node->active, impl->output.node->active, this->passive);
|
impl->input.node->active, impl->output.node->active, this->passive);
|
||||||
|
|
||||||
|
if (!impl->input.node->active || !impl->output.node->active)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (this->destroyed || this->preparing || this->prepared)
|
if (this->destroyed || this->preparing || this->prepared)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -1217,6 +1219,12 @@ static void output_node_result(void *data, int seq, int res, uint32_t type, cons
|
||||||
node_result(impl, &impl->output, seq, res, type, result);
|
node_result(impl, &impl->output, seq, res, type, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void node_active_changed(void *data, bool active)
|
||||||
|
{
|
||||||
|
struct impl *impl = data;
|
||||||
|
pw_impl_link_prepare(&impl->this);
|
||||||
|
}
|
||||||
|
|
||||||
static void node_driver_changed(void *data, struct pw_impl_node *old, struct pw_impl_node *driver)
|
static void node_driver_changed(void *data, struct pw_impl_node *old, struct pw_impl_node *driver)
|
||||||
{
|
{
|
||||||
struct impl *impl = data;
|
struct impl *impl = data;
|
||||||
|
|
@ -1231,12 +1239,14 @@ static void node_driver_changed(void *data, struct pw_impl_node *old, struct pw_
|
||||||
static const struct pw_impl_node_events input_node_events = {
|
static const struct pw_impl_node_events input_node_events = {
|
||||||
PW_VERSION_IMPL_NODE_EVENTS,
|
PW_VERSION_IMPL_NODE_EVENTS,
|
||||||
.result = input_node_result,
|
.result = input_node_result,
|
||||||
|
.active_changed = node_active_changed,
|
||||||
.driver_changed = node_driver_changed,
|
.driver_changed = node_driver_changed,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct pw_impl_node_events output_node_events = {
|
static const struct pw_impl_node_events output_node_events = {
|
||||||
PW_VERSION_IMPL_NODE_EVENTS,
|
PW_VERSION_IMPL_NODE_EVENTS,
|
||||||
.result = output_node_result,
|
.result = output_node_result,
|
||||||
|
.active_changed = node_active_changed,
|
||||||
.driver_changed = node_driver_changed,
|
.driver_changed = node_driver_changed,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1595,8 +1595,6 @@ void pw_impl_port_destroy(struct pw_impl_port *port)
|
||||||
pw_param_clear(&impl->pending_list, SPA_ID_INVALID);
|
pw_param_clear(&impl->pending_list, SPA_ID_INVALID);
|
||||||
free(port->tag[SPA_DIRECTION_INPUT]);
|
free(port->tag[SPA_DIRECTION_INPUT]);
|
||||||
free(port->tag[SPA_DIRECTION_OUTPUT]);
|
free(port->tag[SPA_DIRECTION_OUTPUT]);
|
||||||
free(port->cap[SPA_DIRECTION_INPUT]);
|
|
||||||
free(port->cap[SPA_DIRECTION_OUTPUT]);
|
|
||||||
|
|
||||||
pw_map_clear(&port->mix_port_map);
|
pw_map_clear(&port->mix_port_map);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,6 @@ pw_core_resource_errorf(struct pw_resource *resource, uint32_t id, int seq,
|
||||||
#define pw_context_emit_global_removed(c,g) pw_context_emit(c, global_removed, 0, g)
|
#define pw_context_emit_global_removed(c,g) pw_context_emit(c, global_removed, 0, g)
|
||||||
#define pw_context_emit_driver_added(c,n) pw_context_emit(c, driver_added, 1, n)
|
#define pw_context_emit_driver_added(c,n) pw_context_emit(c, driver_added, 1, n)
|
||||||
#define pw_context_emit_driver_removed(c,n) pw_context_emit(c, driver_removed, 1, n)
|
#define pw_context_emit_driver_removed(c,n) pw_context_emit(c, driver_removed, 1, n)
|
||||||
#define pw_context_emit_recalc_graph(c) pw_context_emit(c, recalc_graph, 2)
|
|
||||||
|
|
||||||
struct pw_context {
|
struct pw_context {
|
||||||
struct pw_impl_core *core; /**< core object */
|
struct pw_impl_core *core; /**< core object */
|
||||||
|
|
@ -1270,8 +1269,6 @@ int pw_context_debug_port_params(struct pw_context *context,
|
||||||
struct spa_node *node, enum spa_direction direction,
|
struct spa_node *node, enum spa_direction direction,
|
||||||
uint32_t port_id, uint32_t id, int err, const char *debug, ...);
|
uint32_t port_id, uint32_t id, int err, const char *debug, ...);
|
||||||
|
|
||||||
int pw_context_set_freewheel(struct pw_context *context, bool freewheel);
|
|
||||||
|
|
||||||
int pw_proxy_init(struct pw_proxy *proxy, struct pw_core *core, const char *type, uint32_t version);
|
int pw_proxy_init(struct pw_proxy *proxy, struct pw_core *core, const char *type, uint32_t version);
|
||||||
|
|
||||||
void pw_proxy_remove(struct pw_proxy *proxy);
|
void pw_proxy_remove(struct pw_proxy *proxy);
|
||||||
|
|
|
||||||
|
|
@ -2504,8 +2504,7 @@ int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time *time, size_t
|
||||||
impl->dequeued.outcount, impl->dequeued.incount,
|
impl->dequeued.outcount, impl->dequeued.incount,
|
||||||
impl->queued.outcount, impl->queued.incount,
|
impl->queued.outcount, impl->queued.incount,
|
||||||
avail_buffers, impl->n_buffers);
|
avail_buffers, impl->n_buffers);
|
||||||
|
return 0;
|
||||||
return stream->state == PW_STREAM_STATE_STREAMING ? 0 : -EIO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SPA_EXPORT
|
SPA_EXPORT
|
||||||
|
|
|
||||||
|
|
@ -293,8 +293,7 @@ struct pw_stream_control {
|
||||||
* Use pw_stream_get_time_n() to get an updated time snapshot of the stream.
|
* Use pw_stream_get_time_n() to get an updated time snapshot of the stream.
|
||||||
* The time snapshot can give information about the time in the driver of the
|
* The time snapshot can give information about the time in the driver of the
|
||||||
* graph, the delay to the edge of the graph and the internal queuing in the
|
* graph, the delay to the edge of the graph and the internal queuing in the
|
||||||
* stream. This function should only be called in the STREAMING state and will
|
* stream.
|
||||||
* return an error when called in any other state.
|
|
||||||
*
|
*
|
||||||
* pw_time.ticks gives a monotonic increasing counter of the time in the graph
|
* pw_time.ticks gives a monotonic increasing counter of the time in the graph
|
||||||
* driver. I can be used to generate a timeline to schedule samples as well
|
* driver. I can be used to generate a timeline to schedule samples as well
|
||||||
|
|
@ -595,7 +594,7 @@ const struct pw_stream_control *pw_stream_get_control(struct pw_stream *stream,
|
||||||
/** Set control values */
|
/** Set control values */
|
||||||
int pw_stream_set_control(struct pw_stream *stream, uint32_t id, uint32_t n_values, float *values, ...);
|
int pw_stream_set_control(struct pw_stream *stream, uint32_t id, uint32_t n_values, float *values, ...);
|
||||||
|
|
||||||
/** Query the time on the stream. Returns an error when the stream is not running. RT safe */
|
/** Query the time on the stream, RT safe */
|
||||||
int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time *time, size_t size);
|
int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time *time, size_t size);
|
||||||
|
|
||||||
/** Get the current time in nanoseconds. This value can be compared with
|
/** Get the current time in nanoseconds. This value can be compared with
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ static void test_create(void)
|
||||||
/* check id, only when connected */
|
/* check id, only when connected */
|
||||||
spa_assert_se(pw_stream_get_node_id(stream) == SPA_ID_INVALID);
|
spa_assert_se(pw_stream_get_node_id(stream) == SPA_ID_INVALID);
|
||||||
|
|
||||||
spa_assert_se(pw_stream_get_time_n(stream, &tm, sizeof(tm)) == -EIO);
|
spa_assert_se(pw_stream_get_time_n(stream, &tm, sizeof(tm)) == 0);
|
||||||
spa_assert_se(tm.now == 0);
|
spa_assert_se(tm.now == 0);
|
||||||
spa_assert_se(tm.rate.num == 0);
|
spa_assert_se(tm.rate.num == 0);
|
||||||
spa_assert_se(tm.rate.denom == 0);
|
spa_assert_se(tm.rate.denom == 0);
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,6 @@ struct data {
|
||||||
const char *media_role;
|
const char *media_role;
|
||||||
const char *channel_map;
|
const char *channel_map;
|
||||||
const char *format;
|
const char *format;
|
||||||
const char *container;
|
|
||||||
const char *target;
|
const char *target;
|
||||||
const char *latency;
|
const char *latency;
|
||||||
struct pw_properties *props;
|
struct pw_properties *props;
|
||||||
|
|
@ -195,6 +194,8 @@ struct data {
|
||||||
uint64_t samples_processed;
|
uint64_t samples_processed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define STR_FMTS "(ulaw|alaw|u8|s8|s16|s32|f32|f64)"
|
||||||
|
|
||||||
static const struct format_info {
|
static const struct format_info {
|
||||||
const char *name;
|
const char *name;
|
||||||
int sf_format;
|
int sf_format;
|
||||||
|
|
@ -210,35 +211,6 @@ static const struct format_info {
|
||||||
{ "s32", SF_FORMAT_PCM_32, SPA_AUDIO_FORMAT_S32, 4 },
|
{ "s32", SF_FORMAT_PCM_32, SPA_AUDIO_FORMAT_S32, 4 },
|
||||||
{ "f32", SF_FORMAT_FLOAT, SPA_AUDIO_FORMAT_F32, 4 },
|
{ "f32", SF_FORMAT_FLOAT, SPA_AUDIO_FORMAT_F32, 4 },
|
||||||
{ "f64", SF_FORMAT_DOUBLE, SPA_AUDIO_FORMAT_F32, 8 },
|
{ "f64", SF_FORMAT_DOUBLE, SPA_AUDIO_FORMAT_F32, 8 },
|
||||||
|
|
||||||
{ "mp1", SF_FORMAT_MPEG_LAYER_I, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "mp2", SF_FORMAT_MPEG_LAYER_II, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "mp3", SF_FORMAT_MPEG_LAYER_III, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "vorbis", SF_FORMAT_VORBIS, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "opus", SF_FORMAT_OPUS, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
|
|
||||||
{ "ima-adpcm", SF_FORMAT_IMA_ADPCM, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "ms-adpcm", SF_FORMAT_MS_ADPCM, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "nms-adpcm-16", SF_FORMAT_NMS_ADPCM_16, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "nms-adpcm-24", SF_FORMAT_NMS_ADPCM_24, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "nms-adpcm-32", SF_FORMAT_NMS_ADPCM_32, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
|
|
||||||
{ "alac-16", SF_FORMAT_ALAC_16, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "alac-20", SF_FORMAT_ALAC_20, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "alac-24", SF_FORMAT_ALAC_24, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "alac-32", SF_FORMAT_ALAC_32, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
|
|
||||||
{ "gsm610", SF_FORMAT_GSM610, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "g721-32", SF_FORMAT_G721_32, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "g723-24", SF_FORMAT_G723_24, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "g723-40", SF_FORMAT_G723_40, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "dwvw-12", SF_FORMAT_DWVW_12, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "dwvw-16", SF_FORMAT_DWVW_16, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "dwvw-24", SF_FORMAT_DWVW_24, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "vox", SF_FORMAT_VOX_ADPCM, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "dpcm-16", SF_FORMAT_DPCM_16, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
{ "dpcm-8", SF_FORMAT_DPCM_8, SPA_AUDIO_FORMAT_F32, 1 },
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct format_info *format_info_by_name(const char *str)
|
static const struct format_info *format_info_by_name(const char *str)
|
||||||
|
|
@ -258,14 +230,6 @@ static const struct format_info *format_info_by_sf_format(int format)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void list_formats(struct data *d)
|
|
||||||
{
|
|
||||||
|
|
||||||
fprintf(stdout, _("Supported formats:\n"));
|
|
||||||
SPA_FOR_EACH_ELEMENT_VAR(format_info, i)
|
|
||||||
fprintf(stdout, " %s\n", i->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sf_playback_fill_x8(struct data *d, void *dest, unsigned int n_frames, bool *null_frame)
|
static int sf_playback_fill_x8(struct data *d, void *dest, unsigned int n_frames, bool *null_frame)
|
||||||
{
|
{
|
||||||
sf_count_t rn;
|
sf_count_t rn;
|
||||||
|
|
@ -744,34 +708,6 @@ static int parse_channelmap(const char *channel_map, struct spa_audio_layout_inf
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void list_layouts(struct data *d)
|
|
||||||
{
|
|
||||||
fprintf(stderr, _("Supported channel layouts:\n"));
|
|
||||||
SPA_FOR_EACH_ELEMENT_VAR(spa_type_audio_layout_info, i) {
|
|
||||||
if (i->name == NULL)
|
|
||||||
break;
|
|
||||||
fprintf(stdout, " %s: [", i->name);
|
|
||||||
for (uint32_t j = 0; j < i->layout.n_channels; j++)
|
|
||||||
fprintf(stdout, "%s%s", j == 0 ? " " : ", ",
|
|
||||||
spa_type_audio_channel_to_short_name(i->layout.position[j]));
|
|
||||||
fprintf(stdout, " ]\n");
|
|
||||||
}
|
|
||||||
fprintf(stderr, _("Supported channel layout aliases:\n"));
|
|
||||||
SPA_FOR_EACH_ELEMENT_VAR(maps, m)
|
|
||||||
fprintf(stdout, _(" %s -> %s\n"), m->name, m->alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void list_channel_names(struct data *d)
|
|
||||||
{
|
|
||||||
fprintf(stderr, _("Supported channel names:\n"));
|
|
||||||
SPA_FOR_EACH_ELEMENT_VAR(spa_type_audio_channel, i) {
|
|
||||||
if (i->name == NULL || SPA_AUDIO_CHANNEL_IS_AUX(i->type))
|
|
||||||
break;
|
|
||||||
fprintf(stdout, " %s\n", spa_type_short_name(i->name));
|
|
||||||
}
|
|
||||||
fprintf(stderr, " AUX0 ... AUX4095\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static int channelmap_default(struct spa_audio_layout_info *map, int n_channels)
|
static int channelmap_default(struct spa_audio_layout_info *map, int n_channels)
|
||||||
{
|
{
|
||||||
switch(n_channels) {
|
switch(n_channels) {
|
||||||
|
|
@ -1112,11 +1048,6 @@ enum {
|
||||||
OPT_CHANNELMAP,
|
OPT_CHANNELMAP,
|
||||||
OPT_FORMAT,
|
OPT_FORMAT,
|
||||||
OPT_VOLUME,
|
OPT_VOLUME,
|
||||||
OPT_CONTAINER,
|
|
||||||
OPT_LISTFORMATS,
|
|
||||||
OPT_LISTCONTAINERS,
|
|
||||||
OPT_LISTLAYOUTS,
|
|
||||||
OPT_LISTCHANNELNAMES,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
|
#ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
|
||||||
|
|
@ -1151,12 +1082,7 @@ static const struct option long_options[] = {
|
||||||
{ "rate", required_argument, NULL, OPT_RATE },
|
{ "rate", required_argument, NULL, OPT_RATE },
|
||||||
{ "channels", required_argument, NULL, OPT_CHANNELS },
|
{ "channels", required_argument, NULL, OPT_CHANNELS },
|
||||||
{ "channel-map", required_argument, NULL, OPT_CHANNELMAP },
|
{ "channel-map", required_argument, NULL, OPT_CHANNELMAP },
|
||||||
{ "list-layouts", no_argument, NULL, OPT_LISTLAYOUTS },
|
|
||||||
{ "list-channel-names", no_argument, NULL, OPT_LISTCHANNELNAMES },
|
|
||||||
{ "format", required_argument, NULL, OPT_FORMAT },
|
{ "format", required_argument, NULL, OPT_FORMAT },
|
||||||
{ "list-formats", no_argument, NULL, OPT_LISTFORMATS },
|
|
||||||
{ "container", required_argument, NULL, OPT_CONTAINER },
|
|
||||||
{ "list-containers", no_argument, NULL, OPT_LISTCONTAINERS },
|
|
||||||
{ "volume", required_argument, NULL, OPT_VOLUME },
|
{ "volume", required_argument, NULL, OPT_VOLUME },
|
||||||
{ "quality", required_argument, NULL, 'q' },
|
{ "quality", required_argument, NULL, 'q' },
|
||||||
{ "raw", no_argument, NULL, 'a' },
|
{ "raw", no_argument, NULL, 'a' },
|
||||||
|
|
@ -1199,17 +1125,12 @@ static void show_usage(const char *name, bool is_error)
|
||||||
DEFAULT_TARGET, DEFAULT_LATENCY_PLAY);
|
DEFAULT_TARGET, DEFAULT_LATENCY_PLAY);
|
||||||
|
|
||||||
fprintf(fp,
|
fprintf(fp,
|
||||||
_(" --rate Sample rate (default %u)\n"
|
_(" --rate Sample rate (req. for rec) (default %u)\n"
|
||||||
" --channels Number of channels (default %u)\n"
|
" --channels Number of channels (req. for rec) (default %u)\n"
|
||||||
" --channel-map Channel map\n"
|
" --channel-map Channel map\n"
|
||||||
" a channel layout: \"Stereo\", \"5.1\",... or\n"
|
" one of: \"Stereo\", \"5.1\",... or\n"
|
||||||
" comma separated list of channel names: eg. \"FL,FR\"\n"
|
" comma separated list of channel names: eg. \"FL,FR\"\n"
|
||||||
" --list-layouts List supported channel layouts\n"
|
" --format Sample format %s (req. for rec) (default %s)\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"
|
|
||||||
" --volume Stream volume 0-1.0 (default %.3f)\n"
|
" --volume Stream volume 0-1.0 (default %.3f)\n"
|
||||||
" -q --quality Resampler quality (0 - 15) (default %d)\n"
|
" -q --quality Resampler quality (0 - 15) (default %d)\n"
|
||||||
" -a, --raw RAW mode\n"
|
" -a, --raw RAW mode\n"
|
||||||
|
|
@ -1218,7 +1139,7 @@ static void show_usage(const char *name, bool is_error)
|
||||||
"\n"),
|
"\n"),
|
||||||
DEFAULT_RATE,
|
DEFAULT_RATE,
|
||||||
DEFAULT_CHANNELS,
|
DEFAULT_CHANNELS,
|
||||||
DEFAULT_FORMAT,
|
STR_FMTS, DEFAULT_FORMAT,
|
||||||
DEFAULT_VOLUME,
|
DEFAULT_VOLUME,
|
||||||
DEFAULT_QUALITY);
|
DEFAULT_QUALITY);
|
||||||
|
|
||||||
|
|
@ -1752,20 +1673,17 @@ static int fill_properties(struct data *data)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static void format_from_filename(SF_INFO *info, const char *filename, const char *container)
|
static void format_from_filename(SF_INFO *info, const char *filename)
|
||||||
{
|
{
|
||||||
int i, count = 0;
|
int i, count = 0;
|
||||||
int format = -1;
|
int format = -1;
|
||||||
const char *extension;
|
|
||||||
|
|
||||||
if (spa_streq(filename, "-"))
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||||
extension = container ? container : "au";
|
info->format |= SF_ENDIAN_BIG;
|
||||||
else if (container)
|
#else
|
||||||
extension = container;
|
info->format |= SF_ENDIAN_LITTLE;
|
||||||
else
|
#endif
|
||||||
extension = filename;
|
|
||||||
|
|
||||||
fprintf(stderr, "%s\n", filename);
|
|
||||||
if (sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) != 0)
|
if (sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) != 0)
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
|
|
@ -1777,67 +1695,22 @@ static void format_from_filename(SF_INFO *info, const char *filename, const char
|
||||||
if (sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) != 0)
|
if (sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (spa_strendswith(extension, fi.extension)) {
|
if (spa_strendswith(filename, fi.extension)) {
|
||||||
format = fi.format;
|
format = fi.format;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (format == -1) {
|
|
||||||
if (sf_command(NULL, SFC_GET_SIMPLE_FORMAT_COUNT, &count, sizeof(int)) != 0)
|
|
||||||
count = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
SF_FORMAT_INFO fi;
|
|
||||||
|
|
||||||
spa_zero(fi);
|
|
||||||
fi.format = i;
|
|
||||||
if (sf_command(NULL, SFC_GET_SIMPLE_FORMAT, &fi, sizeof(fi)) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (spa_strendswith(extension, fi.extension)) {
|
|
||||||
format = fi.format;
|
|
||||||
info->format = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (format == -1)
|
if (format == -1)
|
||||||
format = SF_FORMAT_WAV;
|
format = spa_streq(filename, "-") ? SF_FORMAT_AU : SF_FORMAT_WAV;
|
||||||
if (format == SF_FORMAT_WAV && info->channels > 2)
|
if (format == SF_FORMAT_WAV && info->channels > 2)
|
||||||
format = SF_FORMAT_WAVEX;
|
format = SF_FORMAT_WAVEX;
|
||||||
|
|
||||||
switch (format & SF_FORMAT_TYPEMASK) {
|
|
||||||
case SF_FORMAT_OGG:
|
|
||||||
case SF_FORMAT_FLAC:
|
|
||||||
case SF_FORMAT_MPEG:
|
|
||||||
case SF_FORMAT_AIFF:
|
|
||||||
info->format |= SF_ENDIAN_FILE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
info->format |= SF_ENDIAN_CPU;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
info->format |= format;
|
info->format |= format;
|
||||||
}
|
|
||||||
|
|
||||||
static void list_containers(struct data *d)
|
if (format == SF_FORMAT_OGG || format == SF_FORMAT_FLAC)
|
||||||
{
|
info->format = (info->format & ~SF_FORMAT_ENDMASK) | SF_ENDIAN_FILE;
|
||||||
int i, count = 0;
|
if (format == SF_FORMAT_OGG)
|
||||||
|
info->format = (info->format & ~SF_FORMAT_SUBMASK) | SF_FORMAT_VORBIS;
|
||||||
fprintf(stderr, _("Supported containers and extensions:\n"));
|
|
||||||
if (sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) != 0)
|
|
||||||
count = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
SF_FORMAT_INFO fi;
|
|
||||||
|
|
||||||
spa_zero(fi);
|
|
||||||
fi.format = i;
|
|
||||||
if (sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
fprintf(stderr, " %s: %s\n", fi.extension, fi.name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
|
#ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
|
||||||
|
|
@ -1961,7 +1834,7 @@ static int setup_sndfile(struct data *data)
|
||||||
info.samplerate = data->rate;
|
info.samplerate = data->rate;
|
||||||
info.channels = data->channels;
|
info.channels = data->channels;
|
||||||
info.format = fi->sf_format;
|
info.format = fi->sf_format;
|
||||||
format_from_filename(&info, data->filename, data->container);
|
format_from_filename(&info, data->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
data->sndfile.file = sf_open(data->filename,
|
data->sndfile.file = sf_open(data->filename,
|
||||||
|
|
@ -2322,9 +2195,6 @@ int main(int argc, char *argv[])
|
||||||
case OPT_FORMAT:
|
case OPT_FORMAT:
|
||||||
data.format = optarg;
|
data.format = optarg;
|
||||||
break;
|
break;
|
||||||
case OPT_CONTAINER:
|
|
||||||
data.container = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPT_VOLUME:
|
case OPT_VOLUME:
|
||||||
if (!spa_atof(optarg, &data.volume))
|
if (!spa_atof(optarg, &data.volume))
|
||||||
|
|
@ -2336,18 +2206,6 @@ int main(int argc, char *argv[])
|
||||||
case 'c':
|
case 'c':
|
||||||
data.data_type = TYPE_MIDI2;
|
data.data_type = TYPE_MIDI2;
|
||||||
break;
|
break;
|
||||||
case OPT_LISTFORMATS:
|
|
||||||
list_formats(&data);
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
case OPT_LISTCONTAINERS:
|
|
||||||
list_containers(&data);
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
case OPT_LISTLAYOUTS:
|
|
||||||
list_layouts(&data);
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
case OPT_LISTCHANNELNAMES:
|
|
||||||
list_channel_names(&data);
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
default:
|
default:
|
||||||
goto error_usage;
|
goto error_usage;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -780,7 +780,7 @@ static void show_help(const char *name, bool error)
|
||||||
" -N, --no-colors disable color output\n"
|
" -N, --no-colors disable color output\n"
|
||||||
" -C, --color[=WHEN] whether to enable color support. WHEN is `never`, `always`, or `auto`\n"
|
" -C, --color[=WHEN] whether to enable color support. WHEN is `never`, `always`, or `auto`\n"
|
||||||
" -o, --hide-props hide node properties\n"
|
" -o, --hide-props hide node properties\n"
|
||||||
" -a, --hide-params hide node parameters\n"
|
" -a, --hide-params hide node properties\n"
|
||||||
" -p, --print-separator print empty line after every event to help streaming parser\n",
|
" -p, --print-separator print empty line after every event to help streaming parser\n",
|
||||||
name);
|
name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ PWTEST(context_abi)
|
||||||
void (*global_removed) (void *data, struct pw_global *global);
|
void (*global_removed) (void *data, struct pw_global *global);
|
||||||
void (*driver_added) (void *data, struct pw_impl_node *node);
|
void (*driver_added) (void *data, struct pw_impl_node *node);
|
||||||
void (*driver_removed) (void *data, struct pw_impl_node *node);
|
void (*driver_removed) (void *data, struct pw_impl_node *node);
|
||||||
void (*recalc_graph) (void *data);
|
|
||||||
} test = { PW_VERSION_CONTEXT_EVENTS, NULL };
|
} test = { PW_VERSION_CONTEXT_EVENTS, NULL };
|
||||||
|
|
||||||
pw_init(0, NULL);
|
pw_init(0, NULL);
|
||||||
|
|
@ -41,9 +40,8 @@ PWTEST(context_abi)
|
||||||
TEST_FUNC(ev, test, global_removed);
|
TEST_FUNC(ev, test, global_removed);
|
||||||
TEST_FUNC(ev, test, driver_added);
|
TEST_FUNC(ev, test, driver_added);
|
||||||
TEST_FUNC(ev, test, driver_removed);
|
TEST_FUNC(ev, test, driver_removed);
|
||||||
TEST_FUNC(ev, test, recalc_graph);
|
|
||||||
|
|
||||||
pwtest_int_eq(PW_VERSION_CONTEXT_EVENTS, 2);
|
pwtest_int_eq(PW_VERSION_CONTEXT_EVENTS, 1);
|
||||||
pwtest_int_eq(sizeof(ev), sizeof(test));
|
pwtest_int_eq(sizeof(ev), sizeof(test));
|
||||||
|
|
||||||
pw_deinit();
|
pw_deinit();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue