mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-01 01:07:59 +02:00
scripts: use filter-utils to implement smart filter policy
This commit is contained in:
parent
1ba3844f2c
commit
56017fdbe6
8 changed files with 283 additions and 52 deletions
|
|
@ -503,6 +503,11 @@ wireplumber.components = [
|
|||
provides = hooks.linking.target.find-defined
|
||||
requires = [ support.lua-scripting ]
|
||||
}
|
||||
{
|
||||
name = linking/find-filter-target.lua, type = script/lua
|
||||
provides = hooks.linking.target.find-filter
|
||||
requires = [ support.lua-scripting, metadata.filters ]
|
||||
}
|
||||
{
|
||||
name = linking/find-default-target.lua, type = script/lua
|
||||
provides = hooks.linking.target.find-default
|
||||
|
|
@ -513,6 +518,11 @@ wireplumber.components = [
|
|||
provides = hooks.linking.target.find-best
|
||||
requires = [ support.lua-scripting ]
|
||||
}
|
||||
{
|
||||
name = linking/get-filter-from-target.lua, type = script/lua
|
||||
provides = hooks.linking.target.get-filter-from
|
||||
requires = [ support.lua-scripting, metadata.filters ]
|
||||
}
|
||||
{
|
||||
name = linking/prepare-link.lua, type = script/lua
|
||||
provides = hooks.linking.target.prepare-link
|
||||
|
|
@ -530,8 +540,10 @@ wireplumber.components = [
|
|||
hooks.linking.target.link ]
|
||||
wants = [ hooks.linking.move-follow,
|
||||
hooks.linking.target.find-defined,
|
||||
hooks.linking.target.find-filter,
|
||||
hooks.linking.target.find-default,
|
||||
hooks.linking.target.find-best ]
|
||||
hooks.linking.target.find-best,
|
||||
hooks.linking.target.get-filter-from ]
|
||||
}
|
||||
|
||||
## Linking: Role-based priority system
|
||||
|
|
|
|||
|
|
@ -44,12 +44,19 @@ SimpleEventHook {
|
|||
} do
|
||||
local target_props = target.properties
|
||||
local target_node_id = target_props ["node.id"]
|
||||
local si_target_node = si:get_associated_proxy ("node")
|
||||
local si_target_link_group = si_target_node.properties ["node.link-group"]
|
||||
local priority = tonumber (target_props ["priority.session"]) or 0
|
||||
|
||||
log:debug (string.format ("Looking at: %s (%s)",
|
||||
tostring (target_props ["node.name"]),
|
||||
tostring (target_node_id)))
|
||||
|
||||
if si_target_link_group ~= nil then
|
||||
Log.debug ("... ignoring filter as best target")
|
||||
goto skip_linkable
|
||||
end
|
||||
|
||||
if not putils.canLink (si_props, target) then
|
||||
log:debug ("... cannot link, skip linkable")
|
||||
goto skip_linkable
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ log = Log.open_topic ("s-linking")
|
|||
|
||||
SimpleEventHook {
|
||||
name = "linking/find-default-target",
|
||||
after = "linking/find-defined-target",
|
||||
after = "linking/find-filter-target",
|
||||
interests = {
|
||||
EventInterest {
|
||||
Constraint { "event.type", "=", "select-target" },
|
||||
|
|
|
|||
72
src/scripts/linking/find-filter-target.lua
Normal file
72
src/scripts/linking/find-filter-target.lua
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
-- WirePlumber
|
||||
--
|
||||
-- Copyright © 2023 Collabora Ltd.
|
||||
--
|
||||
-- SPDX-License-Identifier: MIT
|
||||
--
|
||||
-- Check if the target node is a filter target.
|
||||
|
||||
local putils = require ("policy-utils")
|
||||
local cutils = require ("common-utils")
|
||||
local futils = require ("filter-utils")
|
||||
log = Log.open_topic ("s-linking")
|
||||
|
||||
function findFilterTarget (si, om)
|
||||
local node = si:get_associated_proxy ("node")
|
||||
local direction = cutils.getTargetDirection (si.properties)
|
||||
local link_group = node.properties ["node.link-group"]
|
||||
local target_id = -1
|
||||
|
||||
-- return nil if si is not a filter node
|
||||
if link_group == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- get the filter target
|
||||
return futils.get_filter_target (direction, link_group)
|
||||
end
|
||||
|
||||
SimpleEventHook {
|
||||
name = "linking/find-filter-target",
|
||||
after = "linking/find-defined-target",
|
||||
interests = {
|
||||
EventInterest {
|
||||
Constraint { "event.type", "=", "select-target" },
|
||||
},
|
||||
},
|
||||
execute = function (event)
|
||||
local source, om, si, si_props, si_flags, target =
|
||||
putils:unwrap_find_target_event (event)
|
||||
|
||||
-- bypass the hook if the target is already picked up
|
||||
if target then
|
||||
return
|
||||
end
|
||||
|
||||
local target_picked = false
|
||||
|
||||
log:info (si, string.format ("handling item: %s (%s)",
|
||||
tostring (si_props ["node.name"]), tostring (si_props ["node.id"])))
|
||||
|
||||
target = findFilterTarget (si, om)
|
||||
|
||||
local can_passthrough, passthrough_compatible
|
||||
if target then
|
||||
passthrough_compatible, can_passthrough =
|
||||
putils.checkPassthroughCompatibility (si, target)
|
||||
if putils.canLink (si_props, target) and passthrough_compatible then
|
||||
target_picked = true;
|
||||
end
|
||||
end
|
||||
|
||||
if target_picked then
|
||||
log:info (si,
|
||||
string.format ("... filter target picked: %s (%s), can_passthrough:%s",
|
||||
tostring (target.properties ["node.name"]),
|
||||
tostring (target.properties ["node.id"]),
|
||||
tostring (can_passthrough)))
|
||||
si_flags.can_passthrough = can_passthrough
|
||||
event:set_data ("target", target)
|
||||
end
|
||||
end
|
||||
}:register ()
|
||||
64
src/scripts/linking/get-filter-from-target.lua
Normal file
64
src/scripts/linking/get-filter-from-target.lua
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
-- WirePlumber
|
||||
--
|
||||
-- Copyright © 2023 Collabora Ltd.
|
||||
--
|
||||
-- SPDX-License-Identifier: MIT
|
||||
--
|
||||
-- Check if the target node is a filter target.
|
||||
|
||||
local putils = require ("policy-utils")
|
||||
local cutils = require ("common-utils")
|
||||
local futils = require ("filter-utils")
|
||||
log = Log.open_topic ("s-linking")
|
||||
|
||||
SimpleEventHook {
|
||||
name = "linking/get-filter-from-target",
|
||||
after = "linking/find-best-target",
|
||||
interests = {
|
||||
EventInterest {
|
||||
Constraint { "event.type", "=", "select-target" },
|
||||
},
|
||||
},
|
||||
execute = function (event)
|
||||
local source, om, si, si_props, si_flags, target =
|
||||
putils:unwrap_find_target_event (event)
|
||||
|
||||
-- bypass the hook if the target was not found
|
||||
if target == nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- bypass the hook if the session item is a filter
|
||||
local node = si:get_associated_proxy ("node")
|
||||
local link_group = node.properties ["node.link-group"]
|
||||
if link_group ~= nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- Get the filter from the given target, if any
|
||||
local target_direction = cutils.getTargetDirection (si.properties)
|
||||
local filter_target = futils.get_filter_from_target (target_direction, target)
|
||||
if filter_target ~= nil then
|
||||
target = filter_target
|
||||
end
|
||||
|
||||
local can_passthrough, passthrough_compatible
|
||||
if target ~= nil then
|
||||
passthrough_compatible, can_passthrough =
|
||||
putils.checkPassthroughCompatibility (si, target)
|
||||
if putils.canLink (si_props, target) and passthrough_compatible then
|
||||
target_picked = true;
|
||||
end
|
||||
end
|
||||
|
||||
if target_picked then
|
||||
log:info (si,
|
||||
string.format ("... filter target picked: %s (%s), can_passthrough:%s",
|
||||
tostring (target.properties ["node.name"]),
|
||||
tostring (target.properties ["node.id"]),
|
||||
tostring (can_passthrough)))
|
||||
si_flags.can_passthrough = can_passthrough
|
||||
event:set_data ("target", target)
|
||||
end
|
||||
end
|
||||
}:register ()
|
||||
|
|
@ -14,7 +14,7 @@ log = Log.open_topic ("s-linking")
|
|||
|
||||
SimpleEventHook {
|
||||
name = "linking/prepare-link",
|
||||
after = "linking/find-best-target",
|
||||
after = "linking/get-filter-from-target",
|
||||
interests = {
|
||||
EventInterest {
|
||||
Constraint { "event.type", "=", "select-target" },
|
||||
|
|
|
|||
|
|
@ -12,8 +12,26 @@
|
|||
|
||||
local putils = require ("policy-utils")
|
||||
local cutils = require ("common-utils")
|
||||
local futils = require ("filter-utils")
|
||||
log = Log.open_topic ("s-linking")
|
||||
|
||||
function checkFilter (si, om, handle_nonstreams)
|
||||
-- always handle filters if handle_nonstreams is true, even if it is disabled
|
||||
if handle_nonstreams then
|
||||
return true
|
||||
end
|
||||
|
||||
-- always return true if this is not a filter
|
||||
local node = si:get_associated_proxy ("node")
|
||||
local link_group = node.properties["node.link-group"]
|
||||
if link_group == nil then
|
||||
return true
|
||||
end
|
||||
|
||||
local direction = cutils.getTargetDirection (si.properties)
|
||||
return futils.is_filter_enabled (direction, link_group)
|
||||
end
|
||||
|
||||
function checkLinkable (si, om, handle_nonstreams)
|
||||
local si_props = si.properties
|
||||
|
||||
|
|
@ -28,9 +46,48 @@ function checkLinkable (si, om, handle_nonstreams)
|
|||
return false, si_props
|
||||
end
|
||||
|
||||
-- check filters
|
||||
if not checkFilter (si, om, handle_nonstreams) then
|
||||
return false, si_props
|
||||
end
|
||||
|
||||
return true, si_props
|
||||
end
|
||||
|
||||
function unhandleLinkable (si, om)
|
||||
local si_id = si.id
|
||||
local valid, si_props = checkLinkable (si, om, true)
|
||||
if not valid then
|
||||
return
|
||||
end
|
||||
|
||||
log.info (si, string.format ("unhandling item: %s (%s)",
|
||||
tostring (si_props ["node.name"]), tostring (si_props ["node.id"])))
|
||||
|
||||
-- iterate over all the links in the graph and
|
||||
-- remove any links associated with this item
|
||||
for silink in om:iterate { type = "SiLink" } do
|
||||
local out_id = tonumber (silink.properties ["out.item.id"])
|
||||
local in_id = tonumber (silink.properties ["in.item.id"])
|
||||
|
||||
if out_id == si_id or in_id == si_id then
|
||||
local in_flags = putils:get_flags (in_id)
|
||||
local out_flags = putils:get_flags (out_id)
|
||||
|
||||
if out_id == si_id and in_flags.peer_id == out_id then
|
||||
in_flags.peer_id = nil
|
||||
elseif in_id == si_id and out_flags.peer_id == in_id then
|
||||
out_flags.peer_id = nil
|
||||
end
|
||||
|
||||
silink:remove ()
|
||||
log.info (silink, "... link removed")
|
||||
end
|
||||
end
|
||||
|
||||
putils:clear_flags (si_id)
|
||||
end
|
||||
|
||||
SimpleEventHook {
|
||||
name = "linking/linkable-removed",
|
||||
interests = {
|
||||
|
|
@ -43,40 +100,34 @@ SimpleEventHook {
|
|||
local si = event:get_subject ()
|
||||
local source = event:get_source ()
|
||||
local om = source:call ("get-object-manager", "session-item")
|
||||
local si_id = si.id
|
||||
local valid, si_props = checkLinkable (si, om, true)
|
||||
if not valid then
|
||||
return
|
||||
end
|
||||
|
||||
log:info (si, string.format ("unhandling item: %s (%s)",
|
||||
tostring (si_props ["node.name"]), tostring (si_props ["node.id"])))
|
||||
|
||||
-- iterate over all the links in the graph and
|
||||
-- remove any links associated with this item
|
||||
for silink in om:iterate { type = "SiLink" } do
|
||||
local out_id = tonumber (silink.properties ["out.item.id"])
|
||||
local in_id = tonumber (silink.properties ["in.item.id"])
|
||||
|
||||
if out_id == si_id or in_id == si_id then
|
||||
local in_flags = putils:get_flags (in_id)
|
||||
local out_flags = putils:get_flags (out_id)
|
||||
|
||||
if out_id == si_id and in_flags.peer_id == out_id then
|
||||
in_flags.peer_id = nil
|
||||
elseif in_id == si_id and out_flags.peer_id == in_id then
|
||||
out_flags.peer_id = nil
|
||||
end
|
||||
|
||||
silink:remove ()
|
||||
log:info (silink, "... link removed")
|
||||
end
|
||||
end
|
||||
|
||||
putils:clear_flags (si_id)
|
||||
unhandleLinkable (si, om)
|
||||
end
|
||||
}:register ()
|
||||
|
||||
function handleLinkables (source)
|
||||
local om = source:call ("get-object-manager", "session-item")
|
||||
|
||||
for si in om:iterate { type = "SiLinkable" } do
|
||||
local valid, si_props = checkLinkable (si, om)
|
||||
if not valid then
|
||||
goto skip_linkable
|
||||
end
|
||||
|
||||
-- check if we need to link this node at all
|
||||
local autoconnect = cutils.parseBool (si_props ["node.autoconnect"])
|
||||
if not autoconnect then
|
||||
log.debug (si, tostring (si_props ["node.name"]) .. " does not need to be autoconnected")
|
||||
goto skip_linkable
|
||||
end
|
||||
|
||||
-- push event to find target and link
|
||||
source:call ("push-event", "select-target", si, nil)
|
||||
|
||||
::skip_linkable::
|
||||
end
|
||||
end
|
||||
|
||||
SimpleEventHook {
|
||||
name = "linking/rescan",
|
||||
interests = {
|
||||
|
|
@ -86,28 +137,10 @@ SimpleEventHook {
|
|||
},
|
||||
execute = function (event)
|
||||
local source = event:get_source ()
|
||||
local om = source:call ("get-object-manager", "session-item")
|
||||
|
||||
log:info ("rescanning...")
|
||||
|
||||
for si in om:iterate { type = "SiLinkable" } do
|
||||
local valid, si_props = checkLinkable (si, om)
|
||||
if not valid then
|
||||
goto skip_linkable
|
||||
end
|
||||
|
||||
-- check if we need to link this node at all
|
||||
local autoconnect = cutils.parseBool (si_props ["node.autoconnect"])
|
||||
if not autoconnect then
|
||||
log:debug (si, tostring (si_props ["node.name"]) .. " does not need to be autoconnected")
|
||||
goto skip_linkable
|
||||
end
|
||||
|
||||
-- push event to find target and link
|
||||
source:call ("push-event", "select-target", si, nil)
|
||||
|
||||
::skip_linkable::
|
||||
end
|
||||
handleLinkables (source)
|
||||
end
|
||||
}:register ()
|
||||
|
||||
|
|
@ -131,3 +164,44 @@ SimpleEventHook {
|
|||
source:call ("schedule-rescan", "linking")
|
||||
end
|
||||
}:register ()
|
||||
|
||||
SimpleEventHook {
|
||||
name = "linking/rescan-trigger-before-filters-metadata-changed",
|
||||
before = "lib/filter-utils/rescan-metadata-changed",
|
||||
interests = {
|
||||
EventInterest {
|
||||
Constraint { "event.type", "=", "metadata-changed" },
|
||||
Constraint { "metadata.name", "=", "filters" },
|
||||
},
|
||||
},
|
||||
execute = function (event)
|
||||
local source = event:get_source ()
|
||||
local om = source:call ("get-object-manager", "session-item")
|
||||
|
||||
-- unlink all filters
|
||||
for si in om:iterate {
|
||||
type = "SiLinkable",
|
||||
Constraint { "node.link-group", "+" },
|
||||
} do
|
||||
unhandleLinkable (si, om)
|
||||
end
|
||||
|
||||
end
|
||||
}:register ()
|
||||
|
||||
SimpleEventHook {
|
||||
name = "linking/rescan-trigger-after-filters-metadata-changed",
|
||||
after = "lib/filter-utils/rescan-metadata-changed",
|
||||
interests = {
|
||||
EventInterest {
|
||||
Constraint { "event.type", "=", "metadata-changed" },
|
||||
Constraint { "metadata.name", "=", "filters" },
|
||||
},
|
||||
},
|
||||
execute = function (event)
|
||||
local source = event:get_source ()
|
||||
local om = source:call ("get-object-manager", "session-item")
|
||||
|
||||
source:call ("schedule-rescan", "linking")
|
||||
end
|
||||
}:register ()
|
||||
|
|
|
|||
|
|
@ -255,6 +255,8 @@ load_components (ScriptRunnerFixture *f, gconstpointer argv)
|
|||
load_component (f, "linking/find-best-target.lua", "script/lua");
|
||||
load_component (f, "linking/find-default-target.lua", "script/lua");
|
||||
load_component (f, "linking/find-defined-target.lua", "script/lua");
|
||||
load_component (f, "linking/find-filter-target.lua", "script/lua");
|
||||
load_component (f, "linking/get-filter-from-target.lua", "script/lua");
|
||||
load_component (f, "linking/link-target.lua", "script/lua");
|
||||
load_component (f, "linking/prepare-link.lua", "script/lua");
|
||||
load_component (f, "linking/rescan.lua", "script/lua");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue