scripts: add new sm-objects script

This allows loading objects on demand by adding entries on the
"sm-objects" metadata object.

It is useful to dynamically load pipewire modules such as loopbacks
or network modules without having to start a new pipewire process with
a hardcoded config file.

It is also useful to load new metadata objects in order to implement
the singleton metatada concept as discussed in pipewire!1742

This may be expanded in the future to be able to load other types of
objects.

The key name, combined with the subject, is considered a unique id for
this instance of the object. The value should be a json object with
a 'type' specifying the type of object, together with a 'name' and 'args'
This commit is contained in:
George Kiagiadakis
2023-10-17 18:12:18 +03:00
parent 256f60ebf2
commit 0ac2947aed
2 changed files with 106 additions and 0 deletions

View File

@@ -21,3 +21,6 @@ load_script("intended-roles.lua")
-- Automatically suspends idle nodes after 3 seconds
load_script("suspend-node.lua")
-- Allows loading objects on demand via metadata
load_script("sm-objects.lua")

103
src/scripts/sm-objects.lua Normal file
View File

@@ -0,0 +1,103 @@
-- WirePlumber
--
-- Copyright © 2023 Collabora Ltd.
-- @author George Kiagiadakis <george.kiagiadakis@collabora.com>
--
-- SPDX-License-Identifier: MIT
--
-- The script exposes a metadata object named "sm-objects" that clients can
-- use to load objects into the WirePlumber daemon process. The objects are
-- loaded as soon as the metadata is set and are destroyed when the metadata
-- is cleared.
--
-- To load an object, a client needs to set a metadata entry with:
--
-- * subject:
-- The ID of the owner of the object; you can use 0 here, but the
-- idea is to be able to restrict which clients can change and/or
-- delete these objects by using IDs of other objects appropriately
--
-- * key: "<UNIQUE-OBJECT-NAME>"
-- This is the name that will be used to identify the object.
-- If an object with the same name already exists, it will be destroyed.
-- Note that the keys are unique per subject, so you can have multiple
-- objects with the same name as long as they are owned by different subjects.
--
-- * type: "Spa:String:JSON"
--
-- * value: "{ type = <object-type>,
-- name = <object-name>,
-- args = { ...object arguments... } }"
-- The object type can be one of the following:
-- - "pw-module": loads a pipewire module: `name` and `args` are interpreted
-- just like a module entry in pipewire.conf
-- - "metadata": loads a metadata object with `metadata.name` = `name`
-- and any additional properties provided in `args`
--
on_demand_objects = {}
object_constructors = {
["pw-module"] = LocalModule,
["metadata"] = function (name, args)
local m = ImplMetadata (name, args)
m:activate (Features.ALL, function (m, e)
if e then
Log.warning ("failed to activate on-demand metadata `" .. name .. "`: " .. tostring (e))
end
end)
return m
end
}
function handle_metadata_changed (m, subject, key, type, value)
-- destroy all objects when metadata is cleared
if not key then
on_demand_objects = {}
return
end
local object_id = key .. "@" .. tostring(subject)
-- destroy existing object instance, if needed
if on_demand_objects[object_id] then
Log.debug("destroy on-demand object: " .. object_id)
on_demand_objects[object_id] = nil
end
if value then
local json = Json.Raw(value)
if not json:is_object() then
Log.warning("loading '".. object_id .. "' failed: expected JSON object, got: '" .. value .. "'")
return
end
local obj = json:parse(1)
if not obj.type then
Log.warning("loading '".. object_id .. "' failed: no object type specified")
return
end
if not obj.name then
Log.warning("loading '".. object_id .. "' failed: no object name specified")
return
end
local constructor = object_constructors[obj.type]
if not constructor then
Log.warning("loading '".. object_id .. "' failed: unknown object type: " .. obj.type)
return
end
Log.info("load on-demand object: " .. object_id .. " -> " .. obj.name)
on_demand_objects[object_id] = constructor(obj.name, obj.args)
end
end
objects_metadata = ImplMetadata ("sm-objects")
objects_metadata:activate (Features.ALL, function (m, e)
if e then
Log.warning ("failed to activate the sm-objects metadata: " .. tostring (e))
else
m:connect("changed", handle_metadata_changed)
end
end)