180 lines
5.6 KiB
Lua
180 lines
5.6 KiB
Lua
-- WirePlumber
|
|
--
|
|
-- Copyright © 2022 Collabora Ltd.
|
|
--
|
|
-- SPDX-License-Identifier: MIT
|
|
|
|
-- looks for changes in user-preferences and devices added/removed and schedules
|
|
-- rescan and pushes "select-default-node" event for each of the media_classes
|
|
|
|
log = Log.open_topic ("s-default-nodes")
|
|
|
|
-- looks for changes in user-preferences and devices added/removed and schedules
|
|
-- rescan
|
|
SimpleEventHook {
|
|
name = "default-nodes/rescan-trigger",
|
|
interests = {
|
|
EventInterest {
|
|
Constraint { "event.type", "c", "session-item-added", "session-item-removed" },
|
|
Constraint { "event.session-item.interface", "=", "linkable" },
|
|
Constraint { "media.class", "#", "Audio/*" },
|
|
},
|
|
EventInterest {
|
|
Constraint { "event.type", "c", "session-item-added", "session-item-removed" },
|
|
Constraint { "event.session-item.interface", "=", "linkable" },
|
|
Constraint { "media.class", "#", "Video/*" },
|
|
},
|
|
EventInterest {
|
|
Constraint { "event.type", "=", "metadata-changed" },
|
|
Constraint { "metadata.name", "=", "default" },
|
|
Constraint { "event.subject.key", "c", "default.configured.audio.sink",
|
|
"default.configured.audio.source", "default.configured.video.source"
|
|
},
|
|
},
|
|
EventInterest {
|
|
Constraint { "event.type", "=", "device-params-changed"},
|
|
Constraint { "event.subject.param-id", "c", "Route", "EnumRoute"},
|
|
},
|
|
},
|
|
execute = function (event)
|
|
local source = event:get_source ()
|
|
source:call ("schedule-rescan", "default-nodes")
|
|
end
|
|
}:register ()
|
|
|
|
-- pushes "select-default-node" event for each of the media_classes
|
|
SimpleEventHook {
|
|
name = "default-nodes/rescan",
|
|
interests = {
|
|
EventInterest {
|
|
Constraint { "event.type", "=", "rescan-for-default-nodes" },
|
|
},
|
|
},
|
|
execute = function (event)
|
|
local source = event:get_source ()
|
|
local si_om = source:call ("get-object-manager", "session-item")
|
|
local devices_om = source:call ("get-object-manager", "device")
|
|
|
|
log:trace ("re-evaluating default nodes")
|
|
|
|
-- Audio Sink
|
|
pushSelectDefaultNodeEvent (source, si_om, devices_om, "audio.sink", "in", {
|
|
"Audio/Sink", "Audio/Duplex"
|
|
})
|
|
|
|
-- Audio Source
|
|
pushSelectDefaultNodeEvent (source, si_om, devices_om, "audio.source", "out", {
|
|
"Audio/Source", "Audio/Source/Virtual", "Audio/Duplex", "Audio/Sink"
|
|
})
|
|
|
|
-- Video Source
|
|
pushSelectDefaultNodeEvent (source, si_om, devices_om, "video.source", "out", {
|
|
"Video/Source", "Video/Source/Virtual"
|
|
})
|
|
end
|
|
}:register ()
|
|
|
|
function pushSelectDefaultNodeEvent (source, si_om, devices_om, def_node_type,
|
|
port_direction, media_classes)
|
|
local nodes =
|
|
collectAvailableNodes (si_om, devices_om, port_direction, media_classes)
|
|
local event = source:call ("create-event", "select-default-node", nil, {
|
|
["default-node.type"] = def_node_type,
|
|
})
|
|
event:set_data ("available-nodes", Json.Array (nodes))
|
|
EventDispatcher.push_event (event)
|
|
end
|
|
|
|
-- Return an array table where each element is another table containing all the
|
|
-- node properties of all the nodes that can be selected for a given media class
|
|
-- set and direction
|
|
function collectAvailableNodes (si_om, devices_om, port_direction, media_classes)
|
|
local collected = {}
|
|
|
|
for linkable in si_om:iterate {
|
|
type = "SiLinkable",
|
|
Constraint { "media.class", "c", table.unpack (media_classes) },
|
|
} do
|
|
local linkable_props = linkable.properties
|
|
local node = linkable:get_associated_proxy ("node")
|
|
|
|
-- check that the node has ports in the requested direction
|
|
if not node:lookup_port {
|
|
Constraint { "port.direction", "=", port_direction }
|
|
} then
|
|
goto next_linkable
|
|
end
|
|
|
|
-- check that the node has available routes,
|
|
-- if it is associated to a real device
|
|
if not nodeHasAvailableRoutes (node, devices_om) then
|
|
goto next_linkable
|
|
end
|
|
|
|
table.insert (collected, Json.Object (node.properties))
|
|
|
|
::next_linkable::
|
|
end
|
|
|
|
return collected
|
|
end
|
|
|
|
-- If the node has an associated device, verify that it has an available
|
|
-- route. Some UCM profiles expose all paths (headphones, HDMI, etc) as nodes,
|
|
-- even though they may not be connected... See #145
|
|
function nodeHasAvailableRoutes (node, devices_om)
|
|
local properties = node.properties
|
|
local device_id = properties ["device.id"]
|
|
local cpd = properties ["card.profile.device"]
|
|
|
|
if not device_id or not cpd then
|
|
return true
|
|
end
|
|
|
|
-- Get the device
|
|
local device = devices_om:lookup {
|
|
Constraint { "bound-id", "=", device_id, type = "gobject" }
|
|
}
|
|
if not device then
|
|
return true
|
|
end
|
|
|
|
-- Check if the current device route supports the node card device profile
|
|
for r in device:iterate_params ("Route") do
|
|
local route = r:parse ()
|
|
local route_props = route.properties
|
|
if route_props.device == tonumber (cpd) then
|
|
if route_props.available == "no" then
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check if available routes support the node card device profile
|
|
local found = 0
|
|
for r in device:iterate_params ("EnumRoute") do
|
|
local route = r:parse ()
|
|
local route_props = route.properties
|
|
if type (route_props.devices) == "table" then
|
|
for _, i in ipairs (route_props.devices) do
|
|
if i == tonumber (cpd) then
|
|
found = found + 1
|
|
if route_props.available ~= "no" then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- The node is part of a profile without routes so we assume it
|
|
-- is available. This can happen for Pro Audio profiles
|
|
if found == 0 then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|