policy-node: forward filter stream's format to filter device

Currently, when using filter nodes such as echo-cancel or filer-chain, the
ports format of the virtual stream node might not always match the format of its
associated virtual device node. This can happen if the virtual stream node is
linked with a real device node configured with many channels, such as surround
profile. In that case, the channel configuration is not forwarded to the virtual
device node, making the application not being aware of a surround setup.

This change listens for port format changes in the virtual stream nodes, and
reconfigures the virtual device node ports everytime the virtual stream node's
port format changes.

This behavior is experimental and optional, so it is disabled by default.
This commit is contained in:
Julian Bouzas 2022-05-03 14:47:22 -04:00 committed by George Kiagiadakis
parent 07179452e2
commit 7080e9961c
2 changed files with 105 additions and 5 deletions

View file

@ -6,6 +6,11 @@ default_policy.policy = {
["move"] = true, -- moves session items when metadata target.node changes
["follow"] = true, -- moves session items to the default device when it has changed
-- Whether to forward the ports format of filter stream nodes to their
-- associated filter device nodes. This is needed for application to stream
-- surround audio if echo-cancel is enabled.
["filter.forward-format"] = false,
-- Set to 'true' to disable channel splitting & merging on nodes and enable
-- passthrough of audio in the same format as the format of the device.
-- Note that this breaks JACK support; it is generally not recommended

View file

@ -11,6 +11,7 @@ local config = ... or {}
-- ensure config.move and config.follow are not nil
config.move = config.move or false
config.follow = config.follow or false
config.filter_forward_format = config["filter.forward-format"] or false
local self = {}
self.scanning = false
@ -648,10 +649,7 @@ function handleLinkable (si)
Log.info (si, string.format("handling item: %s (%s)",
tostring(si_props["node.name"]), tostring(si_props["node.id"])))
-- prepare flags table
if not si_flags[si.id] then
si_flags[si.id] = {}
end
ensureSiFlags(si)
-- get other important node properties
local reconnect = not parseBool(si_props["node.dont-reconnect"])
@ -857,8 +855,105 @@ if config.move then
end)
end
function findAssociatedLinkGroupNode (si)
local si_props = si.properties
local node = si:get_associated_proxy ("node")
local link_group = node.properties["node.link-group"]
if link_group == nil then
return nil
end
-- get the associated media class
local assoc_direction = getTargetDirection(si_props)
local assoc_media_class =
si_props["media.type"] ..
(assoc_direction == "input" and "/Sink" or "/Source")
-- find the linkable with same link group and matching assoc media class
for assoc_si in linkables_om:iterate() do
local assoc_node = assoc_si:get_associated_proxy ("node")
local assoc_link_group = assoc_node.properties["node.link-group"]
if assoc_link_group == link_group and
assoc_media_class == assoc_node.properties["media.class"] then
return assoc_si
end
end
return nil
end
function onLinkGroupPortsStateChanged (si, old_state, new_state)
local new_str = tostring(new_state)
local si_props = si.properties
-- only handle items with configured ports state
if new_str ~= "configured" then
return
end
Log.info (si, "ports format changed on " .. si_props["node.name"])
-- find associated device
local si_device = findAssociatedLinkGroupNode (si)
if si_device ~= nil then
local device_node_name = si_device.properties["node.name"]
-- get the stream format
local f, m = si:get_ports_format()
-- unregister the device
Log.info (si_device, "unregistering " .. device_node_name)
si_device:remove()
-- set new format in the device
Log.info (si_device, "setting new format in " .. device_node_name)
si_device:set_ports_format(f, m, function (item, e)
if e ~= nil then
Log.warning (item, "failed to configure ports in " ..
device_node_name .. ": " .. e)
end
-- register back the device
Log.info (item, "registering " .. device_node_name)
item:register()
end)
end
end
function ensureSiFlags (si)
-- prepare flags table
if not si_flags[si.id] then
si_flags[si.id] = {}
end
end
function checkFiltersPortsState (si)
local si_props = si.properties
local node = si:get_associated_proxy ("node")
local link_group = node.properties["node.link-group"]
ensureSiFlags(si)
-- only listen for ports state changed on audio filter streams
if si_flags[si.id].ports_state_signal ~= true and
si_props["item.factory.name"] == "si-audio-adapter" and
si_props["item.node.type"] == "stream" and
link_group ~= nil then
si:connect("adapter-ports-state-changed", onLinkGroupPortsStateChanged)
si_flags[si.id].ports_state_signal = true
Log.info (si, "listening ports state changed on " .. si_props["node.name"])
end
end
linkables_om:connect("object-added", function (om, si)
if si.properties["item.node.type"] ~= "stream" then
local si_props = si.properties
-- Forward filters ports format to associated virtual devices if enabled
if config.filter_forward_format then
checkFiltersPortsState (si)
end
if si_props["item.node.type"] ~= "stream" then
scheduleRescan ()
else
handleLinkable (si)