aboutsummaryrefslogtreecommitdiffstats
path: root/alc/backends/pipewire.cpp
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2023-01-24 04:12:17 -0800
committerChris Robinson <[email protected]>2023-01-24 04:12:17 -0800
commit0c75ab9967e84b9fd016b2eb6152494908cdf704 (patch)
tree49e530151716eff6b6965c0035f3ac323c153e90 /alc/backends/pipewire.cpp
parent748f7250a13bd0fb36109f99bf15be7723b099c8 (diff)
Use the object serial ID from PipeWire when available
Using the node ID as a target is deprecated in newer versions of PipeWire. The serial ID is a monotonic 64-bit integer ID, incremeneted for every object created, so is guaranteed to always refer to the same target (until it wraps around, which I suppose isn't expected/allowed to happen), compared to the 32-bit node ID which I guess allows reuse. We could instead use the target node's name instead of the serial ID, but an integer is nicer to manage than a string.
Diffstat (limited to 'alc/backends/pipewire.cpp')
-rw-r--r--alc/backends/pipewire.cpp68
1 files changed, 47 insertions, 21 deletions
diff --git a/alc/backends/pipewire.cpp b/alc/backends/pipewire.cpp
index aa3ae459..04cc38c7 100644
--- a/alc/backends/pipewire.cpp
+++ b/alc/backends/pipewire.cpp
@@ -554,10 +554,12 @@ enum class NodeType : unsigned char {
};
constexpr auto InvalidChannelConfig = DevFmtChannels(255);
struct DeviceNode {
+ uint32_t mId{};
+
+ uint64_t mSerial{};
std::string mName;
std::string mDevName;
- uint32_t mId{};
NodeType mType{};
bool mIsHeadphones{};
bool mIs51Rear{};
@@ -695,8 +697,8 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
auto srates = get_pod_body<int32_t,3>(value);
/* [0] is the default, [1] is the min, and [2] is the max. */
- TRACE("Device ID %u sample rate: %d (range: %d -> %d)\n", mId, srates[0], srates[1],
- srates[2]);
+ TRACE("Device ID %" PRIu64 " sample rate: %d (range: %d -> %d)\n", mSerial, srates[0],
+ srates[1], srates[2]);
mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE));
return;
}
@@ -717,7 +719,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
others += ", ";
others += std::to_string(srates[i]);
}
- TRACE("Device ID %u sample rate: %d (%s)\n", mId, srates[0], others.c_str());
+ TRACE("Device ID %" PRIu64 " sample rate: %d (%s)\n", mSerial, srates[0], others.c_str());
/* Pick the first rate listed that's within the allowed range (default
* rate if possible).
*/
@@ -741,7 +743,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
}
auto srates = get_pod_body<int32_t,1>(value);
- TRACE("Device ID %u sample rate: %d\n", mId, srates[0]);
+ TRACE("Device ID %" PRIu64 " sample rate: %d\n", mSerial, srates[0]);
mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE));
return;
}
@@ -775,7 +777,7 @@ void DeviceNode::parsePositions(const spa_pod *value) noexcept
mChannels = DevFmtStereo;
else
mChannels = DevFmtMono;
- TRACE("Device ID %u got %zu position%s for %s%s\n", mId, chanmap.size(),
+ TRACE("Device ID %" PRIu64 " got %zu position%s for %s%s\n", mSerial, chanmap.size(),
(chanmap.size()==1)?"":"s", DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":"");
}
@@ -791,8 +793,8 @@ void DeviceNode::parseChannelCount(const spa_pod *value) noexcept
mChannels = DevFmtStereo;
else if(*chancount >= 1)
mChannels = DevFmtMono;
- TRACE("Device ID %u got %d channel%s for %s\n", mId, *chancount, (*chancount==1)?"":"s",
- DevFmtChannelsString(mChannels));
+ TRACE("Device ID %" PRIu64 " got %d channel%s for %s\n", mSerial, *chancount,
+ (*chancount==1)?"":"s", DevFmtChannelsString(mChannels));
}
@@ -882,12 +884,26 @@ void NodeProxy::infoCallback(const pw_node_info *info)
if(!nodeName || !*nodeName) nodeName = spa_dict_lookup(info->props, PW_KEY_NODE_NICK);
if(!nodeName || !*nodeName) nodeName = devName;
+#ifdef PW_KEY_OBJECT_SERIAL
+ char *serial_end{};
+ const char *serial_str{spa_dict_lookup(info->props, PW_KEY_OBJECT_SERIAL)};
+ uint64_t serial_id{std::strtoull(serial_str, &serial_end, 0)};
+ if(*serial_end != '\0' || errno == ERANGE)
+ {
+ ERR("Unexpected object serial: %s\n", serial_str);
+ serial_id = info->id;
+ }
+#else
+ uint64_t serial_id{info->id};
+#endif
+
const char *form_factor{spa_dict_lookup(info->props, PW_KEY_DEVICE_FORM_FACTOR)};
TRACE("Got %s device \"%s\"%s%s%s\n", AsString(ntype), devName ? devName : "(nil)",
form_factor?" (":"", form_factor?form_factor:"", form_factor?")":"");
- TRACE(" \"%s\" = ID %u\n", nodeName ? nodeName : "(nil)", info->id);
+ TRACE(" \"%s\" = ID %" PRIu64 "\n", nodeName ? nodeName : "(nil)", serial_id);
DeviceNode &node = DeviceNode::Add(info->id);
+ node.mSerial = serial_id;
if(nodeName && *nodeName) node.mName = nodeName;
else node.mName = "PipeWire node #"+std::to_string(info->id);
node.mDevName = devName ? devName : "";
@@ -1277,7 +1293,7 @@ class PipeWirePlayback final : public BackendBase {
void stop() override;
ClockLatency getClockLatency() override;
- uint32_t mTargetId{PwIdAny};
+ uint64_t mTargetId{PwIdAny};
nanoseconds mTimeBase{0};
ThreadMainloop mLoop;
PwContextPtr mContext;
@@ -1375,7 +1391,7 @@ void PipeWirePlayback::open(const char *name)
{
static std::atomic<uint> OpenCount{0};
- uint32_t targetid{PwIdAny};
+ uint64_t targetid{PwIdAny};
std::string devname{};
gEventHandler.waitForInit();
if(!name)
@@ -1400,7 +1416,7 @@ void PipeWirePlayback::open(const char *name)
"No PipeWire playback device found"};
}
- targetid = match->mId;
+ targetid = match->mSerial;
devname = match->mName;
}
else
@@ -1415,7 +1431,7 @@ void PipeWirePlayback::open(const char *name)
throw al::backend_exception{al::backend_error::NoDevice,
"Device name \"%s\" not found", name};
- targetid = match->mId;
+ targetid = match->mSerial;
devname = match->mName;
}
@@ -1480,7 +1496,7 @@ bool PipeWirePlayback::reset()
auto&& devlist = DeviceNode::GetList();
auto match_id = [targetid=mTargetId](const DeviceNode &n) -> bool
- { return targetid == n.mId; };
+ { return targetid == n.mSerial; };
auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_id);
if(match != devlist.cend())
{
@@ -1537,6 +1553,11 @@ bool PipeWirePlayback::reset()
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", mDevice->UpdateSize,
mDevice->Frequency);
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency);
+#ifdef PW_KEY_TARGET_OBJECT
+ pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%" PRIu64, mTargetId);
+#else
+ pw_properties_setf(props, PW_KEY_NODE_TARGET, "%" PRIu64, mTargetId);
+#endif
MainloopUniqueLock plock{mLoop};
/* The stream takes overship of 'props', even in the case of failure. */
@@ -1551,7 +1572,7 @@ bool PipeWirePlayback::reset()
| PW_STREAM_FLAG_MAP_BUFFERS};
if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pipewire", "rt-mix", true))
flags |= PW_STREAM_FLAG_RT_PROCESS;
- if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, mTargetId, flags, &params, 1)})
+ if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, PwIdAny, flags, &params, 1)})
throw al::backend_exception{al::backend_error::DeviceError,
"Error connecting PipeWire stream (res: %d)", res};
@@ -1773,7 +1794,7 @@ class PipeWireCapture final : public BackendBase {
void captureSamples(al::byte *buffer, uint samples) override;
uint availableSamples() override;
- uint32_t mTargetId{PwIdAny};
+ uint64_t mTargetId{PwIdAny};
ThreadMainloop mLoop;
PwContextPtr mContext;
PwCorePtr mCore;
@@ -1821,7 +1842,7 @@ void PipeWireCapture::open(const char *name)
{
static std::atomic<uint> OpenCount{0};
- uint32_t targetid{PwIdAny};
+ uint64_t targetid{PwIdAny};
std::string devname{};
gEventHandler.waitForInit();
if(!name)
@@ -1850,7 +1871,7 @@ void PipeWireCapture::open(const char *name)
"No PipeWire capture device found"};
}
- targetid = match->mId;
+ targetid = match->mSerial;
if(match->mType != NodeType::Sink) devname = match->mName;
else devname = MonitorPrefix+match->mName;
}
@@ -1873,7 +1894,7 @@ void PipeWireCapture::open(const char *name)
throw al::backend_exception{al::backend_error::NoDevice,
"Device name \"%s\" not found", name};
- targetid = match->mId;
+ targetid = match->mSerial;
devname = name;
}
@@ -1923,7 +1944,7 @@ void PipeWireCapture::open(const char *name)
auto&& devlist = DeviceNode::GetList();
auto match_id = [targetid=mTargetId](const DeviceNode &n) -> bool
- { return targetid == n.mId; };
+ { return targetid == n.mSerial; };
auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_id);
if(match != devlist.cend())
is51rear = match->mIs51Rear;
@@ -1960,6 +1981,11 @@ void PipeWireCapture::open(const char *name)
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", (mDevice->Frequency+25) / 50,
mDevice->Frequency);
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency);
+#ifdef PW_KEY_TARGET_OBJECT
+ pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%" PRIu64, mTargetId);
+#else
+ pw_properties_setf(props, PW_KEY_NODE_TARGET, "%" PRIu64, mTargetId);
+#endif
MainloopUniqueLock plock{mLoop};
mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Capture Stream", props)};
@@ -1971,7 +1997,7 @@ void PipeWireCapture::open(const char *name)
constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
| PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS};
- if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, mTargetId, Flags, params, 1)})
+ if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, params, 1)})
throw al::backend_exception{al::backend_error::DeviceError,
"Error connecting PipeWire stream (res: %d)", res};