Skip to content
Snippets Groups Projects
Commit 590b63bb authored by Frederic Danis's avatar Frederic Danis
Browse files

Add Wireplumber policiy samples to Audio Management concept


This demonstrates simple definition of policy roles and a sample policy
script written in Lua.

Signed-off-by: default avatarFrédéric Danis <frederic.danis@collabora.com>
parent 383c55dd
No related branches found
No related tags found
1 merge request!249Add Wireplumber policy samples to Audio Management concept
Pipeline #255474 passed
......@@ -454,8 +454,9 @@ the user will not hear.
An internal priority module can be written. This module would associate a
priority to all differents streams' metadata. It is loaded statically from the
config file. See [Routing data structure example] for an example of data
structure.
config file. See
[Routing data structure example]( {{< ref "#routing-data-structure-example" >}} )
for an example of data structure.
#### Hybrid routing module maps stream metadata to external audio router calls
......@@ -580,6 +581,120 @@ Since the CE domain do not know what is happening in the automotive domain.
| traffic_info | INFO1 | alert_sink | mix |
| gps | INFO2 | main_sink | mix |
### WirePlumber policy samples
All the policies in WirePlumber are completely scriptable and written in Lua.
The Lua API Documentation can be found
[here](https://pipewire.pages.freedesktop.org/wireplumber/lua_api.html).
The default roles, priorities and related actions are defined in
`/etc/wireconfig/policy.lua.d/50-endpoints-config.lua` and
can be re-written to support the standalone setup defined in
[Routing data structure example]( {{< ref "#routing-data-structure-example" >}} ):
```
default_policy.policy.roles = {
-- main sink
["Multimedia"] = { ["priority"] = 0, ["action.default"] = "cork", ["alias"] = { "Movie", "Music", "Game" }, },
["GPS"] = { ["priority"] = 5, ["action.default"] = "duck", },
["Phone"] = { ["priority"] = 7, ["action.default"] = "cork", ["alias"] = { "CustomRingtone" }, },
-- alert sink
["New_email"] = { ["priority"] = 1, ["action.default"] = "mix", },
["Traffic_info"] = { ["priority"] = 6, ["action.default"] = "mix", },
["Ringtone"] = { ["priority"] = 7, ["action.default"] = "mix", },
}
default_policy.endpoints = {
["endpoint.multimedia"] = { ["media.class"] = "Audio/Sink", ["role"] = "Multimedia", },
["endpoint.gps"] = { ["media.class"] = "Audio/Sink", ["role"] = "GPS", },
["endpoint.phone"] = { ["media.class"] = "Audio/Sink", ["role"] = "Phone", },
["endpoint.ringtone"] = { ["media.class"] = "Audio/Sink", ["role"] = "Ringtone", },
["endpoint.new_email"] = { ["media.class"] = "Audio/Sink", ["role"] = "New_email", },
["endpoint.traffic_info"] = { ["media.class"] = "Audio/Sink", ["role"] = "Traffic_info", },
}
```
And, for example, a policy to automatically switch Bluetooth from A2DP to
HSP/HFP profile when a specific application starts, e.g. Zoom, could be defined
like:
```
#!/usr/bin/wpexec
--
-- WirePlumber
--
-- Copyright © 2021 Collabora Ltd.
-- @author George Kiagiadakis <george.kiagiadakis@collabora.com>
--
-- SPDX-License-Identifier: MIT
--
-- This is an example of a standalone policy making script. It can be executed
-- either on top of another instance of wireplumber or pipewire-media-session,
-- as a standalone executable, or it can be placed in WirePlumber's scripts
-- directory and loaded together with other scripts.
--
-- The script basically watches for a client application called
-- "ZOOM VoiceEngine", and when it appears (i.e. Zoom starts), it switches
-- the profile of all connected bluetooth devices to the "headset-head-unit"
-- (a.k.a HSP Headset Audio) profile. When Zoom exits, it switches again the
-- profile of all bluetooth devices to A2DP Sink.
--
-- The script can be customized further to look for other clients and/or
-- change the profile of a specific device, by customizing the constraints.
-----------------------------------------------------------------------------
devices_om = ObjectManager {
Interest { type = "device",
Constraint { "device.api", "=", "bluez5" },
}
}
clients_om = ObjectManager {
Interest { type = "client",
Constraint { "application.name", "=", "ZOOM VoiceEngine" },
}
}
function set_profile(profile_name)
for device in devices_om:iterate() do
local index = nil
local desc = nil
for profile in device:iterate_params("EnumProfile") do
local p = profile:parse()
if p.properties.name == profile_name then
index = p.properties.index
desc = p.properties.description
break
end
end
if index then
local pod = Pod.Object {
"Spa:Pod:Object:Param:Profile", "Profile",
index = index
}
print("Setting profile of '"
.. device.properties["device.description"]
.. "' to: " .. desc)
device:set_params("Profile", pod)
end
end
end
clients_om:connect("object-added", function (om, client)
print("Client '" .. client.properties["application.name"] .. "' connected")
set_profile("headset-head-unit")
end)
clients_om:connect("object-removed", function (om, client)
print("Client '" .. client.properties["application.name"] .. "' disconnected")
set_profile("a2dp-sink")
end)
devices_om:activate()
clients_om:activate()
```
### Testability
The key point to keep in mind for testing is that several applications can
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment