Commit ee121e30 authored by Julian Bouzas's avatar Julian Bouzas
Browse files

scripts: cleaned and improved policy scripts

The policy scripts can handle now client nodes that want to be linked with other
client nodes. This is needed for nodes created by the Pulse Audio Volume Control
to monitor audio comming from other application client nodes.
parent b46a907d
Pipeline #317060 passed with stages
in 1 minute and 36 seconds
......@@ -5,12 +5,6 @@
--
-- SPDX-License-Identifier: MIT
target_class_assoc = {
["Stream/Input/Audio"] = "Audio/Source",
["Stream/Output/Audio"] = "Audio/Sink",
["Stream/Input/Video"] = "Video/Source",
}
-- Receive script arguments from config.lua
local config = ...
config.roles = config.roles or {}
......@@ -36,12 +30,24 @@ function getSessionItemById (si_id, om)
}
end
function findTargetEndpoint (node, target_media_class)
local media_role = findRole(node.properties["media.role"])
function findTargetEndpoint (node, media_class)
local target_class_assoc = {
["Stream/Input/Audio"] = "Audio/Source",
["Stream/Output/Audio"] = "Audio/Sink",
["Stream/Input/Video"] = "Video/Source",
}
local media_role = nil
local highest_priority = -1
local target = nil
-- get target media class
local target_media_class = target_class_assoc[media_class]
if not target_media_class then
return nil
end
-- find highest priority endpoint by role
media_role = findRole(node.properties["media.role"])
for si_target_ep in siendpoints_om:iterate {
Constraint { "role", "=", media_role, type = "pw-global" },
Constraint { "media.class", "=", target_media_class, type = "pw-global" },
......@@ -70,14 +76,16 @@ function createLink (si, si_target_ep)
-- capture
out_item = si_target_ep
in_item = si
if target_class_assoc[media_class] ~= target_media_class then
if string.find (target_media_class, "Input") or
string.find (target_media_class, "Sink") then
out_context = "reverse"
end
else
-- playback
out_item = si
in_item = si_target_ep
if target_class_assoc[media_class] ~= target_media_class then
if string.find (target_media_class, "Output") or
string.find (target_media_class, "Source") then
in_context = "reverse"
end
end
......@@ -120,32 +128,43 @@ function getSiLinkAndSiPeerEndpoint (si)
return nil, nil
end
function handleSiPortInfo (si)
function isSiPortInfoValid (si)
-- only handle session items that has a node associated proxy
local node = si:get_associated_proxy ("node")
if not node or not node.properties then
return
return false
end
-- only handle session item that has a valid target media class
-- only handle stream session items
local media_class = node.properties["media.class"]
local target_media_class = target_class_assoc[media_class]
if not target_media_class then
return
if not string.find (media_class, "Stream") then
return false
end
-- Determine if we can handle item by this policy
local media_role = node.properties["media.role"]
if siendpoints_om:get_n_objects () == 0 or media_role == nil then
Log.info (si, "item won't be handled by this policy")
return false
end
return true
end
function handleSiPortInfo (si)
-- check if item is valid
if not isSiPortInfoValid (si) then
return
end
local node = si:get_associated_proxy ("node")
local media_class = node.properties["media.class"]
local media_role = node.properties["media.role"]
Log.info (si, "handling item " .. node.properties["node.name"] ..
" with role " .. media_role)
-- find proper target endpoint
local si_target_ep = findTargetEndpoint (node, target_media_class)
local si_target_ep = findTargetEndpoint (node, media_class)
if not si_target_ep then
Log.info (si, "target endpoint not found")
return
......@@ -167,20 +186,24 @@ function handleSiPortInfo (si)
createLink (si, si_target_ep)
end
function reevaluateLinks ()
-- check port info session items and register new links
for si in siportinfos_om:iterate() do
handleSiPortInfo (si)
function unhandleSiPortInfo (si)
-- check if item is valid
if not isSiPortInfoValid (si) then
return
end
-- check link session items and unregister them if not used
local node = si:get_associated_proxy ("node")
Log.info (si, "unhandling item " .. node.properties["node.name"])
-- remove any links associated with this item
for silink in silinks_om:iterate() do
local out_id = tonumber (silink.properties["out.item.id"])
local in_id = tonumber (silink.properties["in.item.id"])
if (getSessionItemById (out_id, siendpoints_om) and not getSessionItemById (in_id, siportinfos_om)) or
(getSessionItemById (in_id, siendpoints_om) and not getSessionItemById (out_id, siportinfos_om)) then
silink:remove ()
Log.info (silink, "link removed")
local out_id = tostring (silink.properties["out.item.id"])
local in_id = tostring (silink.properties["in.item.id"])
for si in siportinfos_om:iterate() do
if out_id == si.id or in_id == si.id then
silink:remove ()
Log.info (silink, "link removed")
end
end
end
end
......@@ -197,8 +220,12 @@ silinks_om = ObjectManager { Interest { type = "SiLink",
Constraint { "is.policy.endpoint.client.link", "=", true, type = "pw-global" },
} }
siportinfos_om:connect("objects-changed", function (om)
reevaluateLinks ()
siportinfos_om:connect("object-added", function (om, si)
handleSiPortInfo (si)
end)
siportinfos_om:connect("object-removed", function (om, si)
unhandleSiPortInfo (si)
end)
siendpoints_om:activate()
......
......@@ -95,21 +95,28 @@ function createLink (si_ep, si_target)
return
end
-- activate and register
si_link:activate (Feature.SessionItem.ACTIVE, function (link)
Log.info (link, "link activated")
link:register ()
end)
-- register
si_link:register ()
-- activate
si_link:activate (Feature.SessionItem.ACTIVE)
end
function getSiLinkAndSiPeer (si_ep)
function getSiLinkAndSiPeer (si_ep, target_media_class)
for silink in silinks_om:iterate() do
local out_id = tonumber(silink.properties["out.item.id"])
local in_id = tonumber(silink.properties["in.item.id"])
if out_id == si_ep.id then
return silink, getSessionItemById (in_id, siportinfos_om)
elseif in_id == si_ep.id then
return silink, getSessionItemById (out_id, siportinfos_om)
if out_id == si_ep.id or in_id == si_ep.id then
local is_out = out_id == si_ep.id and true or false
for peer in siportinfos_om:iterate() do
if peer.id == (is_out and in_id or out_id) then
local peer_node = peer:get_associated_proxy ("node")
local peer_media_class = peer_node.properties["media.class"]
if peer_media_class == target_media_class then
return silink, peer
end
end
end
end
end
return nil, nil
......@@ -133,7 +140,7 @@ function handleSiEndpoint (si_ep)
end
-- Check if item is linked to proper target endpoint, otherwise re-link
local si_link, si_peer = getSiLinkAndSiPeer (si_ep)
local si_link, si_peer = getSiLinkAndSiPeer (si_ep, target_media_class)
if si_link then
if si_peer and si_peer.id == si_target.id then
Log.info (si_ep, "already linked to proper target")
......
......@@ -12,12 +12,6 @@ local config = ...
config.move = config.move or false
config.follow = config.follow or false
target_class_assoc = {
["Stream/Input/Audio"] = "Audio/Source",
["Stream/Output/Audio"] = "Audio/Sink",
["Stream/Input/Video"] = "Video/Source",
}
function createLink (si, si_target)
local node = si:get_associated_proxy ("node")
local target_node = si_target:get_associated_proxy ("node")
......@@ -33,14 +27,16 @@ function createLink (si, si_target)
-- capture
out_item = si_target
in_item = si
if target_class_assoc[media_class] ~= target_media_class then
if string.find (target_media_class, "Input") or
string.find (target_media_class, "Sink") then
out_context = "reverse"
end
else
-- playback
out_item = si
in_item = si_target
if target_class_assoc[media_class] ~= target_media_class then
if string.find (target_media_class, "Output") or
string.find (target_media_class, "Source") then
in_context = "reverse"
end
end
......@@ -60,13 +56,14 @@ function createLink (si, si_target)
["is.policy.item.link"] = true,
} then
Log.warning (si_link, "failed to configure si-standard-link")
return
end
-- activate and register
si_link:activate (Feature.SessionItem.ACTIVE, function (link)
Log.info (link, "link activated")
link:register ()
end)
-- register
si_link:register ()
-- activate
si_link:activate (Feature.SessionItem.ACTIVE)
end
function findTargetByTargetNodeMetadata (node)
......@@ -133,58 +130,80 @@ function findDefinedTarget (node)
return si_target
end
function findUndefinedTarget (target_media_class)
local si_target = findTargetByDefaultNode (target_media_class)
if not si_target then
si_target = findTargetByFirstAvailable (target_media_class)
function findUndefinedTarget (media_class)
local target_class_assoc = {
["Stream/Input/Audio"] = "Audio/Source",
["Stream/Output/Audio"] = "Audio/Sink",
["Stream/Input/Video"] = "Video/Source",
}
local si_target = nil
local target_media_class = target_class_assoc[media_class]
if target_media_class then
si_target = findTargetByDefaultNode (target_media_class)
if not si_target then
si_target = findTargetByFirstAvailable (target_media_class)
end
end
return si_target
end
function getSiLinkAndSiPeer (si)
function getSiLinkAndSiPeer (si, target_media_class)
for silink in silinks_om:iterate() do
local out_id = tonumber(silink.properties["out.item.id"])
local in_id = tonumber(silink.properties["in.item.id"])
if out_id == si.id then
return silink, siportinfos_om:lookup {
Constraint { "id", "=", in_id, type = "gobject" }
}
elseif in_id == si.id then
return silink, siportinfos_om:lookup {
Constraint { "id", "=", out_id, type = "gobject" }
}
if out_id == si.id or in_id == si.id then
local is_out = out_id == si.id and true or false
for peer in siportinfos_om:iterate() do
if peer.id == (is_out and in_id or out_id) then
local peer_node = peer:get_associated_proxy ("node")
local peer_media_class = peer_node.properties["media.class"]
if peer_media_class == target_media_class then
return silink, peer
end
end
end
end
end
return nil, nil
end
function handleSiPortInfo (si)
function isSiPortInfoValid (si)
-- only handle session items that has a node associated proxy
local node = si:get_associated_proxy ("node")
if not node or not node.properties then
return
return false
end
-- only handle session item that has a valid target media class
-- only handle stream session items
local media_class = node.properties["media.class"]
local target_media_class = target_class_assoc[media_class]
if not target_media_class then
return
if not string.find (media_class, "Stream") then
return false
end
-- Determine if we can handle item by this policy
local media_role = node.properties["media.role"]
if siendpoints_om:get_n_objects () > 0 and media_role ~= nil then
Log.info (si, "item won't be handled by this policy")
return false
end
return true
end
function handleSiPortInfo (si)
-- check if item is valid
if not isSiPortInfoValid (si) then
return
end
local node = si:get_associated_proxy ("node")
local media_class = node.properties["media.class"]
Log.info (si, "handling item " .. node.properties["node.name"])
-- find target
local si_target = findDefinedTarget (node)
if not si_target then
si_target = findUndefinedTarget (target_media_class)
si_target = findUndefinedTarget (media_class)
end
if not si_target then
Log.info (si, "target not found")
......@@ -192,7 +211,9 @@ function handleSiPortInfo (si)
end
-- Check if item is linked to proper target, otherwise re-link
local si_link, si_peer = getSiLinkAndSiPeer (si)
local target_node = si_target:get_associated_proxy ("node")
local target_media_class = target_node.properties["media.class"]
local si_link, si_peer = getSiLinkAndSiPeer (si, target_media_class)
if si_link then
if si_peer and si_peer.id == si_target.id then
Log.info (si, "already linked to proper target")
......@@ -207,26 +228,31 @@ function handleSiPortInfo (si)
createLink (si, si_target)
end
function reevaluateLinks ()
-- check port info session items and register new links
for si in siportinfos_om:iterate() do
handleSiPortInfo (si)
function unhandleSiPortInfo (si)
-- check if item is valid
if not isSiPortInfoValid (si) then
return
end
-- check link session items and unregister them if not used
local node = si:get_associated_proxy ("node")
Log.info (si, "unhandling item " .. node.properties["node.name"])
-- remove any links associated with this item
for silink in silinks_om:iterate() do
local used = 0
local out_id_str = silink.properties["out.item.id"]
local in_id_str = silink.properties["in.item.id"]
local out_id = tostring (silink.properties["out.item.id"])
local in_id = tostring (silink.properties["in.item.id"])
for si in siportinfos_om:iterate() do
if tonumber (out_id_str) == si.id or tonumber (in_id_str) == si.id then
used = used + 1
if out_id == si.id or in_id == si.id then
silink:remove ()
Log.info (silink, "link removed")
end
end
if used ~= 2 then
silink:remove ()
Log.info (silink, "link removed")
end
end
end
function reevaluateSiPortInfos ()
for si in siportinfos_om:iterate() do
handleSiPortInfo (si)
end
end
......@@ -247,7 +273,7 @@ silinks_om = ObjectManager { Interest { type = "SiLink",
-- listen for default node changes if config.follow is enabled
if config.follow then
default_nodes:connect("changed", function (p)
reevaluateLinks ()
reevaluateSiPortInfos ()
end)
end
......@@ -256,14 +282,18 @@ if config.move then
metadatas_om:connect("object-added", function (om, metadata)
metadata:connect("changed", function (m, subject, key, t, value)
if key == "target.node" then
reevaluateLinks ()
reevaluateSiPortInfos ()
end
end)
end)
end
siportinfos_om:connect("objects-changed", function (om)
reevaluateLinks ()
siportinfos_om:connect("object-added", function (om, si)
handleSiPortInfo (si)
end)
siportinfos_om:connect("object-removed", function (om, si)
unhandleSiPortInfo (si)
end)
metadatas_om:activate()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment