meta-agl: split wireplumber to run in multiple instances 62/26662/2
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Mon, 20 Sep 2021 11:29:45 +0000 (14:29 +0300)
committerJan-Simon Moeller <jsmoeller@linuxfoundation.org>
Thu, 14 Oct 2021 15:21:16 +0000 (15:21 +0000)
The "IC SoundManager" design specifies that there needs to be
a session manager running in the host to manage devices and permissions,
but all the domain-specific policy and app management needs to run in
the IVI container, together with the apps. With this split, we are able
to achieve that.

In addition to the "host" and "policy" instances, we are also adding
a "bluetooth" one, which loads the bluetooth plugin and manages the
bluetooth device. This can be moved to the IVI container as well,
or elsewhere... it only depends on bluez (so it must run in the same
container as bluez).

For now, given the absence of an IVI container in the lxc-demo image,
all instances are running in the host, but it is trivial to move all
the non-host ones to another container later.

To compliment pipewire-ic-ipc, this change also adds an "alsa-suspend"
lua script that runs in the context of the "host" wireplumber instance
and its purpose is to mute pipewire-managed alsa devices when there is
a sound playing in the IC container (on another alsa device).

Finally, this change also adds V4L2 configuration in the "host" wireplumber
instance, which is still disabled (and untested), but you can easily enable
it for experimentation by uncommenting the relevant line in
host.lua.d/90-enable-all.lua

Bug-AGL: SPEC-4027
Signed-off-by: George Kiagiadakis <george.kiagiadakis@collabora.com>
Change-Id: I9febc4f3919e7c559a5d7d32bfe7bc95c75934f2
Reviewed-on: https://gerrit.automotivelinux.org/gerrit/c/AGL/meta-agl/+/26662
Tested-by: Jenkins Job builder account
ci-image-build: Jenkins Job builder account
ci-image-boot-test: Jenkins Job builder account
Reviewed-by: Naoto YAMAGUCHI <naoto.yamaguchi@aisin.co.jp>
Reviewed-by: Jan-Simon Moeller <jsmoeller@linuxfoundation.org>
13 files changed:
meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/wireplumber/wireplumber-config-agl_git.bbappend
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/99-load-modules.lua [deleted file]
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/alsa-suspend.lua [new file with mode: 0644]
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/bluetooth.conf [new file with mode: 0644]
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/bluetooth.lua.d/30-bluez-monitor.lua [moved from meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/30-bluez-monitor.lua with 96% similarity]
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/host.lua.d/30-alsa-monitor.lua [moved from meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/30-alsa-monitor.lua with 100% similarity]
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/host.lua.d/30-v4l2-monitor.lua [new file with mode: 0644]
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/host.lua.d/40-device-defaults.lua [new file with mode: 0644]
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/host.lua.d/90-enable-all.lua [new file with mode: 0644]
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/policy.conf [new file with mode: 0644]
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/policy.lua.d/10-default-policy.lua [moved from meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/10-default-policy.lua with 64% similarity]
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/wireplumber.conf
meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl_git.bb

index 106de4f..6a40b5f 100644 (file)
@@ -7,9 +7,9 @@ SRC_URI += "\
 
 do_install:append() {
     # install smack-specific config
-    config_dir="${D}${sysconfdir}/wireplumber/config.lua.d/"
-    access_dir="${D}${sysconfdir}/wireplumber/scripts/access/"
-    mkdir -p ${access_dir}
+    config_dir="${D}${sysconfdir}/wireplumber/host.lua.d/"
+    access_dir="${D}${datadir}/wireplumber/scripts/access/"
+    install -d ${access_dir}
     install -m 0644 ${WORKDIR}/50-access-agl.lua ${config_dir}
     install -m 0644 ${WORKDIR}/access-smack.lua ${access_dir}
 }
diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/99-load-modules.lua b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/99-load-modules.lua
deleted file mode 100644 (file)
index 70251ae..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
--- Enable local & bluetooth audio devices
-alsa_monitor.enable()
-bluez_monitor.enable()
-
--- Load policy
-default_policy.enable()
-
--- Implements storing metadata about objects in RAM
-load_module("metadata")
-
--- Keeps track of the "default" sources and sinks
-load_module("default-nodes", {
-  -- do not store runtime user changes in $HOME
-  ["use-persistent-storage"] = false,
-})
-
--- Selects default routes on devices that advertise routes
-load_script("default-routes.lua", {
-  -- do not store runtime user changes in $HOME
-  ["use-persistent-storage"] = false,
-})
-
--- Automatically suspends idle nodes after 3 seconds
-load_script("suspend-node.lua")
-
--- Automatically sets device profiles to 'On'
-load_module("device-activation")
diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/alsa-suspend.lua b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/alsa-suspend.lua
new file mode 100644 (file)
index 0000000..55edd12
--- /dev/null
@@ -0,0 +1,45 @@
+-- WirePlumber
+--
+-- This script mutes all ALSA sinks when the "suspend.playback" metadata
+-- key is set to 1; compliments pipewire-ic-ipc and the respective support
+-- for handling "suspend.playback" in the policy scripts
+--
+-- Copyright © 2021 Collabora Ltd.
+--    @author George Kiagiadakis <george.kiagiadakis@collabora.com>
+--
+-- SPDX-License-Identifier: MIT
+
+mixer_api = Plugin.find("mixer-api")
+
+nodes_om = ObjectManager {
+  Interest { type = "node",
+    Constraint { "media.class", "matches", "Audio/Sink" },
+    Constraint { "object.path", "matches", "alsa:pcm:*" },
+  },
+}
+
+metadata_om = ObjectManager {
+  Interest { type = "metadata",
+    Constraint { "metadata.name", "=", "default" },
+  }
+}
+
+metadata_om:connect("object-added", function (om, metadata)
+  metadata:connect("changed", function (m, subject, key, t, value)
+    if key == "suspend.playback" then
+      local suspended = (value == "1")
+
+      Log.info(string.format("%s ALSA nodes for IC sound",
+                             suspended and "muting" or "unmuting"))
+
+      for n in nodes_om:iterate() do
+        mixer_api:call("set-volume", n["bound-id"], {
+          ["mute"] = suspended,
+        })
+      end
+    end
+  end)
+end)
+
+nodes_om:activate()
+metadata_om:activate()
diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/bluetooth.conf b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/bluetooth.conf
new file mode 100644 (file)
index 0000000..ebd0b60
--- /dev/null
@@ -0,0 +1,74 @@
+# WirePlumber daemon context configuration #
+
+context.properties = {
+  ## Properties to configure the PipeWire context and some modules
+
+  application.name = "WirePlumber Bluetooth"
+  log.level = 2
+  wireplumber.script-engine = lua-scripting
+  wireplumber.export-core = false
+
+  #mem.mlock-all = false
+  #support.dbus  = true
+}
+
+context.spa-libs = {
+  #<factory-name regex> = <library-name>
+  #
+  # Used to find spa factory names. It maps an spa factory name
+  # regular expression to a library name that should contain
+  # that factory.
+  #
+  api.bluez5.*    = bluez5/libspa-bluez5
+  audio.convert.* = audioconvert/libspa-audioconvert
+  support.*       = support/libspa-support
+}
+
+context.modules = [
+  #{   name = <module-name>
+  #    [ args = { <key> = <value> ... } ]
+  #    [ flags = [ [ ifexists ] [ nofail ] ]
+  #}
+  #
+  # PipeWire modules to load.
+  # If ifexists is given, the module is ignored when it is not found.
+  # If nofail is given, module initialization failures are ignored.
+  #
+
+  # The native communication protocol.
+  { name = libpipewire-module-protocol-native }
+
+  # Allows creating nodes that run in the context of the
+  # client. Is used by all clients that want to provide
+  # data to PipeWire.
+  { name = libpipewire-module-client-node }
+
+  # Allows creating devices that run in the context of the
+  # client. Is used by the session manager.
+  { name = libpipewire-module-client-device }
+
+  # Makes a factory for wrapping nodes in an adapter with a
+  # converter and resampler.
+  { name = libpipewire-module-adapter }
+
+  # Allows applications to create metadata objects. It creates
+  # a factory for Metadata objects.
+  { name = libpipewire-module-metadata }
+
+  # Provides factories to make session manager objects.
+  { name = libpipewire-module-session-manager }
+]
+
+wireplumber.components = [
+  #{ name = <component-name>, type = <component-type> }
+  #
+  # WirePlumber components to load
+  #
+
+  # The lua scripting engine
+  { name = libwireplumber-module-lua-scripting, type = module }
+
+  # The lua configuration file
+  # Other components are loaded from there
+  { name = bluetooth.lua, type = config/lua }
+]
@@ -101,9 +101,7 @@ bluez_monitor.rules = {
   },
 }
 
-function bluez_monitor.enable()
-  load_monitor("bluez", {
-    properties = bluez_monitor.properties,
-    rules = bluez_monitor.rules,
-  })
-end
+load_monitor("bluez", {
+  properties = bluez_monitor.properties,
+  rules = bluez_monitor.rules,
+})
diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/host.lua.d/30-v4l2-monitor.lua b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/host.lua.d/30-v4l2-monitor.lua
new file mode 100644 (file)
index 0000000..a4eb58a
--- /dev/null
@@ -0,0 +1,48 @@
+-- V4L2 monitor config file --
+
+v4l2_monitor = {}
+v4l2_monitor.properties = {}
+
+v4l2_monitor.rules = {
+  -- An array of matches/actions to evaluate.
+  {
+    -- Rules for matching a device or node. It is an array of
+    -- properties that all need to match the regexp. If any of the
+    -- matches work, the actions are executed for the object.
+    matches = {
+      {
+        -- This matches all cards.
+        { "device.name", "matches", "v4l2_device.*" },
+      },
+    },
+    -- Apply properties on the matched object.
+    apply_properties = {
+      -- ["device.nick"] = "My Device",
+    },
+  },
+  {
+    matches = {
+      {
+        -- Matches all sources.
+        { "node.name", "matches", "v4l2_input.*" },
+      },
+      {
+        -- Matches all sinks.
+        { "node.name", "matches", "v4l2_output.*" },
+      },
+    },
+    apply_properties = {
+      --["node.nick"] = "My Node",
+      --["priority.driver"] = 100,
+      --["priority.session"] = 100,
+      --["node.pause-on-idle"] = false,
+    },
+  },
+}
+
+function v4l2_monitor.enable()
+  load_monitor("v4l2", {
+    properties = v4l2_monitor.properties,
+    rules = v4l2_monitor.rules,
+  })
+end
diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/host.lua.d/40-device-defaults.lua b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/host.lua.d/40-device-defaults.lua
new file mode 100644 (file)
index 0000000..365bab5
--- /dev/null
@@ -0,0 +1,26 @@
+device_defaults = {}
+
+device_defaults.properties = {
+  -- store preferences to the file system and restore them at startup;
+  -- when set to false, default nodes and routes are selected based on
+  -- their priorities and any runtime changes do not persist after restart
+  ["use-persistent-storage"] = false,
+}
+
+function device_defaults.enable()
+  -- Selects appropriate default nodes and enables saving and restoring them
+  load_module("default-nodes", device_defaults.properties)
+
+  -- Selects appropriate default routes ("ports" in pulseaudio terminology)
+  -- and enables saving and restoring them together with
+  -- their properties (per-route/port volume levels, channel maps, etc)
+  load_script("default-routes.lua", device_defaults.properties)
+
+  if device_defaults.properties["use-persistent-storage"] then
+    -- Enables functionality to save and restore default device profiles
+    load_module("default-profile")
+
+    -- Save and restore stream-specific properties
+    load_script("restore-stream.lua")
+  end
+end
diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/host.lua.d/90-enable-all.lua b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/host.lua.d/90-enable-all.lua
new file mode 100644 (file)
index 0000000..f6d73a3
--- /dev/null
@@ -0,0 +1,20 @@
+-- Provide the "default" pw_metadata, which stores
+-- dynamic properties of pipewire objects in RAM
+load_module("metadata")
+
+-- Load devices
+alsa_monitor.enable()
+--v4l2_monitor.enable()
+
+-- Track/store/restore user choices about devices
+device_defaults.enable()
+
+-- Automatically suspends idle nodes after 3 seconds
+load_script("suspend-node.lua")
+
+-- Automatically sets device profiles to 'On'
+load_module("device-activation")
+
+-- Mute ALSA sinks when requested by pipewire-ic-ipc
+load_module("mixer-api")
+load_script("alsa-suspend.lua")
diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/policy.conf b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/policy.conf
new file mode 100644 (file)
index 0000000..42f7148
--- /dev/null
@@ -0,0 +1,73 @@
+# WirePlumber daemon context configuration #
+
+context.properties = {
+  ## Properties to configure the PipeWire context and some modules
+
+  application.name = "WirePlumber Policy"
+  log.level = 2
+  wireplumber.script-engine = lua-scripting
+  wireplumber.export-core = false
+
+  #mem.mlock-all = false
+  #support.dbus  = true
+}
+
+context.spa-libs = {
+  #<factory-name regex> = <library-name>
+  #
+  # Used to find spa factory names. It maps an spa factory name
+  # regular expression to a library name that should contain
+  # that factory.
+  #
+  audio.convert.* = audioconvert/libspa-audioconvert
+  support.*       = support/libspa-support
+}
+
+context.modules = [
+  #{   name = <module-name>
+  #    [ args = { <key> = <value> ... } ]
+  #    [ flags = [ [ ifexists ] [ nofail ] ]
+  #}
+  #
+  # PipeWire modules to load.
+  # If ifexists is given, the module is ignored when it is not found.
+  # If nofail is given, module initialization failures are ignored.
+  #
+
+  # The native communication protocol.
+  { name = libpipewire-module-protocol-native }
+
+  # Allows creating nodes that run in the context of the
+  # client. Is used by all clients that want to provide
+  # data to PipeWire.
+  { name = libpipewire-module-client-node }
+
+  # Allows creating devices that run in the context of the
+  # client. Is used by the session manager.
+  { name = libpipewire-module-client-device }
+
+  # Makes a factory for wrapping nodes in an adapter with a
+  # converter and resampler.
+  { name = libpipewire-module-adapter }
+
+  # Allows applications to create metadata objects. It creates
+  # a factory for Metadata objects.
+  { name = libpipewire-module-metadata }
+
+  # Provides factories to make session manager objects.
+  { name = libpipewire-module-session-manager }
+]
+
+wireplumber.components = [
+  #{ name = <component-name>, type = <component-type> }
+  #
+  # WirePlumber components to load
+  #
+
+  # The lua scripting engine
+  { name = libwireplumber-module-lua-scripting, type = module }
+
+  # The lua configuration file
+  # Other components are loaded from there
+  { name = policy.lua, type = config/lua }
+]
@@ -1,8 +1,8 @@
--- Default policy config file --
+-- Policy config file --
 
