mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-07 09:58:04 +02:00
policy-node: implement encoded audio passthrough
* also respect node.exclusive * also send error to clients if a target node was not found or if it is not possible to link to it * also allow dont-reconnect nodes to be handled normally the first time they appear, until they are linked once
This commit is contained in:
parent
52c4839a8c
commit
5dcd06c339
3 changed files with 165 additions and 45 deletions
|
|
@ -238,6 +238,12 @@ si_audio_adapter_configure (WpSessionItem * item, WpProperties *p)
|
|||
|
||||
self->node = g_object_ref (node);
|
||||
|
||||
wp_properties_set (si_props, "item.node.supports-encoded-fmts",
|
||||
self->have_encoded ? "true" : "false");
|
||||
|
||||
wp_properties_set (si_props, "item.node.encoded-only",
|
||||
self->encoded_only ? "true" : "false");
|
||||
|
||||
wp_properties_set (si_props, "item.factory.name", SI_FACTORY_NAME);
|
||||
wp_session_item_set_properties (item, g_steal_pointer (&si_props));
|
||||
return TRUE;
|
||||
|
|
@ -578,7 +584,7 @@ si_audio_adapter_set_ports_format (WpSiAdapter * item, WpSpaPod *f,
|
|||
}
|
||||
|
||||
/* build default format if NULL was given */
|
||||
if (!format) {
|
||||
if (!format && !g_strcmp0 (mode, "dsp")) {
|
||||
format = build_adapter_default_format (self, mode);
|
||||
g_return_if_fail (format);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ struct _WpSiStandardLink
|
|||
const gchar *out_item_port_context;
|
||||
const gchar *in_item_port_context;
|
||||
gboolean passive;
|
||||
gboolean passthrough;
|
||||
|
||||
/* activate */
|
||||
GPtrArray *node_links;
|
||||
|
|
@ -59,6 +60,7 @@ si_standard_link_reset (WpSessionItem * item)
|
|||
self->out_item_port_context = NULL;
|
||||
self->in_item_port_context = NULL;
|
||||
self->passive = FALSE;
|
||||
self->passthrough = FALSE;
|
||||
|
||||
WP_SESSION_ITEM_CLASS (si_standard_link_parent_class)->reset (item);
|
||||
}
|
||||
|
|
@ -108,11 +110,10 @@ si_standard_link_configure (WpSessionItem * item, WpProperties * p)
|
|||
"in.item.port.context");
|
||||
|
||||
str = wp_properties_get (si_props, "passive");
|
||||
if (str && sscanf(str, "%u", &self->passive) != 1)
|
||||
return FALSE;
|
||||
if (!str)
|
||||
wp_properties_setf (si_props, "passive", "%u",
|
||||
self->passive);
|
||||
self->passive = str && pw_properties_parse_bool (str);
|
||||
|
||||
str = wp_properties_get (si_props, "passthrough");
|
||||
self->passthrough = str && pw_properties_parse_bool (str);
|
||||
|
||||
g_weak_ref_set(&self->out_item, out_item);
|
||||
g_weak_ref_set(&self->in_item, in_item);
|
||||
|
|
@ -340,23 +341,24 @@ on_main_adapter_ready (GObject *obj, GAsyncResult * res, gpointer p)
|
|||
|
||||
si_out = WP_SI_ADAPTER (g_weak_ref_get (&self->out_item));
|
||||
si_in = WP_SI_ADAPTER (g_weak_ref_get (&self->in_item));
|
||||
|
||||
g_return_if_fail (si_out);
|
||||
g_return_if_fail (si_in);
|
||||
|
||||
/* get format and mode */
|
||||
other = si_out;
|
||||
format = wp_si_adapter_get_ports_format (si_out, &mode);
|
||||
if (!format) {
|
||||
other = si_in;
|
||||
format = wp_si_adapter_get_ports_format (si_in, &mode);
|
||||
}
|
||||
/* get the other adapter */
|
||||
other = ((gpointer)obj == (gpointer)si_in) ? si_out : si_in;
|
||||
|
||||
g_return_if_fail (mode);
|
||||
g_return_if_fail (format);
|
||||
wp_si_adapter_set_ports_format (other, wp_spa_pod_ref (format),
|
||||
g_strcmp0 (mode, "dsp") == 0 ? "dsp" : "convert",
|
||||
on_adapters_ready, transition);
|
||||
if (self->passthrough) {
|
||||
wp_si_adapter_set_ports_format (other, NULL, "passthrough",
|
||||
on_adapters_ready, transition);
|
||||
} else {
|
||||
format = wp_si_adapter_get_ports_format (WP_SI_ADAPTER (obj), &mode);
|
||||
g_return_if_fail (mode);
|
||||
g_return_if_fail (format);
|
||||
|
||||
wp_si_adapter_set_ports_format (other, wp_spa_pod_ref (format),
|
||||
g_strcmp0 (mode, "dsp") == 0 ? "dsp" : "convert",
|
||||
on_adapters_ready, transition);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
|
@ -366,7 +368,7 @@ ports_format_compatible (WpSpaPod *out_format, WpSpaPod *in_format,
|
|||
if (!out_format || !in_format || !out_mode || !in_mode)
|
||||
return FALSE;
|
||||
return wp_spa_pod_equal (out_format, in_format) &&
|
||||
(g_strcmp0 (out_mode, "dsp") == 0) == (g_strcmp0 (in_mode, "dsp") == 0);
|
||||
(g_strcmp0 (out_mode, "dsp") == 0) && (g_strcmp0 (in_mode, "dsp") == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -378,6 +380,12 @@ configure_and_link (WpSiStandardLink *self, WpSiAdapter *main,
|
|||
g_autoptr (WpSpaPod) main_fmt = NULL;
|
||||
g_autoptr (WpSpaPod) other_fmt = NULL;
|
||||
|
||||
if (self->passthrough) {
|
||||
wp_si_adapter_set_ports_format (main, NULL, "passthrough",
|
||||
on_main_adapter_ready, transition);
|
||||
return;
|
||||
}
|
||||
|
||||
main_fmt = wp_si_adapter_get_ports_format (main, &main_mode);
|
||||
other_fmt = wp_si_adapter_get_ports_format (other, &other_mode);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@ function parseBool(var)
|
|||
return var and (var == "true" or var == "1")
|
||||
end
|
||||
|
||||
function createLink (si, si_target)
|
||||
function createLink (si, si_target, passthrough, exclusive)
|
||||
local out_item = nil
|
||||
local in_item = nil
|
||||
local si_props = si.properties
|
||||
local target_props = si_target.properties
|
||||
|
||||
if si.properties["item.node.direction"] == "output" then
|
||||
if si_props["item.node.direction"] == "output" then
|
||||
-- playback
|
||||
out_item = si
|
||||
in_item = si_target
|
||||
|
|
@ -32,13 +34,14 @@ function createLink (si, si_target)
|
|||
out_item = si_target
|
||||
end
|
||||
|
||||
local passive = parseBool(si.properties["node.passive"]) or
|
||||
parseBool(si_target.properties["node.passive"])
|
||||
local passive = parseBool(si_props["node.passive"]) or
|
||||
parseBool(target_props["node.passive"])
|
||||
|
||||
Log.info (string.format("link %s <-> %s passive:%s",
|
||||
tostring(si.properties["node.name"]),
|
||||
tostring(si_target.properties["node.name"]),
|
||||
tostring(passive)))
|
||||
Log.info (
|
||||
string.format("link %s <-> %s passive:%s, passthrough:%s, exclusive:%s",
|
||||
tostring(si_props["node.name"]),
|
||||
tostring(target_props["node.name"]),
|
||||
tostring(passive), tostring(passthrough), tostring(exclusive)))
|
||||
|
||||
-- create and configure link
|
||||
local si_link = SessionItem ( "si-standard-link" )
|
||||
|
|
@ -46,6 +49,8 @@ function createLink (si, si_target)
|
|||
["out.item"] = out_item,
|
||||
["in.item"] = in_item,
|
||||
["passive"] = passive,
|
||||
["passthrough"] = passthrough,
|
||||
["exclusive"] = exclusive,
|
||||
["out.item.port.context"] = "output",
|
||||
["in.item.port.context"] = "input",
|
||||
["is.policy.item.link"] = true,
|
||||
|
|
@ -68,6 +73,47 @@ function createLink (si, si_target)
|
|||
end)
|
||||
end
|
||||
|
||||
function isLinked(si_target)
|
||||
local target_id = si_target.id
|
||||
local linked = false
|
||||
local exclusive = false
|
||||
|
||||
for l in links_om:iterate() do
|
||||
local p = l.properties
|
||||
local out_id = tonumber(p["out.item.id"])
|
||||
local in_id = tonumber(p["in.item.id"])
|
||||
linked = (out_id == target_id) or (in_id == target_id)
|
||||
if linked then
|
||||
exclusive = parseBool(p["exclusive"]) or parseBool(p["passthrough"])
|
||||
break
|
||||
end
|
||||
end
|
||||
return linked, exclusive
|
||||
end
|
||||
|
||||
function canPassthrough (si, si_target)
|
||||
-- both nodes must support encoded formats
|
||||
if not parseBool(si.properties["item.node.supports-encoded-fmts"])
|
||||
or not parseBool(si_target.properties["item.node.supports-encoded-fmts"]) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- make sure that the nodes have at least one common non-raw format
|
||||
local n1 = si:get_associated_proxy ("node")
|
||||
local n2 = si_target:get_associated_proxy ("node")
|
||||
for p1 in n1:iterate_params("EnumFormat") do
|
||||
local p1p = p1:parse()
|
||||
if p1p.properties.mediaSubtype ~= "raw" then
|
||||
for p2 in n2:iterate_params("EnumFormat") do
|
||||
if p1:filter(p2) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function canLink (properties, si_target)
|
||||
local target_properties = si_target.properties
|
||||
|
||||
|
|
@ -268,6 +314,8 @@ function checkLinkable(si)
|
|||
return true, si_props
|
||||
end
|
||||
|
||||
si_flags = {}
|
||||
|
||||
function handleLinkable (si)
|
||||
local valid, si_props = checkLinkable(si)
|
||||
if not valid then
|
||||
|
|
@ -283,28 +331,36 @@ function handleLinkable (si)
|
|||
|
||||
Log.info (si, "handling item: " .. tostring(si_props["node.name"]))
|
||||
|
||||
-- get reconnect
|
||||
local reconnect = not parseBool(si_props["node.dont-reconnect"])
|
||||
|
||||
-- find target
|
||||
local si_target = findDefinedTarget (si_props)
|
||||
if not si_target and not reconnect then
|
||||
Log.info (si, "... destroy node")
|
||||
local node = si:get_associated_proxy ("node")
|
||||
node:request_destroy()
|
||||
return
|
||||
elseif not si_target and reconnect then
|
||||
si_target = findUndefinedTarget (si_props)
|
||||
-- prepare flags table
|
||||
if not si_flags[si.id] then
|
||||
si_flags[si.id] = {}
|
||||
end
|
||||
|
||||
-- get other important node properties
|
||||
local reconnect = not parseBool(si_props["node.dont-reconnect"])
|
||||
local exclusive = parseBool(si_props["node.exclusive"])
|
||||
local must_passthrough = parseBool(si_props["item.node.encoded-only"])
|
||||
|
||||
-- find defined target
|
||||
local si_target = findDefinedTarget(si_props)
|
||||
local can_passthrough = si_target and canPassthrough(si, si_target)
|
||||
if si_target and must_passthrough and not can_passthrough then
|
||||
si_target = nil
|
||||
end
|
||||
|
||||
-- find fallback target
|
||||
if not si_target then
|
||||
Log.info (si, "... target not found")
|
||||
return
|
||||
si_target = findUndefinedTarget(si_props)
|
||||
can_passthrough = si_target and canPassthrough(si, si_target)
|
||||
if si_target and must_passthrough and not can_passthrough then
|
||||
si_target = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if item is linked to proper target, otherwise re-link
|
||||
local si_link, si_peer = getSiLinkAndSiPeer (si, si_props)
|
||||
if si_link then
|
||||
if si_peer and si_peer.id == si_target.id then
|
||||
if si_peer and si_target and si_peer.id == si_target.id then
|
||||
Log.debug (si, "... already linked to proper target")
|
||||
return
|
||||
end
|
||||
|
|
@ -320,8 +376,53 @@ function handleLinkable (si)
|
|||
end
|
||||
end
|
||||
|
||||
-- create new link
|
||||
createLink (si, si_target)
|
||||
-- if the stream has dont-reconnect and was already linked before,
|
||||
-- don't link it to a new target
|
||||
if not reconnect and si_flags[si.id].was_handled then
|
||||
si_target = nil
|
||||
end
|
||||
|
||||
-- check target's availability
|
||||
if si_target then
|
||||
local target_is_linked, target_is_exclusive = isLinked(si_target)
|
||||
if target_is_exclusive then
|
||||
Log.info(si, "... target is linked exclusively")
|
||||
si_target = nil
|
||||
end
|
||||
|
||||
if target_is_linked then
|
||||
if exclusive or must_passthrough then
|
||||
Log.info(si, "... target is already linked, cannot link exclusively")
|
||||
si_target = nil
|
||||
else
|
||||
-- disable passthrough, we can live without it
|
||||
can_passthrough = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not si_target then
|
||||
Log.info (si, "... target not found, reconnect:" .. tostring(reconnect))
|
||||
|
||||
local node = si:get_associated_proxy ("node")
|
||||
if not reconnect then
|
||||
Log.info (si, "... destroy node")
|
||||
node:request_destroy()
|
||||
end
|
||||
|
||||
local client_id = node.properties["client.id"]
|
||||
if client_id then
|
||||
local client = clients_om:lookup {
|
||||
Constraint { "bound-id", "=", client_id, type = "gobject" }
|
||||
}
|
||||
if client then
|
||||
client:send_error(node["bound-id"], -2, "no node available")
|
||||
end
|
||||
end
|
||||
else
|
||||
createLink (si, si_target, can_passthrough, exclusive)
|
||||
si_flags[si.id].was_handled = true
|
||||
end
|
||||
end
|
||||
|
||||
function unhandleLinkable (si)
|
||||
|
|
@ -341,6 +442,8 @@ function unhandleLinkable (si)
|
|||
Log.info (silink, "... link removed")
|
||||
end
|
||||
end
|
||||
|
||||
si_flags[si.id] = nil
|
||||
end
|
||||
|
||||
function rescan()
|
||||
|
|
@ -368,6 +471,8 @@ metadata_om = ObjectManager {
|
|||
|
||||
endpoints_om = ObjectManager { Interest { type = "SiEndpoint" } }
|
||||
|
||||
clients_om = ObjectManager { Interest { type = "client" } }
|
||||
|
||||
linkables_om = ObjectManager {
|
||||
Interest {
|
||||
type = "SiLinkable",
|
||||
|
|
@ -441,5 +546,6 @@ end)
|
|||
|
||||
metadata_om:activate()
|
||||
endpoints_om:activate()
|
||||
clients_om:activate()
|
||||
linkables_om:activate()
|
||||
links_om:activate()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue