filter-utils: handle new 'filter.smart.targetable' property

This property indicates whether the filter can be directly linked with clients
that have a defined target (Eg: pw-play --target <filter-name>) or not. This can
be useful when a client wants to be linked with a filter that is in the middle
of the chain in order to bypass the filters that are placed before the selected
one. If the property is not set, wireplumber will consider the filter not
targetable by default, meaning filters will never by bypassed by clients, and
clients will always be linked with the first filter in the chain.

Fixes #554
This commit is contained in:
Julian Bouzas
2024-01-11 10:54:41 -05:00
parent cdeac07814
commit 598b3c83ce
3 changed files with 64 additions and 0 deletions

View File

@@ -134,6 +134,15 @@ optional node properties on the main node:
disabled filter will never be used in any circumstances. If the property is
not set, wireplumber will consider the filter not disabled by default.
- filter.smart.targetable:
Boolean indicating whether the filter can be directly linked with clients that
have a defined target (Eg: pw-play --target <filter-name>) or not. This can be
useful when a client wants to be linked with a filter that is in the middle of
the chain in order to bypass the filters that are placed before the selected
one. If the property is not set, wireplumber will consider the filter not
targetable by default, meaning filters will never by bypassed by clients, and
clients will always be linked with the first filter in the chain.
- filter.smart.target:
A JSON object that defines the matching properties of the filter's target node.
A filter target can never be another filter node (wireplumber will ignore it),
@@ -173,6 +182,7 @@ The PipeWire configuration files for the 2 filters should be like this:
filter.smart = true
filter.smart.name = loopback-1
filter.smart.disabled = false
filter.smart.targetable = false
filter.smart.before = [ loopback-2 ]
}
playback.props = {
@@ -199,6 +209,7 @@ The PipeWire configuration files for the 2 filters should be like this:
filter.smart = true
filter.smart.name = loopback-2
filter.smart.disabled = false
filter.smart.targetable = false
}
playback.props = {
audio.position = [ FL FR ]
@@ -268,6 +279,7 @@ define the filters like this:
filter.smart = true
filter.smart.name = loopback-1
filter.smart.disabled = false
filter.smart.targetable = false
filter.smart.before = [ loopback-2 ]
filter.smart.target = { node.name = "not-default-audio-device-name" }
}
@@ -295,6 +307,7 @@ define the filters like this:
filter.smart = true
filter.smart.name = loopback-2
filter.smart.disabled = false
filter.smart.targetable = false
}
playback.props = {
audio.position = [ FL FR ]

View File

@@ -83,6 +83,29 @@ local function getFilterSmartDisabled (metadata, node)
return false
end
local function getFilterSmartTargetable (metadata, node)
-- Check metadata
if metadata ~= nil then
local id = node["bound-id"]
local value_str = metadata:find (id, "filter.smart.targetable")
if value_str ~= nil then
local json = Json.Raw (value_str)
if json:is_boolean() then
return json:parse()
end
end
end
-- Check node properties
local prop_str = node.properties ["filter.smart.targetable"]
if prop_str ~= nil then
return cutils.parseBool (prop_str)
end
-- Otherwise consider the filter not targetable by default
return false
end
local function getFilterSmartTarget (metadata, node, om, dont_move)
-- Check metadata and fallback to properties
local id = node["bound-id"]
@@ -287,6 +310,7 @@ local function rescanFilters (om, metadata_om)
filter.smart = getFilterSmart (metadata, n)
filter.name = getFilterSmartName (metadata, n)
filter.disabled = getFilterSmartDisabled (metadata, n)
filter.targetable = getFilterSmartTargetable (metadata, n)
filter.target = getFilterSmartTarget (metadata, n, om)
filter.before = getFilterSmartBefore (metadata, n)
filter.after = getFilterSmartAfter (metadata, n)
@@ -354,6 +378,21 @@ function module.is_filter_disabled (direction, link_group)
return false
end
function module.is_filter_targetable (direction, link_group)
-- Make sure direction and link_group is valid
if direction == nil or link_group == nil then
return false
end
for i, v in ipairs(module.filters) do
if v.direction == direction and v.link_group == link_group then
return v.targetable
end
end
return false
end
function module.get_filter_target (direction, link_group)
-- Make sure direction and link_group are valid
if direction == nil or link_group == nil then

View File

@@ -35,6 +35,18 @@ SimpleEventHook {
return
end
-- bypass the hook if target is defined, is a filter and is targetable
local target_node = target:get_associated_proxy ("node")
local target_link_group = target_node.properties ["node.link-group"]
local target_direction = cutils.getTargetDirection (si.properties)
if target_link_group ~= nil and si_flags.has_defined_target then
if futils.is_filter_smart (target_direction, target_link_group) and
not futils.is_filter_disabled (target_direction, target_link_group) and
futils.is_filter_targetable (target_direction, target_link_group) then
return
end
end
-- Get the filter from the given target if it exists, otherwise get the
-- default filter, but only if target was not defined
local target_direction = cutils.getTargetDirection (si.properties)