-default_policy = {}
+policy_config = {}
 
-default_policy.endpoints = {
+policy_config.endpoints = {
   -- [endpoint name] = { endpoint properties }
 
   ["endpoint.multimedia"] = {
@@ -39,12 +39,12 @@ default_policy.endpoints = {
   },
 }
 
-default_policy.policy = {
+policy_config.policy = {
   ["move"] = false,  -- moves session items when metadata target.node changes
   ["follow"] = true, -- moves session items to the default device when it has changed
 
   -- how much to lower the volume of lower priority streams when ducking
-  -- note that this is a linear volume modifier (not cubic as in the mixer)
+  -- note that this is a linear volume modifier (not cubic as in pulseaudio)
   ["duck.level"] = 0.2,
 
   ["roles"] = {
@@ -92,33 +92,31 @@ default_policy.policy = {
   },
 }
 
-function default_policy.enable()
-  -- Session item factories, building blocks for the session management graph
-  -- Do not disable these unless you really know what you are doing
-  load_module("si-node")
-  load_module("si-audio-adapter")
-  load_module("si-standard-link")
-  load_module("si-audio-endpoint")
+-- Session item factories, building blocks for the session management graph
+-- Do not disable these unless you really know what you are doing
+load_module("si-node")
+load_module("si-audio-adapter")
+load_module("si-standard-link")
+load_module("si-audio-endpoint")
 
-  -- API to access default nodes from scripts
-  load_module("default-nodes-api")
+-- API to access default nodes from scripts
+load_module("default-nodes-api")
 
-  -- API to access mixer controls, needed for volume ducking
-  load_module("mixer-api")
+-- API to access mixer controls, needed for volume ducking
+load_module("mixer-api")
 
-  -- Create endpoints statically at startup
-  load_script("static-endpoints.lua", default_policy.endpoints)
+-- Create endpoints statically at startup
+load_script("static-endpoints.lua", policy_config.endpoints)
 
-  -- Create session items for nodes that appear in the graph
-  load_script("create-item.lua")
+-- Create items for nodes that appear in the graph
+load_script("create-item.lua")
 
-  -- Link nodes to each other to make media flow in the graph
-  load_script("policy-node.lua", default_policy.policy)
+-- Link nodes to each other to make media flow in the graph
+load_script("policy-node.lua", policy_config.policy)
 
-  -- Link client nodes with endpoints to make media flow in the graph
-  load_script("policy-endpoint-client.lua", default_policy.policy)
-  load_script("policy-endpoint-client-links.lua", default_policy.policy)
+-- Link client nodes with endpoints to make media flow in the graph
+load_script("policy-endpoint-client.lua", policy_config.policy)
+load_script("policy-endpoint-client-links.lua", policy_config.policy)
 
-  -- Link endpoints with device nodes to make media flow in the graph
-  load_script("policy-endpoint-device.lua", default_policy.policy)
-end
+-- Link endpoints with device nodes to make media flow in the graph
+load_script("policy-endpoint-device.lua", policy_config.policy)
index 530393e..680a791 100644 (file)
@@ -70,5 +70,5 @@ wireplumber.components = [
 
   # The lua configuration file(s)
   # Other components are loaded from there
-  { name = config.lua, type = config/lua }
+  { name = host.lua, type = config/lua }
 ]
index 2b77df7..839a15f 100644 (file)
@@ -6,11 +6,13 @@ SECTION     = "multimedia"
 LICENSE = "MIT"
 LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
 SRC_URI = "\
+    file://bluetooth.lua.d/ \
+    file://host.lua.d/ \
+    file://policy.lua.d \
     file://00-functions.lua \
-    file://10-default-policy.lua \
-    file://30-alsa-monitor.lua \
-    file://30-bluez-monitor.lua \
-    file://99-load-modules.lua \
+    file://alsa-suspend.lua \
+    file://bluetooth.conf \
+    file://policy.conf \
     file://wireplumber.conf \
     file://wireplumber-bluetooth.conf \
 "
@@ -19,26 +21,51 @@ PACKAGE_ARCH = "${MACHINE_ARCH}"
 do_configure[noexec] = "1"
 do_compile[noexec] = "1"
 do_install:append() {
-    config_dir="${D}${sysconfdir}/wireplumber/config.lua.d/"
+    config_dir="${D}${sysconfdir}/wireplumber/"
+    scripts_dir="${D}${datadir}/wireplumber/scripts/"
     dbus_config_dir="${D}${sysconfdir}/dbus-1/system.d/"
+    systemd_dir="${D}${sysconfdir}/systemd/system/pipewire.service.wants/"
 
     install -d ${config_dir}
     install -m 0644 ${WORKDIR}/00-functions.lua ${config_dir}
-    install -m 0644 ${WORKDIR}/10-default-policy.lua ${config_dir}
-    install -m 0644 ${WORKDIR}/30-alsa-monitor.lua ${config_dir}
-    install -m 0644 ${WORKDIR}/30-bluez-monitor.lua ${config_dir}
-    install -m 0644 ${WORKDIR}/99-load-modules.lua ${config_dir}
 
-    install -m 0644 ${WORKDIR}/wireplumber.conf ${D}${sysconfdir}/wireplumber/
+    # config of the main (host) instance
+    install -d ${config_dir}/host.lua.d/
+    ln -s ../00-functions.lua ${config_dir}/host.lua.d/00-functions.lua
+    install -m 0644 ${WORKDIR}/host.lua.d/*.lua ${config_dir}/host.lua.d/
+    install -m 0644 ${WORKDIR}/wireplumber.conf ${config_dir}
 
+    # config of the policy instance
+    install -d ${config_dir}/policy.lua.d/
+    ln -s ../00-functions.lua ${config_dir}/policy.lua.d/00-functions.lua
+    install -m 0644 ${WORKDIR}/policy.lua.d/*.lua ${config_dir}/policy.lua.d/
+    install -m 0644 ${WORKDIR}/policy.conf ${config_dir}
+
+    # config of the bluetooth instance
+    install -d ${config_dir}/bluetooth.lua.d/
+    ln -s ../00-functions.lua ${config_dir}/bluetooth.lua.d/00-functions.lua
+    install -m 0644 ${WORKDIR}/bluetooth.lua.d/*.lua ${config_dir}/bluetooth.lua.d/
+    install -m 0644 ${WORKDIR}/bluetooth.conf ${config_dir}
+
+    # install the alsa-suspend script, loaded by the main instance
+    install -d ${scripts_dir}
+    install -m 0644 ${WORKDIR}/alsa-suspend.lua ${scripts_dir}
+
+    # install dbus daemon configuration
     install -d ${dbus_config_dir}
     install -m 0644 ${WORKDIR}/wireplumber-bluetooth.conf ${dbus_config_dir}
+
+    # enable additional systemd services
+    install -d ${systemd_dir}
+    ln -s ${systemd_system_unitdir}/wireplumber@.service ${systemd_dir}/wireplumber@policy.service
+    ln -s ${systemd_system_unitdir}/wireplumber@.service ${systemd_dir}/wireplumber@bluetooth.service
 }
 
 FILES:${PN} += "\
-    ${sysconfdir}/wireplumber/* \
+    ${sysconfdir}/* \
+    ${datadir}/wireplumber/* \
 "
 CONFFILES:${PN} += "\
-    ${sysconfdir}/wireplumber/* \
+    ${sysconfdir}/* \
 "
 RPROVIDES:${PN} += "virtual/wireplumber-config"