diff options
73 files changed, 1444 insertions, 2387 deletions
diff --git a/meta-agl-core/files/group b/meta-agl-core/files/group index c309f6a05..3391cb1d8 100644 --- a/meta-agl-core/files/group +++ b/meta-agl-core/files/group @@ -90,3 +90,4 @@ agl-passenger::1002: systemd-network::1005: systemd-resolve::1006: mosquitto::1007: +pipewire::1008: diff --git a/meta-agl-core/files/passwd b/meta-agl-core/files/passwd index ac70c0f9b..9673c1ac5 100644 --- a/meta-agl-core/files/passwd +++ b/meta-agl-core/files/passwd @@ -60,3 +60,4 @@ display::200:200::: systemd-network::1005:1005::: systemd-resolve::1006:1006::: mosquitto::1007:1007::: +pipewire::1008:1008::: diff --git a/meta-pipewire/conf/include/agl-pipewire.inc b/meta-pipewire/conf/include/agl-pipewire.inc index edd893115..7234633ff 100644 --- a/meta-pipewire/conf/include/agl-pipewire.inc +++ b/meta-pipewire/conf/include/agl-pipewire.inc @@ -1,3 +1,2 @@ DISTRO_FEATURES_append = " pipewire" -PREFERRED_RPROVIDER_virtual/pipewire-config = "pipewire-conf-agl" -PREFERRED_RPROVIDER_virtual/wireplumber-config = "wireplumber-board-config-agl" +PREFERRED_RPROVIDER_virtual/wireplumber-config = "wireplumber-config-agl" diff --git a/meta-pipewire/conf/layer.conf b/meta-pipewire/conf/layer.conf index 5fe2ee2fd..850a5922c 100644 --- a/meta-pipewire/conf/layer.conf +++ b/meta-pipewire/conf/layer.conf @@ -10,6 +10,7 @@ BBFILE_PATTERN_meta-pipewire = "^${LAYERDIR}/" BBFILE_PRIORITY_meta-pipewire = "71" LAYERSERIES_COMPAT_meta-pipewire = "dunfell" +LAYERDEPENDS_meta-pipewire = "openembedded-layer" BBFILES_DYNAMIC += " \ app-framework:${LAYERDIR}/dynamic-layers/meta-app-framework/*/*/*.bb \ diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-core/packagegroups/packagegroup-pipewire.bbappend b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-core/packagegroups/packagegroup-pipewire.bbappend index d87bd581e..92ebf837f 100644 --- a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-core/packagegroups/packagegroup-pipewire.bbappend +++ b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-core/packagegroups/packagegroup-pipewire.bbappend @@ -1,4 +1,3 @@ RDEPENDS_${PN} += " \ agl-service-audiomixer \ - bluez-alsa-pipewire \ " diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl/client.env b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl/client.env deleted file mode 100644 index 9b44cee01..000000000 --- a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl/client.env +++ /dev/null @@ -1,10 +0,0 @@ -# This file contains environment variables that will apply -# to pipewire clients started by the application framework - -# libpipewire by default tries to obtain real-time scheduling -# for the streaming thread. This is only useful on the desktop, disable here. -DISABLE_RTKIT=1 - -# Uncomment to enable libpipewire debug for clients -# 1=error, 2=warning, 3=info, 4=debug, 5=trace -#PIPEWIRE_DEBUG=4 diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in deleted file mode 100644 index 6c055bcff..000000000 --- a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in +++ /dev/null @@ -1,17 +0,0 @@ -# daemon config file for PipeWire version "0.2.9" -# distributed by Automotive Grade Linux - -add-spa-lib audio.convert* audioconvert/libspa-audioconvert -add-spa-lib api.alsa.* alsa/libspa-alsa -add-spa-lib api.v4l2.* v4l2/libspa-v4l2 -add-spa-lib api.bluez5.* bluez5/libspa-bluez5 - -load-module libpipewire-module-protocol-native -load-module libpipewire-module-spa-node-factory -load-module libpipewire-module-client-node -load-module libpipewire-module-client-device -load-module libpipewire-module-access same-sec-label-mode=1 -load-module libpipewire-module-adapter -load-module libpipewire-module-link-factory -load-module libpipewire-module-session-manager -exec /usr/bin/wireplumber diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl/server.env b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl/server.env deleted file mode 100644 index c74b941d6..000000000 --- a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl/server.env +++ /dev/null @@ -1,12 +0,0 @@ -# This file contains environment variables that will apply -# to the pipewire daemon as well as its session manager - -# Disable rtkit for wireplumber, which is also a client -DISABLE_RTKIT=1 - -# Uncomment to enable wireplumber debug -#G_MESSAGES_DEBUG=all - -# Uncomment to enable pipewire debug -# 1=error, 2=warning, 3=info, 4=debug, 5=trace -#PIPEWIRE_DEBUG=4 diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl_git.bb b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl_git.bb deleted file mode 100644 index a28c6534e..000000000 --- a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire-conf-agl_git.bb +++ /dev/null @@ -1,43 +0,0 @@ -SUMMARY = "AGL configuration file for pipewire" -HOMEPAGE = "https://pipewire.org" -BUGTRACKER = "https://jira.automotivelinux.org" -AUTHOR = "George Kiagiadakis <george.kiagiadakis@collabora.com>" -SECTION = "multimedia" - -LICENSE = "MIT" -LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" - -SRC_URI = " \ - file://pipewire.conf.in \ - file://client.env \ - file://server.env \ - " - -do_configure[noexec] = "1" -do_compile[noexec] = "1" - -do_install_append() { - # enable optional features in the config - BLUEZ5=${@bb.utils.contains('DISTRO_FEATURES', 'bluez5', '', '#', d)} - sed -e "s/#IF_BLUEZ5 /${BLUEZ5}/" ${WORKDIR}/pipewire.conf.in > ${WORKDIR}/pipewire.conf - - # install our custom config - install -d ${D}/${sysconfdir}/pipewire/ - install -m 0644 ${WORKDIR}/pipewire.conf ${D}${sysconfdir}/pipewire/pipewire.conf - - # install environment variable files - install -d ${D}/${sysconfdir}/afm/unit.env.d/ - install -m 0644 ${WORKDIR}/client.env ${D}/${sysconfdir}/afm/unit.env.d/pipewire - install -m 0644 ${WORKDIR}/server.env ${D}${sysconfdir}/pipewire/environment -} - -FILES_${PN} = "\ - ${sysconfdir}/pipewire/* \ - ${sysconfdir}/afm/unit.env.d/* \ -" -CONFFILES_${PN} += "\ - ${sysconfdir}/pipewire/* \ - ${sysconfdir}/afm/unit.env.d/* \ -" - -RPROVIDES_${PN} += "virtual/pipewire-config" diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/0001-modules-add-new-access-seclabel-module.patch b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/0001-modules-add-new-access-seclabel-module.patch new file mode 100644 index 000000000..7885dfa37 --- /dev/null +++ b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/0001-modules-add-new-access-seclabel-module.patch @@ -0,0 +1,265 @@ +From a949b090e9d4d11c300fb23b416a2cc69483962b Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis <george.kiagiadakis@collabora.com> +Date: Tue, 16 Feb 2021 17:26:20 +0200 +Subject: [PATCH] modules: add new access-seclabel module + +This module allows access control based on the security label +of the client. It is tailored for use with the semantics of SMACK + +Upstream-Status: Inappropriate [smack specific] +--- + src/modules/meson.build | 10 ++ + src/modules/module-access-seclabel.c | 220 +++++++++++++++++++++++++++ + 2 files changed, 230 insertions(+) + create mode 100644 src/modules/module-access-seclabel.c + +diff --git a/src/modules/meson.build b/src/modules/meson.build +index 8c9ccc85..234cff6b 100644 +--- a/src/modules/meson.build ++++ b/src/modules/meson.build +@@ -14,6 +14,16 @@ pipewire_module_access = shared_library('pipewire-module-access', [ 'module-acce + dependencies : [mathlib, dl_lib, pipewire_dep], + ) + ++pipewire_module_access_seclabel = shared_library('pipewire-module-access-seclabel', ++ [ 'module-access-seclabel.c' ], ++ c_args : pipewire_module_c_args, ++ include_directories : [configinc, spa_inc], ++ install : true, ++ install_dir : modules_install_dir, ++ install_rpath: modules_install_dir, ++ dependencies : [mathlib, dl_lib, pipewire_dep], ++) ++ + pipewire_module_profiler = shared_library('pipewire-module-profiler', + [ 'module-profiler.c', + 'module-profiler/protocol-native.c', ], +diff --git a/src/modules/module-access-seclabel.c b/src/modules/module-access-seclabel.c +new file mode 100644 +index 00000000..3739f2e4 +--- /dev/null ++++ b/src/modules/module-access-seclabel.c +@@ -0,0 +1,220 @@ ++/* PipeWire ++ * ++ * Copyright © 2018 Wim Taymans ++ * Copyright © 2021 Collabora Ltd. ++ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++ ++#include <string.h> ++#include <stdio.h> ++#include <errno.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <sys/vfs.h> ++#include <fcntl.h> ++#include <unistd.h> ++ ++#include "config.h" ++ ++#include <spa/utils/result.h> ++#include <spa/utils/json.h> ++ ++#include <pipewire/impl.h> ++#include <pipewire/private.h> ++ ++#define NAME "access-seclabel" ++ ++#define MODULE_USAGE "[ seclabel.allowed=<cmd-line> ] " \ ++ "[ seclabel.rejected=<cmd-line> ] " \ ++ "[ seclabel.restricted=<cmd-line> ] " \ ++ ++static const struct spa_dict_item module_props[] = { ++ { PW_KEY_MODULE_AUTHOR, "George Kiagiadakis <george.kiagiadakis@collabora.com>" }, ++ { PW_KEY_MODULE_DESCRIPTION, "Perform access check based on the security label" }, ++ { PW_KEY_MODULE_USAGE, MODULE_USAGE }, ++ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, ++}; ++ ++struct impl { ++ struct pw_context *context; ++ struct pw_properties *properties; ++ ++ struct spa_hook context_listener; ++ struct spa_hook module_listener; ++}; ++ ++static int check_label(const char *label, const char *str) ++{ ++ char key[1024]; ++ int res = 0; ++ struct spa_json it[2]; ++ ++ spa_json_init(&it[0], str, strlen(str)); ++ if ((res = spa_json_enter_array(&it[0], &it[1])) <= 0) ++ goto exit; ++ ++ res = 0; ++ while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) { ++ if (strcmp(label, key) == 0) { ++ res = 1; ++ break; ++ } ++ } ++exit: ++ return res; ++} ++ ++static void ++context_check_access(void *data, struct pw_impl_client *client) ++{ ++ struct impl *impl = data; ++ struct pw_permission permissions[1]; ++ struct spa_dict_item items[2]; ++ const struct pw_properties *props; ++ const char *str, *access, *label = NULL; ++ int res; ++ ++ if ((props = pw_impl_client_get_properties(client)) != NULL) { ++ if ((str = pw_properties_get(props, PW_KEY_ACCESS)) != NULL) { ++ pw_log_info(NAME " client %p: has already access: '%s'", client, str); ++ return; ++ } ++ label = pw_properties_get(props, PW_KEY_SEC_LABEL); ++ } ++ ++ if (!label) { ++ pw_log_info(NAME " client %p: has no security label", client); ++ return; ++ } ++ ++ if (impl->properties && (str = pw_properties_get(impl->properties, "seclabel.allowed")) != NULL) { ++ res = check_label(label, str); ++ if (res < 0) { ++ pw_log_warn(NAME" %p: client %p allowed check failed: %s", ++ impl, client, spa_strerror(res)); ++ } else if (res > 0) { ++ access = "allowed"; ++ goto granted; ++ } ++ } ++ ++ if (impl->properties && (str = pw_properties_get(impl->properties, "seclabel.rejected")) != NULL) { ++ res = check_label(label, str); ++ if (res < 0) { ++ pw_log_warn(NAME" %p: client %p rejected check failed: %s", ++ impl, client, spa_strerror(res)); ++ } else if (res > 0) { ++ res = -EACCES; ++ access = "rejected"; ++ goto rejected; ++ } ++ } ++ ++ if (impl->properties && (str = pw_properties_get(impl->properties, "seclabel.restricted")) != NULL) { ++ res = check_label(label, str); ++ if (res < 0) { ++ pw_log_warn(NAME" %p: client %p restricted check failed: %s", ++ impl, client, spa_strerror(res)); ++ } ++ else if (res > 0) { ++ pw_log_debug(NAME" %p: restricted client %p added", impl, client); ++ access = "restricted"; ++ goto wait_permissions; ++ } ++ } ++ ++ return; ++ ++granted: ++ pw_log_info(NAME" %p: client %p '%s' access granted", impl, client, access); ++ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_ACCESS, access); ++ pw_impl_client_update_properties(client, &SPA_DICT_INIT(items, 1)); ++ ++ permissions[0] = PW_PERMISSION_INIT(PW_ID_ANY, PW_PERM_ALL); ++ pw_impl_client_update_permissions(client, 1, permissions); ++ return; ++ ++wait_permissions: ++ pw_log_info(NAME " %p: client %p wait for '%s' permissions", ++ impl, client, access); ++ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_ACCESS, access); ++ pw_impl_client_update_properties(client, &SPA_DICT_INIT(items, 1)); ++ return; ++ ++rejected: ++ pw_resource_error(pw_impl_client_get_core_resource(client), res, access); ++ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_ACCESS, access); ++ pw_impl_client_update_properties(client, &SPA_DICT_INIT(items, 1)); ++ return; ++} ++ ++static const struct pw_context_events context_events = { ++ PW_VERSION_CONTEXT_EVENTS, ++ .check_access = context_check_access, ++}; ++ ++static void module_destroy(void *data) ++{ ++ struct impl *impl = data; ++ ++ spa_hook_remove(&impl->context_listener); ++ spa_hook_remove(&impl->module_listener); ++ ++ if (impl->properties) ++ pw_properties_free(impl->properties); ++ ++ free(impl); ++} ++ ++static const struct pw_impl_module_events module_events = { ++ PW_VERSION_IMPL_MODULE_EVENTS, ++ .destroy = module_destroy, ++}; ++ ++SPA_EXPORT ++int pipewire__module_init(struct pw_impl_module *module, const char *args) ++{ ++ struct pw_context *context = pw_impl_module_get_context(module); ++ struct pw_properties *props; ++ struct impl *impl; ++ ++ impl = calloc(1, sizeof(struct impl)); ++ if (impl == NULL) ++ return -errno; ++ ++ pw_log_debug(NAME" module %p: new %s", impl, args); ++ ++ if (args) ++ props = pw_properties_new_string(args); ++ else ++ props = NULL; ++ ++ impl->context = context; ++ impl->properties = props; ++ ++ pw_context_add_listener(context, &impl->context_listener, &context_events, impl); ++ pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl); ++ ++ pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props)); ++ ++ return 0; ++} +-- +2.30.0 + diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire.conf b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire.conf new file mode 100644 index 000000000..bc0c89ac0 --- /dev/null +++ b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire.conf @@ -0,0 +1,59 @@ +context.properties = { + core.daemon = true + core.name = pipewire-0 + support.dbus = false + link.max-buffers = 16 + + # 1=error, 2=warning, 3=info, 4=debug, 5=trace + log.level = 2 + + ## Properties for the DSP configuration. + default.clock.rate = 48000 + default.clock.quantum = 1024 + default.clock.min-quantum = 512 + default.clock.max-quantum = 8192 +} + +context.spa-libs = { + audio.convert.* = audioconvert/libspa-audioconvert + api.alsa.* = alsa/libspa-alsa + api.v4l2.* = v4l2/libspa-v4l2 + support.* = support/libspa-support +} + +context.modules = [ + { name = libpipewire-module-protocol-native } + { name = libpipewire-module-metadata } + { name = libpipewire-module-spa-device-factory } + { name = libpipewire-module-spa-node-factory } + { name = libpipewire-module-client-node } + { name = libpipewire-module-client-device } + { name = libpipewire-module-adapter } + { name = libpipewire-module-link-factory } + { name = libpipewire-module-session-manager } + + # allow clients with the "System" SMACK label + # such a client is also the session manager (wireplumber) + { + name = libpipewire-module-access-seclabel + args= { + seclabel.allowed = [ System ] + } + } + + # and restrict all other clients + { + name = libpipewire-module-access + args= { + access.force = restricted + } + } + + # The profile module. Allows application to access profiler + # and performance data. It provides an interface that is used + # by pw-top and pw-profiler. + #{ name = libpipewire-module-profiler } +] + +context.objects = {} +context.exec = {} diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire.service b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire.service new file mode 100644 index 000000000..b37fe2551 --- /dev/null +++ b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire.service @@ -0,0 +1,24 @@ +[Unit] +Description=Multimedia Service +Requires=pipewire.socket + +[Service] +LockPersonality=yes +MemoryDenyWriteExecute=yes +NoNewPrivileges=yes +RestrictNamespaces=yes +SystemCallArchitectures=native +SystemCallFilter=@system-service +Type=simple +ExecStart=/usr/bin/pipewire +Restart=on-failure +RuntimeDirectory=pipewire +RuntimeDirectoryPreserve=yes +User=pipewire +Environment=PIPEWIRE_RUNTIME_DIR=%t/pipewire +SmackProcessLabel=System::Pipewire +UMask=0077 + +[Install] +Also=pipewire.socket +WantedBy=default.target diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire.socket b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire.socket new file mode 100644 index 000000000..a83435be4 --- /dev/null +++ b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire.socket @@ -0,0 +1,16 @@ +[Unit] +Description=Multimedia System + +[Socket] +Priority=6 +Backlog=5 +ListenStream=%t/pipewire/pipewire-0 +SocketUser=pipewire +SocketGroup=pipewire +SocketMode=0666 +SmackLabel=* +SmackLabelIPIn=System +SmackLabelIPOut=System + +[Install] +WantedBy=sockets.target diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire@.service b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire@.service deleted file mode 100644 index e116dc1fa..000000000 --- a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire@.service +++ /dev/null @@ -1,24 +0,0 @@ -[Unit] -Description=Multimedia Service for user %i -Requires=pipewire@%i.socket - -[Install] -Also=pipewire@%i.socket - -[Service] -Type=simple -Restart=on-failure -ExecStart=/usr/bin/pipewire - -Environment=XDG_RUNTIME_DIR=/run/user/%i -Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%i/bus -EnvironmentFile=-/etc/pipewire/environment - -User=%i -Slice=user-%i.slice -SmackProcessLabel=System::Pipewire -SupplementaryGroups=audio -UMask=0077 -CapabilityBoundingSet= -SystemCallFilter=@basic-io @file-system @io-event @ipc \ - @memlock @network-io @process @resources @signal diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire@.socket b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire@.socket deleted file mode 100644 index 10cb32276..000000000 --- a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire/pipewire@.socket +++ /dev/null @@ -1,19 +0,0 @@ -[Unit] -Description=Multimedia Service socket for user %i -Requires=afm-user-setup@%i.service -After=afm-user-setup@%i.service - -[Socket] -Priority=6 -Backlog=5 -ListenStream=/run/user/%i/pipewire-0 -Service=pipewire@%i.service -SmackLabel=* -SmackLabelIPIn=System -SmackLabelIPOut=System -SocketUser=%i -SocketGroup=%i -SocketMode=0660 - -[Install] -WantedBy=afm-user-session@%i.target diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire_0.3.27.bbappend b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire_0.3.27.bbappend new file mode 100644 index 000000000..d5e52de98 --- /dev/null +++ b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire_0.3.27.bbappend @@ -0,0 +1,33 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/pipewire:" + +SRC_URI_append= "\ + file://0001-modules-add-new-access-seclabel-module.patch \ + file://pipewire.conf \ + file://pipewire.service \ + file://pipewire.socket \ + file://smack-pipewire \ +" + +do_install_append() { + # replace the original config with our smack-aware config + rm -f ${D}${sysconfdir}/pipewire/pipewire.conf + install -m 0644 ${WORKDIR}/pipewire.conf ${D}${sysconfdir}/pipewire/pipewire.conf + + if ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'true', 'false', d)}; then + # remove the original unit files shipped by pipewire + rm -rf ${D}${systemd_system_unitdir}/pipewire.* + + # install our own system-level templates + mkdir -p ${D}${systemd_system_unitdir}/ + install -m 0644 ${WORKDIR}/pipewire.service ${D}${systemd_system_unitdir}/pipewire.service + install -m 0644 ${WORKDIR}/pipewire.socket ${D}${systemd_system_unitdir}/pipewire.socket + + # install smack rules + mkdir -p ${D}${sysconfdir}/smack/accesses.d + install -m 0644 ${WORKDIR}/smack-pipewire ${D}${sysconfdir}/smack/accesses.d/pipewire + fi +} + +FILES_${PN}_append = "\ + ${sysconfdir}/smack/accesses.d/* \ +" diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire_git.bbappend b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire_git.bbappend deleted file mode 100644 index 8c9abf23e..000000000 --- a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/pipewire/pipewire_git.bbappend +++ /dev/null @@ -1,32 +0,0 @@ -FILESEXTRAPATHS_prepend := "${THISDIR}/pipewire:" - -SRC_URI_append= "\ - file://pipewire@.service \ - file://pipewire@.socket \ - file://smack-pipewire \ - " - -do_install_append() { - if ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'true', 'false', d)}; then - # remove the original user unit files shipped by pipewire - rm -rf ${D}${systemd_unitdir} - - # install our own system-level templates - mkdir -p ${D}${systemd_system_unitdir}/ - install -m 0644 ${WORKDIR}/pipewire@.service ${D}${systemd_system_unitdir}/pipewire@.service - install -m 0644 ${WORKDIR}/pipewire@.socket ${D}${systemd_system_unitdir}/pipewire@.socket - - # enable the socket to start together with afm-user-session - mkdir -p ${D}${systemd_system_unitdir}/afm-user-session@.target.wants - ln -sf ../pipewire@.socket ${D}${systemd_system_unitdir}/afm-user-session@.target.wants/pipewire@.socket - - # install smack rules - mkdir -p ${D}${sysconfdir}/smack/accesses.d - install -m 0644 ${WORKDIR}/smack-pipewire ${D}${sysconfdir}/smack/accesses.d/pipewire - fi -} - -FILES_${PN}_append = "\ - ${systemd_system_unitdir}/* \ - ${sysconfdir}/smack/accesses.d/* \ -" diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/wireplumber/wireplumber-config-agl/50-access-agl.lua b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/wireplumber/wireplumber-config-agl/50-access-agl.lua new file mode 100644 index 000000000..10b3d7ae3 --- /dev/null +++ b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/wireplumber/wireplumber-config-agl/50-access-agl.lua @@ -0,0 +1 @@ +load_access("smack") diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/wireplumber/wireplumber-config-agl/access-smack.lua b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/wireplumber/wireplumber-config-agl/access-smack.lua new file mode 100644 index 000000000..a662a0f20 --- /dev/null +++ b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/wireplumber/wireplumber-config-agl/access-smack.lua @@ -0,0 +1,17 @@ +clients_om = ObjectManager { + Interest { + type = "client", + Constraint { "pipewire.access", "=", "restricted" }, + } +} + +clients_om:connect("object-added", function (om, client) + local smack_label = client["global-properties"]["pipewire.sec.label"] + + if smack_label:match("^User::App::.+") then + -- FIXME: apps can work with less permissions + client:update_permissions { ["any"] = "all" } + end +end) + +clients_om:activate() diff --git a/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/wireplumber/wireplumber-config-agl_git.bbappend b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/wireplumber/wireplumber-config-agl_git.bbappend new file mode 100644 index 000000000..e94f67eff --- /dev/null +++ b/meta-pipewire/dynamic-layers/meta-app-framework/recipes-multimedia/wireplumber/wireplumber-config-agl_git.bbappend @@ -0,0 +1,15 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/wireplumber-config-agl:" + +SRC_URI += "\ + file://50-access-agl.lua \ + file://access-smack.lua \ +" + +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} + 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-core/packagegroups/packagegroup-pipewire.bb b/meta-pipewire/recipes-core/packagegroups/packagegroup-pipewire.bb index a20f8a4f7..f883dfa2c 100644 --- a/meta-pipewire/recipes-core/packagegroups/packagegroup-pipewire.bb +++ b/meta-pipewire/recipes-core/packagegroups/packagegroup-pipewire.bb @@ -1,6 +1,6 @@ SUMMARY = "PipeWire Media Server" DESCRIPTION = "The set of packages required to use PipeWire in AGL" -LICENSE = "MIT & LGPL-2.1" +LICENSE = "MIT" inherit packagegroup @@ -10,6 +10,10 @@ PACKAGES = "\ RDEPENDS_${PN} += "\ pipewire \ + pipewire-modules-meta \ + pipewire-spa-plugins-meta \ pipewire-alsa \ gstreamer1.0-pipewire \ + wireplumber \ + ${@bb.utils.contains('DISTRO_FEATURES', 'agl-devel', 'pipewire-tools pipewire-spa-tools', '', d)} \ " diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc b/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc deleted file mode 100644 index b3081ca43..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc +++ /dev/null @@ -1,120 +0,0 @@ -SUMMARY = "Multimedia processing server for Linux" -HOMEPAGE = "https://pipewire.org" -BUGTRACKER = "https://github.com/PipeWire/pipewire/issues" -AUTHOR = "Wim Taymans <wtaymans@redhat.com>" -SECTION = "multimedia" - -LICENSE = "MIT & LGPL-2.1" -LIC_FILES_CHKSUM = "\ - file://COPYING;beginline=3;md5=b3adc775ca6ee80056383a5ae814cc75 \ - file://pipewire-alsa/LICENSE;md5=fc178bcd425090939a8b634d1d6a9594 \ - file://pipewire-jack/LICENSE;md5=fc178bcd425090939a8b634d1d6a9594 \ - file://pipewire-pulseaudio/LICENSE;md5=fc178bcd425090939a8b634d1d6a9594 \ -" - -inherit meson pkgconfig systemd manpages - -DEPENDS = "dbus" - -PACKAGECONFIG ??= "\ - ${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)} \ - ${@bb.utils.filter('DISTRO_FEATURES', 'bluez5', d)} \ - alsa audioconvert audiomixer \ - pipewire-alsa \ - gstreamer \ -" - -GST_VER = "1.0" - -# systemd integration -PACKAGECONFIG[systemd] = "-Dsystemd=true,-Dsystemd=false,systemd" - -# SPA plugins -PACKAGECONFIG[alsa] = "-Dalsa=true,-Dalsa=false,udev alsa-lib" -PACKAGECONFIG[audioconvert] = "-Daudioconvert=true,-Daudioconvert=false," -PACKAGECONFIG[audiomixer] = "-Daudiomixer=true,-Daudiomixer=false," -PACKAGECONFIG[audiotestsrc] = "-Daudiotestsrc=true,-Daudiotestsrc=false, " -PACKAGECONFIG[bluez5] = "-Dbluez5=true,-Dbluez5=false,bluez5 sbc" -PACKAGECONFIG[jack] = "-Djack=true,-Djack=false,jack" -PACKAGECONFIG[v4l2] = "-Dv4l2=true,-Dv4l2=false,udev v4l-utils" -PACKAGECONFIG[videotestsrc] = "-Dvideotestsrc=true,-Dvideotestsrc=false, " -PACKAGECONFIG[vulkan] = "-Dvulkan=true,-Dvulkan=false,vulkan" - -# alsa plugin to redirect audio to pipewire -PACKAGECONFIG[pipewire-alsa] = "-Dpipewire-alsa=true,-Dpipewire-alsa=false,alsa-lib" -# pulseaudio drop-in replacement library -PACKAGECONFIG[pipewire-pulseaudio] = "-Dpipewire-pulseaudio=true,-Dpipewire-pulseaudio=false,pulseaudio glib-2.0" -# jack drop-in replacement library -PACKAGECONFIG[pipewire-jack] = "-Dpipewire-jack=true,-Dpipewire-jack=false,jack" - -# GStreamer plugins -PACKAGECONFIG[gstreamer] = "-Dgstreamer=true,-Dgstreamer=false,glib-2.0 gstreamer${GST_VER} gstreamer${GST_VER}-plugins-base" - -# man pages -PACKAGECONFIG[manpages] = "-Dman=true,-Dman=false,libxml-parser-perl-native" - -do_install_append() { - # only install the alsa config file if the alsa-lib plugin has been built - # this avoids creating the pipewire-alsa package when the pipewire-alsa - # feature is not enabled - if [ -d ${D}${libdir}/alsa-lib ] - then - mkdir -p ${D}${datadir}/alsa/alsa.conf.d - install -m 0644 ${S}/pipewire-alsa/conf/50-pipewire.conf ${D}${datadir}/alsa/alsa.conf.d/50-pipewire.conf - fi -} - -PACKAGES =+ "\ - ${PN}-spa-plugins \ - ${PN}-alsa \ - ${PN}-pulseaudio \ - ${PN}-jack \ - ${PN}-config \ - gstreamer${GST_VER}-${PN} \ - lib${PN} \ - lib${PN}-modules \ -" - -FILES_${PN} = "\ - ${bindir}/pipewire* \ - ${systemd_user_unitdir}/* \ -" - -FILES_lib${PN} = "\ - ${libdir}/libpipewire-*.so.* \ -" - -FILES_lib${PN}-modules = "\ - ${libdir}/pipewire-*/* \ -" - -FILES_${PN}-spa-plugins = "\ - ${bindir}/spa-* \ - ${libdir}/spa/* \ -" - -FILES_${PN}-alsa = "\ - ${libdir}/alsa-lib/* \ - ${datadir}/alsa/alsa.conf.d/50-pipewire.conf \ -" - -FILES_${PN}-pulseaudio = "\ - ${libdir}/libpulse*.so.* \ -" - -FILES_gstreamer${GST_VER}-${PN} = "\ - ${libdir}/gstreamer-${GST_VER}/* \ -" - -RDEPENDS_lib${PN} += "lib${PN}-modules ${PN}-spa-plugins" - -# The default pipewire config. -# Replace in your own package using -# "virtual/pipewire-config" -FILES_${PN}-config = "\ - ${sysconfdir}/pipewire/pipewire.conf \ -" -CONFFILES_${PN}-config += "\ - ${sysconfdir}/pipewire/pipewire.conf \ -" -RPROVIDES_${PN}-config += "virtual/pipewire-config" diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-meson-revert-version-check-to-require-meson-0.47-not.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-meson-revert-version-check-to-require-meson-0.47-not.patch deleted file mode 100644 index 4e7bb0d4f..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-meson-revert-version-check-to-require-meson-0.47-not.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 5a249321aa84cd74e3d83bcd555c85fba3cd682d Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Sun, 22 Sep 2019 17:59:19 +0300 -Subject: [PATCH] meson: revert version check to require meson 0.47, not 0.50 - -meson 0.50 is not really needed, but there are some strange warnings -if you require an older version; in any case, AGL does not have 0.50 -yet, so let's not fail compilation because of that... - -Upstream-Status: Inappropriate [workaround] ---- - meson.build | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/meson.build b/meson.build -index 2734b0d2..c9da6b4d 100644 ---- a/meson.build -+++ b/meson.build -@@ -1,7 +1,7 @@ - project('pipewire', ['c' ], - version : '0.2.9', - license : 'MIT', -- meson_version : '>= 0.50.0', -+ meson_version : '>= 0.47.0', - default_options : [ 'warning_level=1', - 'c_std=gnu99', - 'buildtype=debugoptimized' ]) --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-pipewiresink-use-all-the-available-dest-memory-when-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-pipewiresink-use-all-the-available-dest-memory-when-.patch new file mode 100644 index 000000000..a3bde14bc --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-pipewiresink-use-all-the-available-dest-memory-when-.patch @@ -0,0 +1,33 @@ +From b86e5cabfae3ab354f350f8f7589b21a153a8a5d Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis <george.kiagiadakis@collabora.com> +Date: Mon, 10 May 2021 17:12:12 +0300 +Subject: pipewiresink: use all the available dest memory when copying buffer + +When pipewiresink needs to copy data, it has to resize the destination +buffer (to a smaller size) in order to send the correct data size to +pipewire. When this dest buffer is reused later, it will still have +this smaller size as its total size and the copy may discard data +from upstream if the new upstream buffer is bigger than the last one +that was copied on the same dest buffer. + +Upstream-Status: Backport [from master/0.3.28] +--- + src/gst/gstpipewiresink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/gst/gstpipewiresink.c b/src/gst/gstpipewiresink.c +index 966b12c7..031b3ae0 100644 +--- a/src/gst/gstpipewiresink.c ++++ b/src/gst/gstpipewiresink.c +@@ -616,7 +616,7 @@ gst_pipewire_sink_render (GstBaseSink * bsink, GstBuffer * buffer) + goto done; + + gst_buffer_map (b, &info, GST_MAP_WRITE); +- gst_buffer_extract (buffer, 0, info.data, info.size); ++ gst_buffer_extract (buffer, 0, info.data, info.maxsize); + gst_buffer_unmap (b, &info); + gst_buffer_resize (b, 0, gst_buffer_get_size (buffer)); + buffer = b; +-- +2.30.2 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch deleted file mode 100644 index 2077af63d..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch +++ /dev/null @@ -1,30 +0,0 @@ -From e4b81946baf2d8c08de87088c01a1d87ae4f03d9 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Mon, 24 Jun 2019 12:19:20 +0300 -Subject: [PATCH] arm: build with -mno-unaligned-access - -Upstream-Status: Inappropriate [workaround] -See also https://gitlab.freedesktop.org/pipewire/pipewire/issues/161 ---- - meson.build | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/meson.build b/meson.build -index c9da6b4d..5c121339 100644 ---- a/meson.build -+++ b/meson.build -@@ -52,6 +52,11 @@ if cc.get_id() == 'gcc' - language : 'c') - endif - -+if host_machine.cpu_family() == 'arm' -+ add_global_arguments('-mno-unaligned-access', -+ language: 'c') -+endif -+ - sse_args = '-msse' - sse2_args = '-msse2' - ssse3_args = '-mssse3' --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-pipewiresink-release-manually-acquired-buffers-back-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-pipewiresink-release-manually-acquired-buffers-back-.patch new file mode 100644 index 000000000..aeafae616 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-pipewiresink-release-manually-acquired-buffers-back-.patch @@ -0,0 +1,52 @@ +From 8911e22793ed3713ceb78be5414eeb03ebde962e Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis <george.kiagiadakis@collabora.com> +Date: Mon, 10 May 2021 17:25:57 +0300 +Subject: pipewiresink: release manually acquired buffers back to the pool + +When we manually acquire buffers from the pool, we never release them. +But because gst_buffer_pool_acquire_buffer() adds a reference to the pool, +this leaks the pool eventually. + +To fix this, just unref the buffer after it has been sent. This releases +it back to the pool and unrefs the pool. + +This has no significant effect to the stream, since the underlying +pw buffers are actually pooled in the pw_stream. It just prevents leaking. + +Upstream-Status: Backport [from master/0.3.28] +--- + src/gst/gstpipewiresink.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/gst/gstpipewiresink.c b/src/gst/gstpipewiresink.c +index 031b3ae0..2f79a4f7 100644 +--- a/src/gst/gstpipewiresink.c ++++ b/src/gst/gstpipewiresink.c +@@ -578,6 +578,7 @@ gst_pipewire_sink_render (GstBaseSink * bsink, GstBuffer * buffer) + GstPipeWireSink *pwsink; + GstFlowReturn res = GST_FLOW_OK; + const char *error = NULL; ++ gboolean unref_buffer = FALSE; + + pwsink = GST_PIPEWIRE_SINK (bsink); + +@@ -620,6 +621,7 @@ gst_pipewire_sink_render (GstBaseSink * bsink, GstBuffer * buffer) + gst_buffer_unmap (b, &info); + gst_buffer_resize (b, 0, gst_buffer_get_size (buffer)); + buffer = b; ++ unref_buffer = TRUE; + + pw_thread_loop_lock (pwsink->core->loop); + if (pw_stream_get_state (pwsink->stream, &error) != PW_STREAM_STATE_STREAMING) +@@ -628,6 +630,8 @@ gst_pipewire_sink_render (GstBaseSink * bsink, GstBuffer * buffer) + + GST_DEBUG ("push buffer"); + do_send_buffer (pwsink, buffer); ++ if (unref_buffer) ++ gst_buffer_unref (buffer); + + done_unlock: + pw_thread_loop_unlock (pwsink->core->loop); +-- +2.30.2 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch deleted file mode 100644 index b3eba21f7..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch +++ /dev/null @@ -1,1280 +0,0 @@ -From 1b1f884a165ed7b2147affbdddf85a641d4cf180 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Tue, 19 Feb 2019 18:23:19 +0200 -Subject: [PATCH] gst: Implement new pwaudio{src,sink} elements, based on - GstAudioBase{Src,Sink} - -These are much more reliable elements to use for audio data. -* GstAudioBaseSink provides a reliable clock implementation based - on the number of samples read/written -* on the pipewire side we make sure to dequeue, fill and enqueue - a single buffer inside the process() function, which avoids - underruns - -Both elements share a common ringbuffer that actually implements -the pipewire integration. - -Upstream-Status: Denied -See https://gitlab.freedesktop.org/pipewire/pipewire/merge_requests/140 ---- - src/gst/gstpipewire.c | 8 +- - src/gst/gstpwaudioringbuffer.c | 565 +++++++++++++++++++++++++++++++++ - src/gst/gstpwaudioringbuffer.h | 83 +++++ - src/gst/gstpwaudiosink.c | 207 ++++++++++++ - src/gst/gstpwaudiosink.h | 48 +++ - src/gst/gstpwaudiosrc.c | 200 ++++++++++++ - src/gst/gstpwaudiosrc.h | 48 +++ - src/gst/meson.build | 6 + - 8 files changed, 1164 insertions(+), 1 deletion(-) - create mode 100644 src/gst/gstpwaudioringbuffer.c - create mode 100644 src/gst/gstpwaudioringbuffer.h - create mode 100644 src/gst/gstpwaudiosink.c - create mode 100644 src/gst/gstpwaudiosink.h - create mode 100644 src/gst/gstpwaudiosrc.c - create mode 100644 src/gst/gstpwaudiosrc.h - -diff --git a/src/gst/gstpipewire.c b/src/gst/gstpipewire.c -index 4040264b..68fd446f 100644 ---- a/src/gst/gstpipewire.c -+++ b/src/gst/gstpipewire.c -@@ -40,6 +40,8 @@ - #include "gstpipewiresrc.h" - #include "gstpipewiresink.h" - #include "gstpipewiredeviceprovider.h" -+#include "gstpwaudiosrc.h" -+#include "gstpwaudiosink.h" - - GST_DEBUG_CATEGORY (pipewire_debug); - -@@ -52,12 +54,16 @@ plugin_init (GstPlugin *plugin) - GST_TYPE_PIPEWIRE_SRC); - gst_element_register (plugin, "pipewiresink", GST_RANK_NONE, - GST_TYPE_PIPEWIRE_SINK); -+ gst_element_register (plugin, "pwaudiosrc", GST_RANK_NONE, -+ GST_TYPE_PW_AUDIO_SRC); -+ gst_element_register (plugin, "pwaudiosink", GST_RANK_NONE, -+ GST_TYPE_PW_AUDIO_SINK); - - if (!gst_device_provider_register (plugin, "pipewiredeviceprovider", - GST_RANK_PRIMARY + 1, GST_TYPE_PIPEWIRE_DEVICE_PROVIDER)) - return FALSE; - -- GST_DEBUG_CATEGORY_INIT (pipewire_debug, "pipewire", 0, "PipeWirie elements"); -+ GST_DEBUG_CATEGORY_INIT (pipewire_debug, "pipewire", 0, "PipeWire elements"); - - return TRUE; - } -diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -new file mode 100644 -index 00000000..babf2d83 ---- /dev/null -+++ b/src/gst/gstpwaudioringbuffer.c -@@ -0,0 +1,565 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "gstpwaudioringbuffer.h" -+ -+#include <spa/param/audio/format-utils.h> -+#include <spa/pod/builder.h> -+ -+GST_DEBUG_CATEGORY_STATIC (pw_audio_ring_buffer_debug); -+#define GST_CAT_DEFAULT pw_audio_ring_buffer_debug -+ -+#define gst_pw_audio_ring_buffer_parent_class parent_class -+G_DEFINE_TYPE (GstPwAudioRingBuffer, gst_pw_audio_ring_buffer, GST_TYPE_AUDIO_RING_BUFFER); -+ -+enum -+{ -+ PROP_0, -+ PROP_ELEMENT, -+ PROP_DIRECTION, -+ PROP_PROPS -+}; -+ -+static void -+gst_pw_audio_ring_buffer_init (GstPwAudioRingBuffer * self) -+{ -+ self->loop = pw_loop_new (NULL); -+ self->main_loop = pw_thread_loop_new (self->loop, "pw-audioringbuffer-loop"); -+ self->core = pw_core_new (self->loop, NULL, 0); -+} -+ -+static void -+gst_pw_audio_ring_buffer_finalize (GObject * object) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (object); -+ -+ pw_core_destroy (self->core); -+ pw_thread_loop_destroy (self->main_loop); -+ pw_loop_destroy (self->loop); -+} -+ -+static void -+gst_pw_audio_ring_buffer_set_property (GObject * object, guint prop_id, -+ const GValue * value, GParamSpec * pspec) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (object); -+ -+ switch (prop_id) { -+ case PROP_ELEMENT: -+ self->elem = g_value_get_object (value); -+ break; -+ -+ case PROP_DIRECTION: -+ self->direction = g_value_get_int (value); -+ break; -+ -+ case PROP_PROPS: -+ self->props = g_value_get_pointer (value); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static void -+on_remote_state_changed (void *data, enum pw_remote_state old, -+ enum pw_remote_state state, const char *error) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); -+ -+ GST_DEBUG_OBJECT (self->elem, "got remote state %d", state); -+ -+ switch (state) { -+ case PW_REMOTE_STATE_UNCONNECTED: -+ case PW_REMOTE_STATE_CONNECTING: -+ case PW_REMOTE_STATE_CONNECTED: -+ break; -+ case PW_REMOTE_STATE_ERROR: -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("remote error: %s", error), (NULL)); -+ break; -+ } -+ pw_thread_loop_signal (self->main_loop, FALSE); -+} -+ -+static const struct pw_remote_events remote_events = { -+ PW_VERSION_REMOTE_EVENTS, -+ .state_changed = on_remote_state_changed, -+}; -+ -+static gboolean -+wait_for_remote_state (GstPwAudioRingBuffer *self, -+ enum pw_remote_state target) -+{ -+ while (TRUE) { -+ enum pw_remote_state state = pw_remote_get_state (self->remote, NULL); -+ if (state == target) -+ return TRUE; -+ if (state == PW_REMOTE_STATE_ERROR) -+ return FALSE; -+ pw_thread_loop_wait (self->main_loop); -+ } -+} -+ -+static gboolean -+gst_pw_audio_ring_buffer_open_device (GstAudioRingBuffer *buf) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf); -+ -+ GST_DEBUG_OBJECT (self->elem, "open device"); -+ -+ if (pw_thread_loop_start (self->main_loop) < 0) -+ goto mainloop_error; -+ -+ pw_thread_loop_lock (self->main_loop); -+ -+ self->remote = pw_remote_new (self->core, NULL, 0); -+ pw_remote_add_listener (self->remote, &self->remote_listener, &remote_events, -+ self); -+ -+ if (self->props->fd == -1) -+ pw_remote_connect (self->remote); -+ else -+ pw_remote_connect_fd (self->remote, self->props->fd); -+ -+ GST_DEBUG_OBJECT (self->elem, "waiting for connection"); -+ -+ if (!wait_for_remote_state (self, PW_REMOTE_STATE_CONNECTED)) -+ goto connect_error; -+ -+ pw_thread_loop_unlock (self->main_loop); -+ -+ return TRUE; -+ -+ /* ERRORS */ -+mainloop_error: -+ { -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("Failed to start mainloop"), (NULL)); -+ return FALSE; -+ } -+connect_error: -+ { -+ pw_thread_loop_unlock (self->main_loop); -+ return FALSE; -+ } -+} -+ -+static gboolean -+gst_pw_audio_ring_buffer_close_device (GstAudioRingBuffer *buf) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf); -+ -+ GST_DEBUG_OBJECT (self->elem, "closing device"); -+ -+ pw_thread_loop_lock (self->main_loop); -+ if (self->remote) { -+ pw_remote_disconnect (self->remote); -+ wait_for_remote_state (self, PW_REMOTE_STATE_UNCONNECTED); -+ } -+ pw_thread_loop_unlock (self->main_loop); -+ -+ pw_thread_loop_stop (self->main_loop); -+ -+ if (self->remote) { -+ pw_remote_destroy (self->remote); -+ self->remote = NULL; -+ } -+ return TRUE; -+} -+ -+static void -+on_stream_state_changed (void *data, enum pw_stream_state old, -+ enum pw_stream_state state, const char *error) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); -+ GstMessage *msg; -+ -+ GST_DEBUG_OBJECT (self->elem, "got stream state: %s", -+ pw_stream_state_as_string (state)); -+ -+ switch (state) { -+ case PW_STREAM_STATE_ERROR: -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("stream error: %s", error), (NULL)); -+ break; -+ case PW_STREAM_STATE_UNCONNECTED: -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("stream disconnected unexpectedly"), (NULL)); -+ break; -+ case PW_STREAM_STATE_CONNECTING: -+ break; -+ case PW_STREAM_STATE_PAUSED: -+ if (old == PW_STREAM_STATE_STREAMING) { -+ if (GST_STATE (self->elem) != GST_STATE_PAUSED && -+ GST_STATE_TARGET (self->elem) != GST_STATE_PAUSED) { -+ GST_DEBUG_OBJECT (self->elem, "requesting GST_STATE_PAUSED"); -+ msg = gst_message_new_request_state (GST_OBJECT (self->elem), -+ GST_STATE_PAUSED); -+ gst_element_post_message (self->elem, msg); -+ } -+ } -+ break; -+ case PW_STREAM_STATE_STREAMING: -+ if (GST_STATE (self->elem) != GST_STATE_PLAYING && -+ GST_STATE_TARGET (self->elem) != GST_STATE_PLAYING) { -+ GST_DEBUG_OBJECT (self->elem, "requesting GST_STATE_PLAYING"); -+ msg = gst_message_new_request_state (GST_OBJECT (self->elem), -+ GST_STATE_PLAYING); -+ gst_element_post_message (self->elem, msg); -+ } -+ break; -+ } -+ pw_thread_loop_signal (self->main_loop, FALSE); -+} -+ -+static gboolean -+wait_for_stream_state (GstPwAudioRingBuffer *self, -+ enum pw_stream_state target) -+{ -+ while (TRUE) { -+ enum pw_stream_state state = pw_stream_get_state (self->stream, NULL); -+ if (state >= target) -+ return TRUE; -+ if (state == PW_STREAM_STATE_ERROR || state == PW_STREAM_STATE_UNCONNECTED) -+ return FALSE; -+ pw_thread_loop_wait (self->main_loop); -+ } -+} -+ -+static void -+on_stream_param_changed (void *data, uint32_t id, const struct spa_pod *format) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); -+ const struct spa_pod *params[1]; -+ struct spa_pod_builder b = { NULL }; -+ uint8_t buffer[512]; -+ -+ if (format == NULL || id != SPA_PARAM_Format) -+ return; -+ -+ spa_pod_builder_init (&b, buffer, sizeof (buffer)); -+ params[0] = spa_pod_builder_add_object (&b, -+ SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, -+ SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(16, 1, INT32_MAX), -+ SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), -+ SPA_PARAM_BUFFERS_size, SPA_POD_Int(self->segsize), -+ SPA_PARAM_BUFFERS_stride, SPA_POD_Int(self->bpf), -+ SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); -+ -+ GST_DEBUG_OBJECT (self->elem, "doing finish format, buffer size:%d", self->segsize); -+ pw_stream_update_params (self->stream, params, 1); -+} -+ -+static void -+on_stream_process (void *data) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); -+ GstAudioRingBuffer *buf = GST_AUDIO_RING_BUFFER (data); -+ struct pw_buffer *b; -+ struct spa_data *d; -+ gint size; /*< size to read/write from/to the spa buffer */ -+ gint offset; /*< offset to read/write from/to in the spa buffer */ -+ gint segment; /*< the current segment number in the ringbuffer */ -+ guint8 *ringptr; /*< pointer to the beginning of the current segment */ -+ gint segsize; /*< the size of one segment in the ringbuffer */ -+ gint copy_size; /*< the bytes to copy in one memcpy() invocation */ -+ gint remain; /*< remainder of bytes available in the spa buffer */ -+ -+ if (g_atomic_int_get (&buf->state) != GST_AUDIO_RING_BUFFER_STATE_STARTED) { -+ GST_LOG_OBJECT (self->elem, "ring buffer is not started"); -+ return; -+ } -+ -+ b = pw_stream_dequeue_buffer (self->stream); -+ if (!b) { -+ GST_INFO_OBJECT (self->elem, "no pipewire buffer available"); -+ return; -+ } -+ -+ d = &b->buffer->datas[0]; -+ -+ if (self->direction == PW_DIRECTION_OUTPUT) { -+ /* in output mode, always fill the entire spa buffer */ -+ offset = d->chunk->offset = 0; -+ size = d->chunk->size = d->maxsize; -+ b->size = size / self->bpf; -+ } else { -+ offset = SPA_MIN (d->chunk->offset, d->maxsize); -+ size = SPA_MIN (d->chunk->size, d->maxsize - offset); -+ } -+ -+ do { -+ gst_audio_ring_buffer_prepare_read (buf, &segment, &ringptr, &segsize); -+ -+ /* in INPUT (src) mode, it is possible that the skew algorithm -+ * advances the ringbuffer behind our back */ -+ if (self->segoffset > 0 && self->cur_segment != segment) -+ self->segoffset = 0; -+ -+ copy_size = SPA_MIN (size, segsize - self->segoffset); -+ -+ if (self->direction == PW_DIRECTION_OUTPUT) { -+ memcpy (((guint8*) d->data) + offset, ringptr + self->segoffset, -+ copy_size); -+ } else { -+ memcpy (ringptr + self->segoffset, ((guint8*) d->data) + offset, -+ copy_size); -+ } -+ -+ remain = size - (segsize - self->segoffset); -+ -+ GST_TRACE_OBJECT (self->elem, -+ "seg %d: %s %d bytes remained:%d offset:%d segoffset:%d", segment, -+ self->direction == PW_DIRECTION_INPUT ? "INPUT" : "OUTPUT", -+ copy_size, remain, offset, self->segoffset); -+ -+ if (remain >= 0) { -+ offset += (segsize - self->segoffset); -+ size = remain; -+ -+ /* write silence on the segment we just read */ -+ if (self->direction == PW_DIRECTION_OUTPUT) -+ gst_audio_ring_buffer_clear (buf, segment); -+ -+ /* notify that we have read a complete segment */ -+ gst_audio_ring_buffer_advance (buf, 1); -+ self->segoffset = 0; -+ } else { -+ self->segoffset += size; -+ self->cur_segment = segment; -+ } -+ } while (remain > 0); -+ -+ pw_stream_queue_buffer (self->stream, b); -+} -+ -+static const struct pw_stream_events stream_events = { -+ PW_VERSION_STREAM_EVENTS, -+ .state_changed = on_stream_state_changed, -+ .param_changed = on_stream_param_changed, -+ .process = on_stream_process, -+}; -+ -+static gboolean -+copy_properties (GQuark field_id, const GValue *value, gpointer user_data) -+{ -+ struct pw_properties *properties = user_data; -+ -+ if (G_VALUE_HOLDS_STRING (value)) -+ pw_properties_set (properties, -+ g_quark_to_string (field_id), -+ g_value_get_string (value)); -+ return TRUE; -+} -+ -+static gboolean -+gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, -+ GstAudioRingBufferSpec *spec) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf); -+ struct pw_properties *props; -+ struct spa_pod_builder b = { NULL }; -+ uint8_t buffer[512]; -+ const struct spa_pod *params[1]; -+ -+ g_return_val_if_fail (spec, FALSE); -+ g_return_val_if_fail (GST_AUDIO_INFO_IS_VALID (&spec->info), FALSE); -+ g_return_val_if_fail (!self->stream, TRUE); /* already acquired */ -+ -+ g_return_val_if_fail (spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW, FALSE); -+ g_return_val_if_fail (GST_AUDIO_INFO_IS_FLOAT (&spec->info), FALSE); -+ -+ GST_DEBUG_OBJECT (self->elem, "acquire"); -+ -+ /* construct param & props objects */ -+ -+ props = pw_properties_new (NULL, NULL); -+ if (self->props->properties) { -+ gst_structure_foreach (self->props->properties, copy_properties, props); -+ } -+ -+ spa_pod_builder_init (&b, buffer, sizeof (buffer)); -+ params[0] = spa_pod_builder_add_object (&b, -+ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, -+ SPA_FORMAT_mediaType, SPA_POD_Id (SPA_MEDIA_TYPE_audio), -+ SPA_FORMAT_mediaSubtype, SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw), -+ SPA_FORMAT_AUDIO_format, SPA_POD_Id (SPA_AUDIO_FORMAT_F32), -+ SPA_FORMAT_AUDIO_rate, SPA_POD_Int (GST_AUDIO_INFO_RATE (&spec->info)), -+ SPA_FORMAT_AUDIO_channels, SPA_POD_Int (GST_AUDIO_INFO_CHANNELS (&spec->info))); -+ -+ self->segsize = spec->segsize; -+ self->bpf = GST_AUDIO_INFO_BPF (&spec->info); -+ self->rate = GST_AUDIO_INFO_RATE (&spec->info); -+ self->segoffset = 0; -+ -+ pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", -+ self->segsize / self->bpf, self->rate); -+ GST_DEBUG_OBJECT (self->elem, "segsize:%u, bpf:%u, node.latency = %s", -+ self->segsize, self->bpf, pw_properties_get (props, PW_KEY_NODE_LATENCY)); -+ -+ /* connect stream */ -+ -+ pw_thread_loop_lock (self->main_loop); -+ -+ GST_DEBUG_OBJECT (self->elem, "creating stream"); -+ -+ self->stream = pw_stream_new (self->remote, self->props->client_name, props); -+ pw_stream_add_listener(self->stream, &self->stream_listener, &stream_events, -+ self); -+ -+ if (pw_stream_connect (self->stream, -+ self->direction, -+ self->props->path ? (uint32_t)atoi(self->props->path) : SPA_ID_INVALID, -+ PW_STREAM_FLAG_AUTOCONNECT | -+ PW_STREAM_FLAG_MAP_BUFFERS | -+ PW_STREAM_FLAG_RT_PROCESS, -+ params, 1) < 0) -+ goto start_error; -+ -+ GST_DEBUG_OBJECT (self->elem, "waiting for stream CONFIGURE"); -+ -+ if (!wait_for_stream_state (self, PW_STREAM_STATE_PAUSED)) -+ goto start_error; -+ -+ pw_thread_loop_unlock (self->main_loop); -+ -+ /* allocate the internal ringbuffer */ -+ -+ spec->seglatency = spec->segtotal + 1; -+ buf->size = spec->segtotal * spec->segsize; -+ buf->memory = g_malloc (buf->size); -+ -+ gst_audio_format_fill_silence (buf->spec.info.finfo, buf->memory, -+ buf->size); -+ -+ GST_DEBUG_OBJECT (self->elem, "acquire done"); -+ -+ return TRUE; -+ -+start_error: -+ { -+ GST_ERROR_OBJECT (self->elem, "could not start stream"); -+ pw_stream_destroy (self->stream); -+ self->stream = NULL; -+ pw_thread_loop_unlock (self->main_loop); -+ return FALSE; -+ } -+} -+ -+static gboolean -+gst_pw_audio_ring_buffer_release (GstAudioRingBuffer *buf) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf); -+ -+ GST_DEBUG_OBJECT (self->elem, "release"); -+ -+ pw_thread_loop_lock (self->main_loop); -+ if (self->stream) { -+ spa_hook_remove (&self->stream_listener); -+ pw_stream_disconnect (self->stream); -+ pw_stream_destroy (self->stream); -+ self->stream = NULL; -+ } -+ pw_thread_loop_unlock (self->main_loop); -+ -+ /* free the buffer */ -+ g_free (buf->memory); -+ buf->memory = NULL; -+ -+ return TRUE; -+} -+ -+static guint -+gst_pw_audio_ring_buffer_delay (GstAudioRingBuffer *buf) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf); -+ struct pw_time t; -+ -+ if (!self->stream || pw_stream_get_time (self->stream, &t) < 0) -+ return 0; -+ -+ if (self->direction == PW_DIRECTION_OUTPUT) { -+ /* on output streams, we set the pw_buffer.size in frames, -+ so no conversion is necessary */ -+ return t.queued; -+ } else { -+ /* on input streams, pw_buffer.size is set by pw_stream in ticks, -+ so we need to convert it to frames and also add segoffset, which -+ is the number of bytes we have read but not advertised yet, as -+ the segment is incomplete */ -+ if (t.rate.denom > 0) -+ return -+ gst_util_uint64_scale (t.queued, self->rate * t.rate.num, t.rate.denom) -+ + self->segoffset / self->bpf; -+ else -+ return self->segoffset / self->bpf; -+ } -+ -+ return 0; -+} -+ -+static void -+gst_pw_audio_ring_buffer_class_init (GstPwAudioRingBufferClass * klass) -+{ -+ GObjectClass *gobject_class; -+ GstAudioRingBufferClass *gstaudiorbuf_class; -+ -+ gobject_class = (GObjectClass *) klass; -+ gstaudiorbuf_class = (GstAudioRingBufferClass *) klass; -+ -+ gobject_class->finalize = gst_pw_audio_ring_buffer_finalize; -+ gobject_class->set_property = gst_pw_audio_ring_buffer_set_property; -+ -+ gstaudiorbuf_class->open_device = gst_pw_audio_ring_buffer_open_device; -+ gstaudiorbuf_class->acquire = gst_pw_audio_ring_buffer_acquire; -+ gstaudiorbuf_class->release = gst_pw_audio_ring_buffer_release; -+ gstaudiorbuf_class->close_device = gst_pw_audio_ring_buffer_close_device; -+ gstaudiorbuf_class->delay = gst_pw_audio_ring_buffer_delay; -+ -+ g_object_class_install_property (gobject_class, PROP_ELEMENT, -+ g_param_spec_object ("element", "Element", "The audio source or sink", -+ GST_TYPE_ELEMENT, -+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_DIRECTION, -+ g_param_spec_int ("direction", "Direction", "The stream direction", -+ PW_DIRECTION_INPUT, PW_DIRECTION_OUTPUT, PW_DIRECTION_INPUT, -+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_PROPS, -+ g_param_spec_pointer ("props", "Properties", "The properties struct", -+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); -+ -+ GST_DEBUG_CATEGORY_INIT (pw_audio_ring_buffer_debug, "pwaudioringbuffer", 0, -+ "PipeWire Audio Ring Buffer"); -+} -diff --git a/src/gst/gstpwaudioringbuffer.h b/src/gst/gstpwaudioringbuffer.h -new file mode 100644 -index 00000000..f47f668a ---- /dev/null -+++ b/src/gst/gstpwaudioringbuffer.h -@@ -0,0 +1,83 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifndef __GST_PW_AUDIO_RING_BUFFER_H__ -+#define __GST_PW_AUDIO_RING_BUFFER_H__ -+ -+#include <gst/gst.h> -+#include <gst/audio/audio.h> -+#include <pipewire/pipewire.h> -+ -+G_BEGIN_DECLS -+ -+#define GST_TYPE_PW_AUDIO_RING_BUFFER \ -+ (gst_pw_audio_ring_buffer_get_type ()) -+ -+G_DECLARE_FINAL_TYPE(GstPwAudioRingBuffer, gst_pw_audio_ring_buffer, -+ GST, PW_AUDIO_RING_BUFFER, GstAudioRingBuffer); -+ -+typedef struct _GstPwAudioRingBufferProps GstPwAudioRingBufferProps; -+ -+struct _GstPwAudioRingBuffer -+{ -+ GstAudioRingBuffer parent; -+ -+ /* properties */ -+ GstElement *elem; -+ enum pw_direction direction; -+ GstPwAudioRingBufferProps *props; -+ -+ /* internal */ -+ struct pw_loop *loop; -+ struct pw_thread_loop *main_loop; -+ -+ struct pw_core *core; -+ struct pw_remote *remote; -+ struct spa_hook remote_listener; -+ -+ struct pw_stream *stream; -+ struct spa_hook stream_listener; -+ -+ gint segsize; -+ gint bpf; -+ gint rate; -+ -+ /* on_stream_process() state */ -+ gint segoffset; -+ gint cur_segment; -+}; -+ -+struct _GstPwAudioRingBufferProps -+{ -+ gchar *path; -+ gchar *client_name; -+ GstStructure *properties; -+ int fd; -+}; -+ -+G_END_DECLS -+ -+#endif -diff --git a/src/gst/gstpwaudiosink.c b/src/gst/gstpwaudiosink.c -new file mode 100644 -index 00000000..069996c3 ---- /dev/null -+++ b/src/gst/gstpwaudiosink.c -@@ -0,0 +1,207 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "gstpwaudiosink.h" -+ -+GST_DEBUG_CATEGORY_STATIC (pw_audio_sink_debug); -+#define GST_CAT_DEFAULT pw_audio_sink_debug -+ -+G_DEFINE_TYPE (GstPwAudioSink, gst_pw_audio_sink, GST_TYPE_AUDIO_BASE_SINK); -+ -+enum -+{ -+ PROP_0, -+ PROP_PATH, -+ PROP_CLIENT_NAME, -+ PROP_STREAM_PROPERTIES, -+ PROP_FD -+}; -+ -+static GstStaticPadTemplate gst_pw_audio_sink_template = -+GST_STATIC_PAD_TEMPLATE ("sink", -+ GST_PAD_SINK, -+ GST_PAD_ALWAYS, -+ GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32)) -+ ", layout = (string)\"interleaved\"") -+); -+ -+ -+static void -+gst_pw_audio_sink_init (GstPwAudioSink * self) -+{ -+ self->props.fd = -1; -+ -+ /* Bump the default buffer size up to 21.3 ms, which is the default on most -+ * sound cards, in hope to match the alsa buffer size on the pipewire server. -+ * This may not always happen, but it still sounds better than the 10ms -+ * default latency. This is temporary until we have a better mechanism to -+ * select the appropriate latency */ -+ GST_AUDIO_BASE_SINK (self)->latency_time = 21333; -+} -+ -+static void -+gst_pw_audio_sink_finalize (GObject * object) -+{ -+ GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object); -+ -+ g_free (pwsink->props.path); -+ g_free (pwsink->props.client_name); -+ if (pwsink->props.properties) -+ gst_structure_free (pwsink->props.properties); -+} -+ -+static void -+gst_pw_audio_sink_set_property (GObject * object, guint prop_id, -+ const GValue * value, GParamSpec * pspec) -+{ -+ GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object); -+ -+ switch (prop_id) { -+ case PROP_PATH: -+ g_free (pwsink->props.path); -+ pwsink->props.path = g_value_dup_string (value); -+ break; -+ -+ case PROP_CLIENT_NAME: -+ g_free (pwsink->props.client_name); -+ pwsink->props.client_name = g_value_dup_string (value); -+ break; -+ -+ case PROP_STREAM_PROPERTIES: -+ if (pwsink->props.properties) -+ gst_structure_free (pwsink->props.properties); -+ pwsink->props.properties = -+ gst_structure_copy (gst_value_get_structure (value)); -+ break; -+ -+ case PROP_FD: -+ pwsink->props.fd = g_value_get_int (value); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static void -+gst_pw_audio_sink_get_property (GObject * object, guint prop_id, -+ GValue * value, GParamSpec * pspec) -+{ -+ GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object); -+ -+ switch (prop_id) { -+ case PROP_PATH: -+ g_value_set_string (value, pwsink->props.path); -+ break; -+ -+ case PROP_CLIENT_NAME: -+ g_value_set_string (value, pwsink->props.client_name); -+ break; -+ -+ case PROP_STREAM_PROPERTIES: -+ gst_value_set_structure (value, pwsink->props.properties); -+ break; -+ -+ case PROP_FD: -+ g_value_set_int (value, pwsink->props.fd); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static GstAudioRingBuffer * -+gst_pw_audio_sink_create_ringbuffer (GstAudioBaseSink * sink) -+{ -+ GstPwAudioSink *self = GST_PW_AUDIO_SINK (sink); -+ GstAudioRingBuffer *buffer; -+ -+ GST_DEBUG_OBJECT (sink, "creating ringbuffer"); -+ buffer = g_object_new (GST_TYPE_PW_AUDIO_RING_BUFFER, -+ "element", sink, -+ "direction", PW_DIRECTION_OUTPUT, -+ "props", &self->props, -+ NULL); -+ GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer); -+ -+ return buffer; -+} -+ -+static void -+gst_pw_audio_sink_class_init (GstPwAudioSinkClass * klass) -+{ -+ GObjectClass *gobject_class; -+ GstElementClass *gstelement_class; -+ GstAudioBaseSinkClass *gstaudiobsink_class; -+ -+ gobject_class = (GObjectClass *) klass; -+ gstelement_class = (GstElementClass *) klass; -+ gstaudiobsink_class = (GstAudioBaseSinkClass *) klass; -+ -+ gobject_class->finalize = gst_pw_audio_sink_finalize; -+ gobject_class->set_property = gst_pw_audio_sink_set_property; -+ gobject_class->get_property = gst_pw_audio_sink_get_property; -+ -+ gstaudiobsink_class->create_ringbuffer = gst_pw_audio_sink_create_ringbuffer; -+ -+ g_object_class_install_property (gobject_class, PROP_PATH, -+ g_param_spec_string ("path", "Path", -+ "The sink path to connect to (NULL = default)", NULL, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_CLIENT_NAME, -+ g_param_spec_string ("client-name", "Client Name", -+ "The client name to use (NULL = default)", NULL, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_STREAM_PROPERTIES, -+ g_param_spec_boxed ("stream-properties", "Stream properties", -+ "List of PipeWire stream properties", GST_TYPE_STRUCTURE, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_FD, -+ g_param_spec_int ("fd", "Fd", "The fd to connect with", -1, G_MAXINT, -1, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ gst_element_class_set_static_metadata (gstelement_class, -+ "PipeWire Audio sink", "Sink/Audio", -+ "Send audio to PipeWire", -+ "George Kiagiadakis <george.kiagiadakis@collabora.com>"); -+ -+ gst_element_class_add_pad_template (gstelement_class, -+ gst_static_pad_template_get (&gst_pw_audio_sink_template)); -+ -+ GST_DEBUG_CATEGORY_INIT (pw_audio_sink_debug, "pwaudiosink", 0, -+ "PipeWire Audio Sink"); -+} -+ -diff --git a/src/gst/gstpwaudiosink.h b/src/gst/gstpwaudiosink.h -new file mode 100644 -index 00000000..7ed0de7b ---- /dev/null -+++ b/src/gst/gstpwaudiosink.h -@@ -0,0 +1,48 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifndef __GST_PW_AUDIO_SINK_H__ -+#define __GST_PW_AUDIO_SINK_H__ -+ -+#include "gstpwaudioringbuffer.h" -+ -+G_BEGIN_DECLS -+ -+#define GST_TYPE_PW_AUDIO_SINK \ -+ (gst_pw_audio_sink_get_type ()) -+ -+G_DECLARE_FINAL_TYPE(GstPwAudioSink, gst_pw_audio_sink, -+ GST, PW_AUDIO_SINK, GstAudioBaseSink); -+ -+struct _GstPwAudioSink -+{ -+ GstAudioBaseSink parent; -+ GstPwAudioRingBufferProps props; -+}; -+ -+G_END_DECLS -+ -+#endif -diff --git a/src/gst/gstpwaudiosrc.c b/src/gst/gstpwaudiosrc.c -new file mode 100644 -index 00000000..6c522982 ---- /dev/null -+++ b/src/gst/gstpwaudiosrc.c -@@ -0,0 +1,200 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "gstpwaudiosrc.h" -+ -+GST_DEBUG_CATEGORY_STATIC (pw_audio_src_debug); -+#define GST_CAT_DEFAULT pw_audio_src_debug -+ -+G_DEFINE_TYPE (GstPwAudioSrc, gst_pw_audio_src, GST_TYPE_AUDIO_BASE_SRC); -+ -+enum -+{ -+ PROP_0, -+ PROP_PATH, -+ PROP_CLIENT_NAME, -+ PROP_STREAM_PROPERTIES, -+ PROP_FD -+}; -+ -+static GstStaticPadTemplate gst_pw_audio_src_template = -+GST_STATIC_PAD_TEMPLATE ("src", -+ GST_PAD_SRC, -+ GST_PAD_ALWAYS, -+ GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32)) -+ ", layout = (string)\"interleaved\"") -+); -+ -+ -+static void -+gst_pw_audio_src_init (GstPwAudioSrc * self) -+{ -+ self->props.fd = -1; -+} -+ -+static void -+gst_pw_audio_src_finalize (GObject * object) -+{ -+ GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object); -+ -+ g_free (self->props.path); -+ g_free (self->props.client_name); -+ if (self->props.properties) -+ gst_structure_free (self->props.properties); -+} -+ -+static void -+gst_pw_audio_src_set_property (GObject * object, guint prop_id, -+ const GValue * value, GParamSpec * pspec) -+{ -+ GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object); -+ -+ switch (prop_id) { -+ case PROP_PATH: -+ g_free (self->props.path); -+ self->props.path = g_value_dup_string (value); -+ break; -+ -+ case PROP_CLIENT_NAME: -+ g_free (self->props.client_name); -+ self->props.client_name = g_value_dup_string (value); -+ break; -+ -+ case PROP_STREAM_PROPERTIES: -+ if (self->props.properties) -+ gst_structure_free (self->props.properties); -+ self->props.properties = -+ gst_structure_copy (gst_value_get_structure (value)); -+ break; -+ -+ case PROP_FD: -+ self->props.fd = g_value_get_int (value); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static void -+gst_pw_audio_src_get_property (GObject * object, guint prop_id, -+ GValue * value, GParamSpec * pspec) -+{ -+ GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object); -+ -+ switch (prop_id) { -+ case PROP_PATH: -+ g_value_set_string (value, self->props.path); -+ break; -+ -+ case PROP_CLIENT_NAME: -+ g_value_set_string (value, self->props.client_name); -+ break; -+ -+ case PROP_STREAM_PROPERTIES: -+ gst_value_set_structure (value, self->props.properties); -+ break; -+ -+ case PROP_FD: -+ g_value_set_int (value, self->props.fd); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static GstAudioRingBuffer * -+gst_pw_audio_src_create_ringbuffer (GstAudioBaseSrc * sink) -+{ -+ GstPwAudioSrc *self = GST_PW_AUDIO_SRC (sink); -+ GstAudioRingBuffer *buffer; -+ -+ GST_DEBUG_OBJECT (sink, "creating ringbuffer"); -+ buffer = g_object_new (GST_TYPE_PW_AUDIO_RING_BUFFER, -+ "element", sink, -+ "direction", PW_DIRECTION_INPUT, -+ "props", &self->props, -+ NULL); -+ GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer); -+ -+ return buffer; -+} -+ -+static void -+gst_pw_audio_src_class_init (GstPwAudioSrcClass * klass) -+{ -+ GObjectClass *gobject_class; -+ GstElementClass *gstelement_class; -+ GstAudioBaseSrcClass *gstaudiobsrc_class; -+ -+ gobject_class = (GObjectClass *) klass; -+ gstelement_class = (GstElementClass *) klass; -+ gstaudiobsrc_class = (GstAudioBaseSrcClass *) klass; -+ -+ gobject_class->finalize = gst_pw_audio_src_finalize; -+ gobject_class->set_property = gst_pw_audio_src_set_property; -+ gobject_class->get_property = gst_pw_audio_src_get_property; -+ -+ gstaudiobsrc_class->create_ringbuffer = gst_pw_audio_src_create_ringbuffer; -+ -+ g_object_class_install_property (gobject_class, PROP_PATH, -+ g_param_spec_string ("path", "Path", -+ "The sink path to connect to (NULL = default)", NULL, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_CLIENT_NAME, -+ g_param_spec_string ("client-name", "Client Name", -+ "The client name to use (NULL = default)", NULL, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_STREAM_PROPERTIES, -+ g_param_spec_boxed ("stream-properties", "Stream properties", -+ "List of PipeWire stream properties", GST_TYPE_STRUCTURE, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_FD, -+ g_param_spec_int ("fd", "Fd", "The fd to connect with", -1, G_MAXINT, -1, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ gst_element_class_set_static_metadata (gstelement_class, -+ "PipeWire Audio source", "Source/Audio", -+ "Receive audio from PipeWire", -+ "George Kiagiadakis <george.kiagiadakis@collabora.com>"); -+ -+ gst_element_class_add_pad_template (gstelement_class, -+ gst_static_pad_template_get (&gst_pw_audio_src_template)); -+ -+ GST_DEBUG_CATEGORY_INIT (pw_audio_src_debug, "pwaudiosrc", 0, -+ "PipeWire Audio Src"); -+} -+ -diff --git a/src/gst/gstpwaudiosrc.h b/src/gst/gstpwaudiosrc.h -new file mode 100644 -index 00000000..c46e644c ---- /dev/null -+++ b/src/gst/gstpwaudiosrc.h -@@ -0,0 +1,48 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifndef __GST_PW_AUDIO_SRC_H__ -+#define __GST_PW_AUDIO_SRC_H__ -+ -+#include "gstpwaudioringbuffer.h" -+ -+G_BEGIN_DECLS -+ -+#define GST_TYPE_PW_AUDIO_SRC \ -+ (gst_pw_audio_src_get_type ()) -+ -+G_DECLARE_FINAL_TYPE(GstPwAudioSrc, gst_pw_audio_src, -+ GST, PW_AUDIO_SRC, GstAudioBaseSrc); -+ -+struct _GstPwAudioSrc -+{ -+ GstAudioBaseSrc parent; -+ GstPwAudioRingBufferProps props; -+}; -+ -+G_END_DECLS -+ -+#endif -diff --git a/src/gst/meson.build b/src/gst/meson.build -index ad0e0801..0e922347 100644 ---- a/src/gst/meson.build -+++ b/src/gst/meson.build -@@ -6,6 +6,9 @@ pipewire_gst_sources = [ - 'gstpipewirepool.c', - 'gstpipewiresink.c', - 'gstpipewiresrc.c', -+ 'gstpwaudioringbuffer.c', -+ 'gstpwaudiosink.c', -+ 'gstpwaudiosrc.c', - ] - - pipewire_gst_headers = [ -@@ -15,6 +18,9 @@ pipewire_gst_headers = [ - 'gstpipewirepool.h', - 'gstpipewiresink.h', - 'gstpipewiresrc.h', -+ 'gstpwaudioringbuffer.h', -+ 'gstpwaudiosink.h', -+ 'gstpwaudiosrc.h', - ] - - pipewire_gst_c_args = [ --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-audioconvert-always-assume-that-output-ports-are-NOT.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-audioconvert-always-assume-that-output-ports-are-NOT.patch deleted file mode 100644 index beb878390..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-audioconvert-always-assume-that-output-ports-are-NOT.patch +++ /dev/null @@ -1,35 +0,0 @@ -From ce155eb0073fba84556782633f79bb7d03492c07 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Wed, 2 Oct 2019 21:40:34 +0300 -Subject: [PATCH] audioconvert: always assume that output ports are NOT monitor - ports - -Otherwise, when we setup audioconvert in merge+split mode, -it assumes that the splitter's ports are monitor ports and -belong to the merger. - -Upstream-Status: Inappropriate [workaround] ---- - spa/plugins/audioconvert/audioconvert.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c -index 74a62a35..72da37d1 100644 ---- a/spa/plugins/audioconvert/audioconvert.c -+++ b/spa/plugins/audioconvert/audioconvert.c -@@ -113,8 +113,12 @@ struct impl { - unsigned int add_listener:1; - }; - -+#if 0 - #define IS_MONITOR_PORT(this,dir,port_id) (dir == SPA_DIRECTION_OUTPUT && port_id > 0 && \ - this->mode[SPA_DIRECTION_INPUT] == SPA_PARAM_PORT_CONFIG_MODE_dsp) -+#else -+#define IS_MONITOR_PORT(this,dir,port_id) (false) -+#endif - - static void emit_node_info(struct impl *this, bool full) - { --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-module-access-add-same-sec-label-mode.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-module-access-add-same-sec-label-mode.patch deleted file mode 100644 index 07a1ec114..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-module-access-add-same-sec-label-mode.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 19fad1a4fa8bdc4f02aac4e169e7ff9cab18bdcd Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Tue, 19 Nov 2019 17:09:07 +0200 -Subject: [PATCH] module-access: add same-sec-label-mode - -This is a mode where the access module allows all clients that have -the same security label as the pipewire daemon, and every other -client is put on the restricted state. - -In systems that use SMACK security labels, such as AGL, this allows -the session manager (which is spawned by pipewire, inheriting the -same smack label) to have full access to all objects, while every -other client is restricted and the session manager must decide -what to do with it - -Note that while this option is configurable, there is no loss of -security if this option is not set in the configuration. Clients -that don't have the same security context will be considered to -be flatpak clients because pipewire will not be able to open -/proc/pid/cmdline. This however results in some unwanted error -messages that may be confusing. - -Upstream-Status: Inappropriate [agl/smack specific] ---- - src/modules/module-access.c | 45 ++++++++++++++++++++++++++++++++++++- - 1 file changed, 44 insertions(+), 1 deletion(-) - -diff --git a/src/modules/module-access.c b/src/modules/module-access.c -index 09dafa43..f75306d9 100644 ---- a/src/modules/module-access.c -+++ b/src/modules/module-access.c -@@ -50,6 +50,30 @@ struct impl { - struct spa_hook module_listener; - }; - -+static int check_seclabel(const char *str) -+{ -+ char attr[1024]; -+ int fd, len; -+ -+ fd = open("/proc/self/attr/current", O_RDONLY); -+ if (fd < 0) -+ return -errno; -+ -+ if ((len = read(fd, attr, 1024)) <= 0) { -+ close(fd); -+ return -EIO; -+ } -+ attr[len] = '\0'; -+ -+ if (strcmp(attr, str) == 0) { -+ close(fd); -+ return 1; -+ } -+ -+ close(fd); -+ return 0; -+} -+ - static int check_cmdline(struct pw_client *client, int pid, const char *str) - { - char path[2048]; -@@ -121,8 +145,27 @@ core_check_access(void *data, struct pw_client *client) - const char *str; - int pid, res; - -+ props = pw_client_get_properties(client); -+ -+ if (impl->properties && -+ (str = pw_properties_get(impl->properties, "same-sec-label-mode")) != NULL && -+ strcmp(str, "1") == 0) { -+ if (props && (str = pw_properties_get(props, PW_KEY_SEC_LABEL)) != NULL) { -+ res = check_seclabel(str); -+ if (res == 1) -+ goto granted; -+ else if (res < 0) -+ pw_log_warn("module %p: client %p seclabel check failed: %s", -+ impl, client, spa_strerror(res)); -+ } -+ pw_log_debug("module %p: seclabel restricted client %p added", -+ impl, client); -+ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_ACCESS, "restricted"); -+ goto wait_permissions; -+ } -+ - pid = -EINVAL; -- if ((props = pw_client_get_properties(client)) != NULL) { -+ if (props != NULL) { - if ((str = pw_properties_get(props, PW_KEY_SEC_PID)) != NULL) - pid = atoi(str); - } --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-alsa-pcm-call-reuse_buffers-when-resetting-the-state.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-alsa-pcm-call-reuse_buffers-when-resetting-the-state.patch deleted file mode 100644 index cae4d70f6..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-alsa-pcm-call-reuse_buffers-when-resetting-the-state.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 5946fbd2ca3a7f892b4ebc10090f62df6bb1ec88 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Thu, 9 Jan 2020 19:27:23 +0200 -Subject: [PATCH] alsa-pcm: call reuse_buffers when resetting the state of the - buffers - -This allows the upstream node to put buffers back to its pool in case -they were left around in the ready list locally when the alsa-pcm-sink -was last paused. - -Fixes #203 ---- - spa/plugins/alsa/alsa-pcm.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c -index 63d75549..a6f22cf0 100644 ---- a/spa/plugins/alsa/alsa-pcm.c -+++ b/spa/plugins/alsa/alsa-pcm.c -@@ -1115,6 +1115,7 @@ static void reset_buffers(struct state *this) - struct buffer *b = &this->buffers[i]; - if (this->stream == SND_PCM_STREAM_PLAYBACK) { - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); -+ spa_node_call_reuse_buffer(&this->callbacks, 0, b->id); - } else { - spa_list_append(&this->free, &b->link); - SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); --- -2.24.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-alsa-Set-period_size-depending-on-hardware.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-alsa-Set-period_size-depending-on-hardware.patch deleted file mode 100644 index f9649400e..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-alsa-Set-period_size-depending-on-hardware.patch +++ /dev/null @@ -1,35 +0,0 @@ -From bbf9c767d5b353142e03080762bdd805e124d50b Mon Sep 17 00:00:00 2001 -From: Walter Lozano <walter.lozano@collabora.com> -Date: Fri, 7 Aug 2020 10:58:29 -0300 -Subject: [PATCH] alsa: Set period_size depending on hardware - -Currently PipeWire is unable to reproduce audio in systems where DMA -granularity is not burst. - -In order to mitigate this issue, set period_size depending on hardware, -lowering it when snd_pcm_hw_params_is_batch == 1, to reduce DMA -transfers size. - -Signed-off-by: Walter Lozano <walter.lozano@collabora.com> ---- - spa/plugins/alsa/alsa-pcm.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c -index 63d75549..2d3850b7 100644 ---- a/spa/plugins/alsa/alsa-pcm.c -+++ b/spa/plugins/alsa/alsa-pcm.c -@@ -463,6 +463,10 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_ - - dir = 0; - period_size = 1024; -+ if (snd_pcm_hw_params_is_batch(params)) { -+ period_size = 512; -+ spa_log_warn(state->log, NAME" hardware does double buffering, changing period_size to %ld", period_size); -+ } - CHECK(snd_pcm_hw_params_set_period_size_near(hndl, params, &period_size, &dir), "set_period_size_near"); - CHECK(snd_pcm_hw_params_get_buffer_size_max(params, &state->buffer_frames), "get_buffer_size_max"); - CHECK(snd_pcm_hw_params_set_buffer_size_near(hndl, params, &state->buffer_frames), "set_buffer_size_near"); --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-alsa-add-warning-in-case-of-partial-read-write.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-alsa-add-warning-in-case-of-partial-read-write.patch deleted file mode 100644 index 98a2c98fc..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-alsa-add-warning-in-case-of-partial-read-write.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 45658f75e61da47b274f2eba3a55e62d016f8b42 Mon Sep 17 00:00:00 2001 -From: Walter Lozano <walter.lozano@collabora.com> -Date: Mon, 24 Aug 2020 12:08:32 -0300 -Subject: [PATCH 8/9] alsa: add warning in case of partial read/write - -Currently alsa_read and alsa_write assumes that all the frames committed -using snd_pcm_mmap_commit are read or written, which is probably true. -However, as it could be some corner cases add a warning to notice this -fact. - -Signed-off-by: Walter Lozano <walter.lozano@collabora.com> ---- - spa/plugins/alsa/alsa-pcm.c | 28 ++++++++++++++++++++-------- - 1 file changed, 20 insertions(+), 8 deletions(-) - -diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c -index ed9bf42b..92ef2151 100644 ---- a/spa/plugins/alsa/alsa-pcm.c -+++ b/spa/plugins/alsa/alsa-pcm.c -@@ -721,6 +721,7 @@ int spa_alsa_write(struct state *state, snd_pcm_uframes_t silence) - snd_pcm_t *hndl = state->hndl; - const snd_pcm_channel_area_t *my_areas; - snd_pcm_uframes_t written, frames, offset, off, to_write, total_written; -+ snd_pcm_sframes_t commitres; - int res; - - if (state->position && state->duration != state->position->clock.duration) { -@@ -834,11 +835,16 @@ again: - state, offset, written, state->sample_count); - total_written += written; - -- if ((res = snd_pcm_mmap_commit(hndl, offset, written)) < 0) { -+ if ((commitres = snd_pcm_mmap_commit(hndl, offset, written)) < 0) { - spa_log_error(state->log, NAME" %p: snd_pcm_mmap_commit error: %s", -- state, snd_strerror(res)); -- if (res != -EPIPE && res != -ESTRPIPE) -- return res; -+ state, snd_strerror(commitres)); -+ if (commitres != -EPIPE && commitres != -ESTRPIPE) -+ return commitres; -+ } -+ -+ if (commitres > 0 && written != (snd_pcm_uframes_t) commitres) { -+ spa_log_warn(state->log, NAME" %p: mmap_commit wrote %ld instead of %ld", -+ state, commitres, written); - } - - if (!spa_list_is_empty(&state->ready) && written > 0) -@@ -922,6 +928,7 @@ int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence) - snd_pcm_uframes_t total_read = 0, to_read; - const snd_pcm_channel_area_t *my_areas; - snd_pcm_uframes_t read, frames, offset; -+ snd_pcm_sframes_t commitres; - int res; - - if (state->position) { -@@ -994,11 +1001,16 @@ int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence) - offset, read, state->sample_count); - total_read += read; - -- if ((res = snd_pcm_mmap_commit(hndl, offset, read)) < 0) { -+ if ((commitres = snd_pcm_mmap_commit(hndl, offset, read)) < 0) { - spa_log_error(state->log, NAME" %p: snd_pcm_mmap_commit error: %s", -- state, snd_strerror(res)); -- if (res != -EPIPE && res != -ESTRPIPE) -- return res; -+ state, snd_strerror(commitres)); -+ if (commitres != -EPIPE && commitres != -ESTRPIPE) -+ return commitres; -+ } -+ -+ if (commitres > 0 && read != (snd_pcm_uframes_t) commitres) { -+ spa_log_warn(state->log, NAME" %p: mmap_commit read %ld instead of %ld", -+ state, commitres, read); - } - - state->sample_count += total_read; --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-adjust-delay-depending-on-hardware.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-adjust-delay-depending-on-hardware.patch deleted file mode 100644 index a448063f1..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-adjust-delay-depending-on-hardware.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 38cdfa4483de4c2e91bfccb9c22ec72d9c3720f4 Mon Sep 17 00:00:00 2001 -From: Walter Lozano <walter.lozano@collabora.com> -Date: Sat, 22 Aug 2020 11:51:30 -0300 -Subject: [PATCH 9/9] alsa: adjust delay depending on hardware - -Currently PipeWire is able to reproduce audio in systems where -DMA granularity is not burst but it could face an xrun. - -In order to mitigate this issue, adjust the delay PipeWire -calculates to make sure that a period is available in the buffer -when snd_pcm_hw_params_is_batch == 1. - -Signed-off-by: Walter Lozano <walter.lozano@collabora.com> ---- - spa/plugins/alsa/alsa-pcm.c | 12 +++++++++++- - spa/plugins/alsa/alsa-pcm.h | 1 + - 2 files changed, 12 insertions(+), 1 deletion(-) - -diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c -index 92ef2151..1f15085f 100644 ---- a/spa/plugins/alsa/alsa-pcm.c -+++ b/spa/plugins/alsa/alsa-pcm.c -@@ -462,8 +462,9 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_ - state->frame_size = info->channels * (snd_pcm_format_physical_width(format) / 8); - - dir = 0; -+ state->pcm_is_batch = snd_pcm_hw_params_is_batch(params); - period_size = 1024; -- if (snd_pcm_hw_params_is_batch(params)) { -+ if (state->pcm_is_batch) { - period_size = 512; - spa_log_warn(state->log, NAME" hardware does double buffering, changing period_size to %ld", period_size); - } -@@ -639,6 +640,15 @@ static int get_status(struct state *state, snd_pcm_uframes_t *delay, snd_pcm_ufr - - if (state->stream == SND_PCM_STREAM_PLAYBACK) { - *delay = state->buffer_frames - avail; -+ if (state->pcm_is_batch) { -+ /* In this case, as we don't have a good granularity in the -+ * avail report try to compensate this by tweaking the delay -+ * and make sure that a period is available in the buffer */ -+ if (*delay > state->period_frames) -+ *delay = *delay - state->period_frames; -+ else -+ *delay = 0; -+ } - } - else { - *delay = avail; -diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h -index b7a2dd29..3b5c0d7b 100644 ---- a/spa/plugins/alsa/alsa-pcm.h -+++ b/spa/plugins/alsa/alsa-pcm.h -@@ -100,6 +100,7 @@ struct state { - - bool have_format; - struct spa_audio_info current_format; -+ bool pcm_is_batch; - - snd_pcm_uframes_t buffer_frames; - snd_pcm_uframes_t period_frames; --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.27.bb b/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.27.bb new file mode 100644 index 000000000..2757e9278 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.27.bb @@ -0,0 +1,282 @@ +# This recipe was written by Carlos Rafael Giani <crg7475@mailbox.org> +# for meta-oe +# +# The intention is to keep this synced with meta-oe and remove it when we +# can depend on meta-oe to provide this recipe for us +# +# AGL-specific overrides and configuration should go in the .bbappend file +# +SUMMARY = "Multimedia processing server for Linux" +DESCRIPTION = "Linux server for handling and routing audio and video streams between applications and multimedia I/O devices" +HOMEPAGE = "https://pipewire.org/" +BUGTRACKER = "https://gitlab.freedesktop.org/pipewire/pipewire/issues" +LICENSE = "MIT" +LIC_FILES_CHKSUM = " \ + file://LICENSE;md5=e2c0b7d86d04e716a3c4c9ab34260e69 \ + file://COPYING;md5=97be96ca4fab23e9657ffa590b931c1a \ +" +SECTION = "multimedia" + +DEPENDS = "dbus" + +SRCREV = "e598d0a42227c9dfa79dcb7583c054c5b2ec072d" +SRC_URI = "git://gitlab.freedesktop.org/pipewire/pipewire.git;branch=master;protocol=https" + +S = "${WORKDIR}/git" + +inherit meson pkgconfig systemd manpages gettext useradd + +USERADD_PACKAGES = "${PN}" + +GROUPADD_PARAM_${PN} = "--system pipewire" + +USERADD_PARAM_${PN} = "--system --home / --no-create-home \ + --comment 'PipeWire multimedia daemon' \ + --gid pipewire --groups audio,video \ + pipewire" + +# For "EVL", look up https://evlproject.org/ . It involves +# a specially prepared kernel, and is currently unavailable +# in Yocto. +# FFmpeg and Vulkan aren't really supported - at the current +# stage (version 0.3.22), these are just experiments, not +# actual features. +# libcamera support currently does not build successfully. +# systemd user service files are disabled because per-user +# PipeWire instances aren't really something that makes +# much sense in an embedded environment. A system-wide +# instance does. +EXTRA_OEMESON += " \ + -Daudiotestsrc=enabled \ + -Devl=disabled \ + -Dsystemd-user-service=disabled \ + -Dtests=disabled \ + -Dudevrulesdir=${nonarch_base_libdir}/udev/rules.d/ \ + -Dvideotestsrc=enabled \ + -Dffmpeg=disabled \ + -Dvulkan=disabled \ + -Dlibcamera=disabled \ +" + +PACKAGECONFIG ??= "\ + ${@bb.utils.contains('DISTRO_FEATURES', 'bluetooth', 'bluez', '', d)} \ + ${@bb.utils.filter('DISTRO_FEATURES', 'alsa systemd', d)} \ + gstreamer jack v4l2 \ +" + +# "jack" and "pipewire-jack" packageconfigs cannot be both enabled, +# since "jack" imports libjack, and "pipewire-jack" generates +# libjack.so* files, thus colliding with the libpack package. This +# is why these two are marked in their respective packageconfigs +# as being in conflict. + +PACKAGECONFIG[alsa] = "-Dalsa=enabled,-Dalsa=disabled,alsa-lib udev" +PACKAGECONFIG[bluez] = "-Dbluez5=enabled,-Dbluez5=disabled,bluez5 sbc" +PACKAGECONFIG[docs] = "-Ddocs=enabled,-Ddocs=disabled,doxygen" +PACKAGECONFIG[gstreamer] = "-Dgstreamer=enabled,-Dgstreamer=disabled,glib-2.0 gstreamer1.0 gstreamer1.0-plugins-base" +PACKAGECONFIG[jack] = "-Djack=enabled,-Djack=disabled,jack,,,pipewire-jack" +PACKAGECONFIG[manpages] = "-Dman=enabled,-Dman=disabled,libxml-parser-perl-native" +PACKAGECONFIG[sdl2] = "-Dsdl2=enabled,-Dsdl2=disabled,virtual/libsdl2" +PACKAGECONFIG[sndfile] = "-Dsndfile=enabled,-Dsndfile=disabled,libsndfile1" +PACKAGECONFIG[systemd] = "-Dsystemd=enabled -Dsystemd-system-service=enabled ,-Dsystemd=disabled -Dsystemd-system-service=disabled,systemd" +PACKAGECONFIG[v4l2] = "-Dv4l2=enabled,-Dv4l2=disabled,udev" +PACKAGECONFIG[pipewire-alsa] = "-Dpipewire-alsa=enabled,-Dpipewire-alsa=disabled,alsa-lib" +PACKAGECONFIG[pipewire-jack] = "-Dpipewire-jack=enabled -Dlibjack-path=${libdir}/${PW_MODULE_SUBDIR}/jack,-Dpipewire-jack=disabled,jack,,,jack" + +PACKAGESPLITFUNCS_prepend = " split_dynamic_packages " +PACKAGESPLITFUNCS_append = " set_dynamic_metapkg_rdepends " + +SPA_SUBDIR = "spa-0.2" +PW_MODULE_SUBDIR = "pipewire-0.3" + +remove_unused_installed_files() { + # jack.conf is used by pipewire-jack (not the JACK SPA plugin). + # Remove it if pipewire-jack is not built to avoid creating the + # pipewire-jack package. + if ${@bb.utils.contains('PACKAGECONFIG', 'pipewire-jack', 'false', 'true', d)}; then + rm -f "${D}${sysconfdir}/pipewire/jack.conf" + fi +} + +do_install[postfuncs] += "remove_unused_installed_files" + +python split_dynamic_packages () { + # Create packages for each SPA plugin. These plugins are located + # in individual subdirectories, so a recursive search is needed. + spa_libdir = d.expand('${libdir}/${SPA_SUBDIR}') + do_split_packages(d, spa_libdir, r'^libspa-(.*)\.so$', d.expand('${PN}-spa-plugins-%s'), 'PipeWire SPA plugin for %s', extra_depends='', recursive=True) + + # Create packages for each PipeWire module. + pw_module_libdir = d.expand('${libdir}/${PW_MODULE_SUBDIR}') + do_split_packages(d, pw_module_libdir, r'^libpipewire-module-(.*)\.so$', d.expand('${PN}-modules-%s'), 'PipeWire %s module', extra_depends='', recursive=False) +} + +python set_dynamic_metapkg_rdepends () { + import os + import oe.utils + + # Go through all generated SPA plugin and PipeWire module packages + # (excluding the main package and the -meta package itself) and + # add them to the -meta package as RDEPENDS. + + base_pn = d.getVar('PN') + + spa_pn = base_pn + '-spa-plugins' + spa_metapkg = spa_pn + '-meta' + + pw_module_pn = base_pn + '-modules' + pw_module_metapkg = pw_module_pn + '-meta' + + d.setVar('ALLOW_EMPTY_' + spa_metapkg, "1") + d.setVar('FILES_' + spa_metapkg, "") + + d.setVar('ALLOW_EMPTY_' + pw_module_metapkg, "1") + d.setVar('FILES_' + pw_module_metapkg, "") + + blacklist = [ spa_pn, spa_metapkg, pw_module_pn, pw_module_metapkg ] + spa_metapkg_rdepends = [] + pw_module_metapkg_rdepends = [] + pkgdest = d.getVar('PKGDEST') + + for pkg in oe.utils.packages_filter_out_system(d): + if pkg in blacklist: + continue + + is_spa_pkg = pkg.startswith(spa_pn) + is_pw_module_pkg = pkg.startswith(pw_module_pn) + if not is_spa_pkg and not is_pw_module_pkg: + continue + + if pkg in spa_metapkg_rdepends or pkg in pw_module_metapkg_rdepends: + continue + + # See if the package is empty by looking at the contents of its + # PKGDEST subdirectory. If this subdirectory is empty, then then + # package is empty as well. Empty packages do not get added to + # the meta package's RDEPENDS. + pkgdir = os.path.join(pkgdest, pkg) + if os.path.exists(pkgdir): + dir_contents = os.listdir(pkgdir) or [] + else: + dir_contents = [] + is_empty = len(dir_contents) == 0 + if not is_empty: + if is_spa_pkg: + spa_metapkg_rdepends.append(pkg) + if is_pw_module_pkg: + pw_module_metapkg_rdepends.append(pkg) + + d.setVar('RDEPENDS_' + spa_metapkg, ' '.join(spa_metapkg_rdepends)) + d.setVar('DESCRIPTION_' + spa_metapkg, spa_pn + ' meta package') + + d.setVar('RDEPENDS_' + pw_module_metapkg, ' '.join(pw_module_metapkg_rdepends)) + d.setVar('DESCRIPTION_' + pw_module_metapkg, pw_module_pn + ' meta package') +} + +PACKAGES =+ "\ + libpipewire \ + ${PN}-tools \ + ${PN}-pulse \ + ${PN}-alsa \ + ${PN}-jack \ + ${PN}-media-session \ + ${PN}-spa-plugins \ + ${PN}-spa-plugins-meta \ + ${PN}-spa-tools \ + ${PN}-modules \ + ${PN}-modules-meta \ + ${PN}-alsa-card-profile \ + gstreamer1.0-pipewire \ +" + +PACKAGES_DYNAMIC = "^${PN}-spa-plugins.* ^${PN}-modules.*" + +SYSTEMD_SERVICE_${PN} = "pipewire.service" +CONFFILES_${PN} += "${sysconfdir}/pipewire/pipewire.conf" +FILES_${PN} = " \ + ${sysconfdir}/pipewire/pipewire.conf \ + ${systemd_system_unitdir}/pipewire.* \ + ${bindir}/pipewire \ +" + +FILES_${PN}-dev += " \ + ${libdir}/${PW_MODULE_SUBDIR}/jack/libjack*.so \ +" + +CONFFILES_libpipewire += "${sysconfdir}/pipewire/client.conf" +FILES_libpipewire = " \ + ${sysconfdir}/pipewire/client.conf \ + ${libdir}/libpipewire-*.so.* \ +" +# Add the bare minimum modules and plugins required to be able +# to use libpipewire. Without these, it is essentially unusable. +RDEPENDS_libpipewire += " \ + ${PN}-modules-client-node \ + ${PN}-modules-protocol-native \ + ${PN}-spa-plugins-support \ +" + +FILES_${PN}-tools = " \ + ${bindir}/pw-* \ +" + +# This is a shim daemon that is intended to be used as a +# drop-in PulseAudio replacement, providing a pulseaudio-compatible +# socket that can be used by applications that use libpulse. +CONFFILES_${PN}-pulse += "${sysconfdir}/pipewire/pipewire-pulse.conf" +FILES_${PN}-pulse = " \ + ${sysconfdir}/pipewire/pipewire-pulse.conf \ + ${bindir}/pipewire-pulse \ +" +RDEPENDS_${PN}-pulse += " \ + ${PN}-modules-protocol-pulse \ +" + +# alsa plugin to redirect audio to pipewire +FILES_${PN}-alsa = "\ + ${libdir}/alsa-lib/* \ + ${datadir}/alsa/alsa.conf.d/* \ +" + +# jack drop-in libraries to redirect audio to pipewire +CONFFILES_${PN}-jack = "${sysconfdir}/pipewire/jack.conf" +FILES_${PN}-jack = "\ + ${sysconfdir}/pipewire/jack.conf \ + ${libdir}/${PW_MODULE_SUBDIR}/jack/libjack*.so.* \ +" + +# Example session manager. Not intended for use in production. +SYSTEMD_SERVICE_${PN}-media-session = "pipewire-media-session.service" +CONFFILES_${PN}-media-session = "${sysconfdir}/pipewire/media-session.d/*" +FILES_${PN}-media-session = " \ + ${bindir}/pipewire-media-session \ + ${systemd_system_unitdir}/pipewire-media-session.service \ + ${sysconfdir}/pipewire/media-session.d/* \ +" +RPROVIDES_${PN}-media-session = "virtual/pipewire-sessionmanager" + +# Dynamic packages (see set_dynamic_metapkg_rdepends). +FILES_${PN}-spa-plugins = "" +RRECOMMENDS_${PN}-spa-plugins += "${PN}-spa-plugins-meta" + +FILES_${PN}-spa-tools = " \ + ${bindir}/spa-* \ +" + +# Dynamic packages (see set_dynamic_metapkg_rdepends). +FILES_${PN}-modules = "" +RRECOMMENDS_${PN}-modules += "${PN}-modules-meta" + +CONFFILES_${PN}-modules-rtkit = "${sysconfdir}/pipewire/client-rt.conf" +FILES_${PN}-modules-rtkit += " \ + ${sysconfdir}/pipewire/client-rt.conf \ + " + +FILES_${PN}-alsa-card-profile = " \ + ${datadir}/alsa-card-profile/* \ + ${nonarch_base_libdir}/udev/rules.d/90-pipewire-alsa.rules \ +" + +FILES_gstreamer1.0-pipewire = " \ + ${libdir}/gstreamer-1.0/* \ +" diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.27.bbappend b/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.27.bbappend new file mode 100644 index 000000000..22700ae67 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.27.bbappend @@ -0,0 +1,12 @@ +PACKAGECONFIG = "\ + ${@bb.utils.contains('DISTRO_FEATURES', 'bluez5', 'bluez', '', d)} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'alsa', 'alsa pipewire-alsa', '', d)} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'agl-devel', 'sndfile', '', d)} \ + ${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)} \ + gstreamer v4l2 \ +" + +SRC_URI += "\ + file://0001-pipewiresink-use-all-the-available-dest-memory-when-.patch \ + file://0002-pipewiresink-release-manually-acquired-buffers-back-.patch \ +" diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb deleted file mode 100644 index e2560ad9d..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb +++ /dev/null @@ -1,20 +0,0 @@ -require pipewire.inc - -SRC_URI = "git://gitlab.freedesktop.org/pipewire/pipewire.git;protocol=https;branch=master \ - file://0001-meson-revert-version-check-to-require-meson-0.47-not.patch \ - file://0002-arm-build-with-mno-unaligned-access.patch \ - file://0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch \ - file://0004-audioconvert-always-assume-that-output-ports-are-NOT.patch \ - file://0005-module-access-add-same-sec-label-mode.patch \ - file://0006-alsa-pcm-call-reuse_buffers-when-resetting-the-state.patch \ - file://0007-alsa-Set-period_size-depending-on-hardware.patch \ - file://0008-alsa-add-warning-in-case-of-partial-read-write.patch \ - file://0009-alsa-adjust-delay-depending-on-hardware.patch \ - " - -SRCREV = "b0932e687fc47e0872ca291531f2291d99042d70" - -PV = "0.2.91+git${SRCPV}+2" -S = "${WORKDIR}/git" - -RDEPENDS_${PN} += "virtual/pipewire-sessionmanager virtual/pipewire-config" diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-audio-sink.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-audio-sink.endpoint deleted file mode 100644 index 4bc435742..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-audio-sink.endpoint +++ /dev/null @@ -1,10 +0,0 @@ -[match-node] -priority = 0 -properties = [ - { name = "media.class", value = "Audio/Sink" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "sink" -streams = "playback.streams" diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-audio-source.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-audio-source.endpoint deleted file mode 100644 index 7657f6f40..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-audio-source.endpoint +++ /dev/null @@ -1,10 +0,0 @@ -[match-node] -priority = 0 -properties = [ - { name = "media.class", value = "Audio/Source" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "source" -streams = "capture.streams" diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-default-input-audio.endpoint-link b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-default-input-audio.endpoint-link deleted file mode 100644 index 4b70dc89f..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-default-input-audio.endpoint-link +++ /dev/null @@ -1,7 +0,0 @@ -[match-endpoint] -priority = 0 -direction = "sink" -media_class = "Stream/Input/Audio" - -[endpoint-link] -keep = false diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-default-output-audio.endpoint-link b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-default-output-audio.endpoint-link deleted file mode 100644 index 5d6428f94..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-default-output-audio.endpoint-link +++ /dev/null @@ -1,7 +0,0 @@ -[match-endpoint] -priority = 0 -direction = "source" -media_class = "Stream/Output/Audio" - -[endpoint-link] -keep = false diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-stream-input-audio.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-stream-input-audio.endpoint deleted file mode 100644 index 2993f3e44..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-stream-input-audio.endpoint +++ /dev/null @@ -1,9 +0,0 @@ -[match-node] -priority = 0 -properties = [ - { name = "media.class", value = "Stream/Input/Audio" }, -] - -[endpoint] -direction = "sink" -type = "pw-audio-softdsp-endpoint" diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-stream-output-audio.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-stream-output-audio.endpoint deleted file mode 100644 index 1cf82ea02..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/00-stream-output-audio.endpoint +++ /dev/null @@ -1,9 +0,0 @@ -[match-node] -priority = 0 -properties = [ - { name = "media.class", value = "Stream/Output/Audio" }, -] - -[endpoint] -direction = "source" -type = "pw-audio-softdsp-endpoint" diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/01-hw00-audio-sink.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/01-hw00-audio-sink.endpoint deleted file mode 100644 index 85a9b5117..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/01-hw00-audio-sink.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 1 -properties = [ - { name = "media.class", value = "Audio/Sink" }, - { name = "api.alsa.path", value = "hw:0,0" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "sink" -streams = "playback.streams" -priority = 1 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/01-hw00-audio-source.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/01-hw00-audio-source.endpoint deleted file mode 100644 index c77701c0d..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/01-hw00-audio-source.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 1 -properties = [ - { name = "media.class", value = "Audio/Source" }, - { name = "api.alsa.path", value = "hw:0,0" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "source" -streams = "capture.streams" -priority = 1 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-ak4613-audio-sink.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-ak4613-audio-sink.endpoint deleted file mode 100644 index 4405f7b7a..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-ak4613-audio-sink.endpoint +++ /dev/null @@ -1,13 +0,0 @@ -[match-node] -priority = 30 -properties = [ - { name = "media.class", value = "Audio/Sink" }, - { name = "api.alsa.card.id", value = "ak4613" }, - { name = "api.alsa.pcm.device", value = "0" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "sink" -streams = "playback.streams" -priority = 30 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-ak4613-audio-source.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-ak4613-audio-source.endpoint deleted file mode 100644 index ad5680592..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-ak4613-audio-source.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 30 -properties = [ - { name = "media.class", value = "Audio/Source" }, - { name = "api.alsa.card.id", value = "ak4613" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "source" -streams = "capture.streams" -priority = 30 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-dra7xx-audio-sink.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-dra7xx-audio-sink.endpoint deleted file mode 100644 index becd21e2e..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-dra7xx-audio-sink.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 30 -properties = [ - { name = "media.class", value = "Audio/Sink" }, - { name = "api.alsa.card.id", value = "DRA7xx-EVM" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "sink" -streams = "playback.streams" -priority = 30 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-dra7xx-audio-source.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-dra7xx-audio-source.endpoint deleted file mode 100644 index 72ef46770..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-dra7xx-audio-source.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 30 -properties = [ - { name = "media.class", value = "Audio/Source" }, - { name = "api.alsa.card.id", value = "DRA7xx-EVM" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "source" -streams = "capture.streams" -priority = 30 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-imx8mq-audio-sink.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-imx8mq-audio-sink.endpoint deleted file mode 100644 index 538d31376..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-imx8mq-audio-sink.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 30 -properties = [ - { name = "media.class", value = "Audio/Sink" }, - { name = "api.alsa.card.id", value = "wm8524audio" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "sink" -streams = "playback.streams" -priority = 30 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-rcarsound-audio-sink.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-rcarsound-audio-sink.endpoint deleted file mode 100644 index 0c7b20805..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-rcarsound-audio-sink.endpoint +++ /dev/null @@ -1,13 +0,0 @@ -[match-node] -priority = 30 -properties = [ - { name = "media.class", value = "Audio/Sink" }, - { name = "api.alsa.card.id", value = "rcarsound" }, - { name = "api.alsa.pcm.device", value = "0" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "sink" -streams = "playback.streams" -priority = 30 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-rcarsound-audio-source.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-rcarsound-audio-source.endpoint deleted file mode 100644 index d72d7e31c..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-rcarsound-audio-source.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 30 -properties = [ - { name = "media.class", value = "Audio/Source" }, - { name = "api.alsa.card.id", value = "rcarsound" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "source" -streams = "capture.streams" -priority = 30 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-rpi3-audio-sink.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-rpi3-audio-sink.endpoint deleted file mode 100644 index 74e4d62e6..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/30-rpi3-audio-sink.endpoint +++ /dev/null @@ -1,13 +0,0 @@ -[match-node] -priority = 30 -properties = [ - { name = "media.class", value = "Audio/Sink" }, - { name = "api.alsa.card.name", value = "bcm2835 ALSA" }, - { name = "api.alsa.pcm.name", value = "bcm2835 ALSA" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "sink" -streams = "playback.streams" -priority = 30 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/40-fiberdyne-amp.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/40-fiberdyne-amp.endpoint deleted file mode 100644 index 807ad4688..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/40-fiberdyne-amp.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 40 -properties = [ - { name = "media.class", value = "Audio/Sink" }, - { name = "api.alsa.card.id", value = "ep016ch" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "sink" -streams = "playback.streams" -priority = 40 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/40-microchip-mic.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/40-microchip-mic.endpoint deleted file mode 100644 index bbfcd43a5..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/40-microchip-mic.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 40 -properties = [ - { name = "media.class", value = "Audio/Source" }, - { name = "api.alsa.card.id", value = "ep811ch" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "source" -streams = "capture.streams" -priority = 40 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/70-usb-audio-sink.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/70-usb-audio-sink.endpoint deleted file mode 100644 index 62e279090..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/70-usb-audio-sink.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 70 -properties = [ - { name = "media.class", value = "Audio/Sink" }, - { name = "api.alsa.card.driver", value = "USB-Audio" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "sink" -streams = "playback.streams" -priority = 70 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/70-usb-audio-source.endpoint b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/70-usb-audio-source.endpoint deleted file mode 100644 index 505ae8d81..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/70-usb-audio-source.endpoint +++ /dev/null @@ -1,12 +0,0 @@ -[match-node] -priority = 70 -properties = [ - { name = "media.class", value = "Audio/Source" }, - { name = "api.alsa.card.driver", value = "USB-Audio" }, -] - -[endpoint] -type = "pw-audio-softdsp-endpoint" -direction = "source" -streams = "capture.streams" -priority = 70 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/bluealsa-input-audio.endpoint-link b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/bluealsa-input-audio.endpoint-link deleted file mode 100644 index b5753a102..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/bluealsa-input-audio.endpoint-link +++ /dev/null @@ -1,11 +0,0 @@ -[match-endpoint] -priority = 75 -direction = "sink" -name = "bluealsa*" -media_class = "Stream/Input/Audio" -properties = [ - { name = "bluealsa.profile", value = "sco" }, -] - -[endpoint-link] -keep = true diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/bluealsa-output-audio.endpoint-link b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/bluealsa-output-audio.endpoint-link deleted file mode 100644 index d1b3cec07..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/bluealsa-output-audio.endpoint-link +++ /dev/null @@ -1,11 +0,0 @@ -[match-endpoint] -priority = 75 -direction = "source" -name = "bluealsa*" -media_class = "Stream/Output/Audio" -properties = [ - { name = "bluealsa.profile", value = "sco" }, -] - -[endpoint-link] -keep = true diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/capture.streams b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/capture.streams deleted file mode 100644 index e7ce36f6a..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/capture.streams +++ /dev/null @@ -1,3 +0,0 @@ -[[streams]] -name = "Default" -priority = 25 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/playback.streams b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/playback.streams deleted file mode 100644 index c645416ad..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/playback.streams +++ /dev/null @@ -1,31 +0,0 @@ -[[streams]] -name = "Multimedia" -priority = 25 - -[[streams]] -name = "Speech-Low" -priority = 30 - -[[streams]] -name = "Custom-Low" -priority = 35 - -[[streams]] -name = "Navigation" -priority = 50 - -[[streams]] -name = "Speech-High" -priority = 60 - -[[streams]] -name = "Custom-High" -priority = 65 - -[[streams]] -name = "Communication" -priority = 75 - -[[streams]] -name = "Emergency" -priority = 99 diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/wireplumber.conf b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/wireplumber.conf deleted file mode 100644 index e0975a81f..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl/wireplumber.conf +++ /dev/null @@ -1,30 +0,0 @@ -# Register well-known SPA factories -# These do not need to exist on the system to be registered -add-spa-lib audio.convert* audioconvert/libspa-audioconvert -add-spa-lib api.alsa.* alsa/libspa-alsa -add-spa-lib api.v4l2.* v4l2/libspa-v4l2 -add-spa-lib api.bluez5.* bluez5/libspa-bluez5 - -# the client-device pipewire module is needed for libwireplumber-module-monitor -load-pipewire-module libpipewire-module-client-device - -# Session object implementation -# This keeps track of the default input & output device endpoint -load-module C libwireplumber-module-session - -# Basic pipewire integration - do not remove -load-module C libwireplumber-module-pipewire - -# Grants access to security confined clients -load-module C libwireplumber-module-client-permissions - -load-module C libwireplumber-module-monitor { - "factory": <"api.alsa.enum.udev">, - "flags": <["use-adapter", "activate-devices"]> -} - -# Implements endpoint creation based on TOML configuration files -load-module C libwireplumber-module-config-endpoint - -# Implements linking clients to devices based on TOML configuration files -load-module C libwireplumber-module-config-policy diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl_git.bb b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl_git.bb deleted file mode 100644 index 8b9a64594..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-board-config-agl_git.bb +++ /dev/null @@ -1,58 +0,0 @@ -SUMMARY = "AGL configuration file for wireplumber" -HOMEPAGE = "https://gitlab.freedesktop.org/gkiagia/wireplumber" -BUGTRACKER = "https://jira.automotivelinux.org" -AUTHOR = "George Kiagiadakis <george.kiagiadakis@collabora.com>" -SECTION = "multimedia" - -LICENSE = "MIT" -LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" - -SRC_URI = "\ - file://wireplumber.conf \ - file://00-audio-sink.endpoint \ - file://00-audio-source.endpoint \ - file://00-default-input-audio.endpoint-link \ - file://00-default-output-audio.endpoint-link \ - file://00-stream-input-audio.endpoint \ - file://00-stream-output-audio.endpoint \ - file://01-hw00-audio-sink.endpoint \ - file://01-hw00-audio-source.endpoint \ - file://30-ak4613-audio-sink.endpoint \ - file://30-ak4613-audio-source.endpoint \ - file://30-rcarsound-audio-sink.endpoint \ - file://30-rcarsound-audio-source.endpoint \ - file://30-dra7xx-audio-sink.endpoint \ - file://30-dra7xx-audio-source.endpoint \ - file://30-rpi3-audio-sink.endpoint \ - file://30-imx8mq-audio-sink.endpoint \ - file://40-fiberdyne-amp.endpoint \ - file://40-microchip-mic.endpoint \ - file://70-usb-audio-sink.endpoint \ - file://70-usb-audio-source.endpoint \ - file://bluealsa-input-audio.endpoint-link \ - file://bluealsa-output-audio.endpoint-link \ - file://capture.streams \ - file://playback.streams \ -" - -PACKAGE_ARCH = "${MACHINE_ARCH}" - -do_configure[noexec] = "1" -do_compile[noexec] = "1" - -do_install_append() { - install -d ${D}/${sysconfdir}/wireplumber/ - install -m 644 ${WORKDIR}/wireplumber.conf ${D}/${sysconfdir}/wireplumber/wireplumber.conf - install -m 644 ${WORKDIR}/*.endpoint ${D}/${sysconfdir}/wireplumber/ - install -m 644 ${WORKDIR}/*.endpoint-link ${D}/${sysconfdir}/wireplumber/ - install -m 644 ${WORKDIR}/*.streams ${D}/${sysconfdir}/wireplumber/ -} - -FILES_${PN} += "\ - ${sysconfdir}/wireplumber/* \ -" -CONFFILES_${PN} += "\ - ${sysconfdir}/wireplumber/* \ -" - -RPROVIDES_${PN} += "virtual/wireplumber-config" diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/00-functions.lua b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/00-functions.lua new file mode 100644 index 000000000..7e1794df0 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/00-functions.lua @@ -0,0 +1,27 @@ +components = {} + +function load_module(m) + if not components[m] then + components[m] = { "libwireplumber-module-" .. m, type = "module" } + end +end + +function load_pw_module(m) + if not components[m] then + components[m] = { "libpipewire-module-" .. m, type = "pw_module" } + end +end + +function load_script(s, a) + if not components[s] then + components[s] = { s, type = "script/lua", args = a } + end +end + +function load_monitor(s, a) + load_script("monitors/" .. s .. ".lua", a) +end + +function load_access(s, a) + load_script("access/access-" .. s .. ".lua", a) +end diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/00-spa-libs.lua b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/00-spa-libs.lua new file mode 100644 index 000000000..f284b92e7 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/00-spa-libs.lua @@ -0,0 +1,10 @@ +-- ["<factory-name regex>"] = "<library-name>" +-- +-- used to find spa factory names. It maps a spa factory name +-- regular expression to a library name that should contain that factory. +-- +spa_libs = { + ["api.alsa.*"] = "alsa/libspa-alsa", + ["api.v4l2.*"] = "v4l2/libspa-v4l2", + ["api.bluez5.*"] = "bluez5/libspa-bluez5", +} diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/10-default-policy.lua b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/10-default-policy.lua new file mode 100644 index 000000000..3c04652f0 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/10-default-policy.lua @@ -0,0 +1,124 @@ +-- Default policy config file -- + +default_policy = {} + +default_policy.sessions = { + -- [session name] = { session properties } + ["audio"] = { ["media.type"] = "Audio" }, + --["video"] = { ["media.type"] = "Video" }, +} + +default_policy.endpoints = { + -- [endpoint name] = { endpoint properties } + + ["endpoint.multimedia"] = { + ["media.class"] = "Audio/Sink", + ["role"] = "Multimedia", + }, + ["endpoint.speech_low"] = { + ["media.class"] = "Audio/Sink", + ["role"] = "Speech-Low", + }, + ["endpoint.custom_low"] = { + ["media.class"] = "Audio/Sink", + ["role"] = "Custom-Low", + }, + ["endpoint.navigation"] = { + ["media.class"] = "Audio/Sink", + ["role"] = "Navigation", + }, + ["endpoint.speech_high"] = { + ["media.class"] = "Audio/Sink", + ["role"] = "Speech-High", + }, + ["endpoint.custom_high"] = { + ["media.class"] = "Audio/Sink", + ["role"] = "Custom-High", + }, + ["endpoint.communication"] = { + ["media.class"] = "Audio/Sink", + ["role"] = "Communication", + }, + ["endpoint.emergency"] = { + ["media.class"] = "Audio/Sink", + ["role"] = "Emergency", + }, +} + +default_policy.policy = { + ["move"] = false, -- moves session items when metadata target.node changes + ["follow"] = true, -- moves session items to the default device when it has changed + + ["roles"] = { + ["Multimedia"] = { + ["alias"] = { "Movie", "Music", "Game" }, + ["priority"] = 25, + ["action.default"] = "cork", + }, + ["Speech-Low"] = { + ["priority"] = 30, + ["action.default"] = "cork", + ["action.Speech-Low"] = "mix", + }, + ["Custom-Low"] = { + ["priority"] = 35, + ["action.default"] = "cork", + ["action.Custom-Low"] = "mix", + }, + ["Navigation"] = { + ["priority"] = 50, + ["action.default"] = "cork", + ["action.Navigation"] = "mix", + }, + ["Speech-High"] = { + ["priority"] = 60, + ["action.default"] = "cork", + ["action.Speech-High"] = "mix", + }, + ["Custom-High"] = { + ["priority"] = 65, + ["action.default"] = "cork", + ["action.Custom-High"] = "mix", + }, + ["Communication"] = { + ["priority"] = 75, + ["action.default"] = "cork", + ["action.Communication"] = "mix", + }, + ["Emergency"] = { + ["alias"] = { "Alert" }, + ["priority"] = 99, + ["action.default"] = "cork", + ["action.Emergency"] = "mix", + }, + }, +} + +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") + + -- Create sessions statically at startup + load_script("static-sessions.lua", default_policy.sessions) + + + -- Create endpoints statically at startup + load_script("static-endpoints.lua", default_policy.endpoints) + + -- Create session 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 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 endpoints with device nodes to make media flow in the graph + load_script("policy-endpoint-device.lua", default_policy.policy) +end diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/30-alsa-monitor.lua b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/30-alsa-monitor.lua new file mode 100644 index 000000000..fd6b7ecb5 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/30-alsa-monitor.lua @@ -0,0 +1,161 @@ +-- ALSA monitor config file -- + +alsa_monitor = {} + +alsa_monitor.properties = { + ["alsa.jack-device"] = false, + ["alsa.reserve"] = false, +} + +alsa_monitor.rules = { + -- disable ACP (PulseAudio-like profiles) + { + matches = { + { + { "device.name", "matches", "alsa_card.*" }, + }, + }, + apply_properties = { + ["api.alsa.use-acp"] = false, + }, + }, + + -- + -- Bump priority of well-known output devices + -- Higher priority means it gets selected as the default if it's present + -- + + -- USB card + { + matches = { + { + { "node.name", "matches", "alsa_output.*" }, + { "api.alsa.card.driver", "=", "USB-Audio" }, + }, + }, + apply_properties = { + ["priority.driver"] = 1300, + ["priority.session"] = 1300, + } + }, + + -- fiberdyne amp + { + matches = { + { + { "node.name", "matches", "alsa_output.*" }, + { "api.alsa.card.id", "=", "ep016ch" }, + }, + }, + apply_properties = { + ["priority.driver"] = 1200, + ["priority.session"] = 1200, + } + }, + + -- well-known internal devices + { + matches = { + { + -- ak4613 + { "node.name", "matches", "alsa_output.*" }, + { "api.alsa.card.id", "=", "ak4613" }, + { "api.alsa.pcm.device", "=", "0" }, + }, + { + -- dra7xx + { "node.name", "matches", "alsa_output.*" }, + { "api.alsa.card.id", "=", "DRA7xx-EVM" }, + }, + { + -- imx8mq + { "node.name", "matches", "alsa_output.*" }, + { "api.alsa.card.id", "=", "wm8524audio" }, + }, + { + -- rcarsound + { "node.name", "matches", "alsa_output.*" }, + { "api.alsa.card.id", "=", "rcarsound" }, + { "api.alsa.pcm.device", "=", "0" }, + }, + { + -- rpi3 + { "node.name", "matches", "alsa_output.*" }, + { "api.alsa.pcm.name", "=", "bcm2835 ALSA" }, + }, + }, + apply_properties = { + ["priority.driver"] = 1100, + ["priority.session"] = 1100, + } + }, + + -- + -- Same for input devices + -- + + -- USB card + { + matches = { + { + { "node.name", "matches", "alsa_input.*" }, + { "api.alsa.card.driver", "=", "USB-Audio" }, + }, + }, + apply_properties = { + ["priority.driver"] = 2300, + ["priority.session"] = 2300, + } + }, + + -- microchip mic + { + matches = { + { + { "node.name", "matches", "alsa_input.*" }, + { "api.alsa.card.id", "=", "ep811ch" }, + }, + }, + apply_properties = { + ["priority.driver"] = 2200, + ["priority.session"] = 2200, + } + }, + + -- well-known internal devices + { + matches = { + { + -- ak4613 + { "node.name", "matches", "alsa_input.*" }, + { "api.alsa.card.id", "=", "ak4613" }, + }, + { + -- dra7xx + { "node.name", "matches", "alsa_input.*" }, + { "api.alsa.card.id", "=", "DRA7xx-EVM" }, + }, + { + -- imx8mq + { "node.name", "matches", "alsa_input.*" }, + { "api.alsa.card.id", "=", "wm8524audio" }, + }, + { + -- rcarsound + { "node.name", "matches", "alsa_input.*" }, + { "api.alsa.card.id", "=", "rcarsound" }, + }, + }, + apply_properties = { + ["priority.driver"] = 2100, + ["priority.session"] = 2100, + } + }, +} + +function alsa_monitor.enable() + load_monitor("alsa", { + properties = alsa_monitor.properties, + rules = alsa_monitor.rules, + }) +end diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/30-bluez-monitor.lua b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/30-bluez-monitor.lua new file mode 100644 index 000000000..fab9ac0e4 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/30-bluez-monitor.lua @@ -0,0 +1,109 @@ +-- Bluez monitor config file -- + +bluez_monitor = {} + +bluez_monitor.properties = { + -- Enable mSBC support, disabled by default. Be aware that + -- mSBC is not expected to work on all headset + adapter combinations. + -- This can be overloaded for a specific device and native backend + -- in rules section. + --["bluez5.msbc-support"] = false, + + --["bluez5.sbc-xq-support"] = true, + + -- Enabled headset roles (default: [ hsp_hs hfp_ag ]), this + -- property only applies to native backend. Currently some headsets + -- (Sony WH-1000XM3) are not working with both hsp_ag and hfp_ag + -- enabled, disable either hsp_ag or hfp_ag to work around it. + -- + -- Supported headset roles: hsp_hs (HSP Headset), + -- hsp_ag (HSP Audio Gateway), + -- hfp_hf (HFP Hands-Free), + -- hfp_ag (HFP Audio Gateway) + ["bluez5.headset-roles"] = "[ hsp_hs hsp_ag hfp_hf hfp_ag ]", + + -- Enabled A2DP codecs (default: all). + --["bluez5.codecs"] = "[ sbc aac ldac aptx aptx_hd ]", + + -- Properties for the A2DP codec configuration + --["bluez5.default.rate"] = 48000, + --["bluez5.default.channels"] = 2, +} + +bluez_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", "bluez_card.*" }, + }, + }, + -- Apply properties on the matched object. + apply_properties = { + -- Auto-connect device profiles on start up or when only partial + -- profiles have connected. Disabled by default if the property + -- is not specified. + --["bluez5.auto-connect"] = "[ hfp_hf hsp_hs a2dp_sink hfp_ag hsp_ag a2dp_source ]", + ["bluez5.auto-connect"] = "[ hfp_hf hsp_hs a2dp_sink ]", + + -- Overload mSBC support for native backend and a specific device. + --["bluez5.msbc-support"] = false, + + -- Hardware volume control (default: [ hfp_ag hsp_ag a2dp_source ]) + --["bluez5.hw-volume"] = "[ hfp_hf hsp_hs a2dp_sink hfp_ag hsp_ag a2dp_source ]", + + -- LDAC encoding quality + -- Available values: auto (Adaptive Bitrate, default) + -- hq (High Quality, 990/909kbps) + -- sq (Standard Quality, 660/606kbps) + -- mq (Mobile use Quality, 330/303kbps) + --["bluez5.a2dp.ldac.quality"] = "auto", + + -- AAC variable bitrate mode + -- Available values: 0 (cbr, default), 1-5 (quality level) + --["bluez5.a2dp.aac.bitratemode"] = 0, + + -- Profile connected first + -- Available values: a2dp-sink (default), headset-head-unit + --["device.profile"] = "a2dp-sink", + }, + }, + { + -- Make output hsp/hfp stream nodes go through the Communication endpoint + -- Unfortunately we cannot match on "media.class" because this property + -- is not known before the node is created + matches = { + { + { "api.bluez5.profile", "equals", "headset-audio-gateway" }, + { "factory.name", "matches", "*source*" }, + }, + }, + apply_properties = { + ["media.role"] = "Communication", + }, + }, + { + -- Make output a2dp stream nodes go through the Multimedia endpoint + -- Unfortunately we cannot match on "media.class" because this property + -- is not known before the node is created + matches = { + { + { "api.bluez5.profile", "equals", "a2dp-source" }, + }, + }, + apply_properties = { + ["media.role"] = "Multimedia", + }, + }, +} + +function bluez_monitor.enable() + load_monitor("bluez", { + properties = bluez_monitor.properties, + rules = bluez_monitor.rules, + }) +end 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 new file mode 100644 index 000000000..3e2c28a3a --- /dev/null +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/99-load-modules.lua @@ -0,0 +1,22 @@ +-- 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, +}) +load_module("default-nodes-api") + +-- 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/wireplumber-bluetooth.conf b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/wireplumber-bluetooth.conf new file mode 100644 index 000000000..950e6a5d2 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/wireplumber-bluetooth.conf @@ -0,0 +1,13 @@ +<!-- This configuration file specifies the required security policies + for wireplumber to talk to system bluetooth-related services. --> + +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + + <policy user="pipewire"> + <allow send_destination="org.bluez"/> + <allow send_destination="org.ofono"/> + </policy> + +</busconfig> diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/wireplumber.conf b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/wireplumber.conf new file mode 100644 index 000000000..6f9b0b186 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl/wireplumber.conf @@ -0,0 +1,74 @@ +# WirePlumber daemon context configuration # + +context.properties = { + ## Properties to configure the PipeWire context and some modules + + # 1=error/critical, 2=warning, 3=info, 4=debug, 5=trace + log.level = 2 + + wireplumber.script-engine = lua-scripting +} + +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.alsa.* = alsa/libspa-alsa + api.bluez5.* = bluez5/libspa-bluez5 + api.v4l2.* = v4l2/libspa-v4l2 + api.libcamera.* = libcamera/libspa-libcamera + 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(s) + # Other components are loaded from there + { name = config.lua, type = config/lua } +] diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl_git.bb b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl_git.bb new file mode 100644 index 000000000..86d77257d --- /dev/null +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber-config-agl_git.bb @@ -0,0 +1,46 @@ +SUMMARY = "AGL configuration file for wireplumber" +HOMEPAGE = "https://gitlab.freedesktop.org/gkiagia/wireplumber" +BUGTRACKER = "https://jira.automotivelinux.org" +AUTHOR = "George Kiagiadakis <george.kiagiadakis@collabora.com>" +SECTION = "multimedia" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" +SRC_URI = "\ + file://00-functions.lua \ + file://00-spa-libs.lua \ + file://10-default-policy.lua \ + file://30-alsa-monitor.lua \ + file://30-bluez-monitor.lua \ + file://99-load-modules.lua \ + file://wireplumber.conf \ + file://wireplumber-bluetooth.conf \ +" +PACKAGE_ARCH = "${MACHINE_ARCH}" + +do_configure[noexec] = "1" +do_compile[noexec] = "1" +do_install_append() { + config_dir="${D}${sysconfdir}/wireplumber/config.lua.d/" + dbus_config_dir="${D}${sysconfdir}/dbus-1/system.d/" + + install -d ${config_dir} + install -m 0644 ${WORKDIR}/00-functions.lua ${config_dir} + install -m 0644 ${WORKDIR}/00-spa-libs.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/ + + install -d ${dbus_config_dir} + install -m 0644 ${WORKDIR}/wireplumber-bluetooth.conf ${dbus_config_dir} +} + +FILES_${PN} += "\ + ${sysconfdir}/wireplumber/* \ +" +CONFFILES_${PN} += "\ + ${sysconfdir}/wireplumber/* \ +" +RPROVIDES_${PN} += "virtual/wireplumber-config" diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber/0001-Build-cpptoml-without-a-cmake-subproject.patch b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber/0001-Build-cpptoml-without-a-cmake-subproject.patch deleted file mode 100644 index 726b35e74..000000000 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber/0001-Build-cpptoml-without-a-cmake-subproject.patch +++ /dev/null @@ -1,28 +0,0 @@ -From e5efe3d4f0abc28251dac245ce0cf4124e7e2a12 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Thu, 5 Dec 2019 17:59:44 +0200 -Subject: [PATCH] Build cpptoml without a cmake subproject - -Upstream-Status: Inappropriate ---- - meson.build | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/meson.build b/meson.build -index 5a75d96..0b21377 100644 ---- a/meson.build -+++ b/meson.build -@@ -24,9 +24,7 @@ else - wireplumber_config_dir = join_paths(get_option('prefix'), get_option('sysconfdir'), 'wireplumber') - endif - --cmake = import('cmake') --cpptoml = cmake.subproject('cpptoml') --cpptoml_dep = cpptoml.dependency('cpptoml') -+cpptoml_dep = declare_dependency(include_directories: include_directories('subprojects/cpptoml')) - - gobject_dep = dependency('gobject-2.0') - gmodule_dep = dependency('gmodule-2.0') --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber_git.bb b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber_git.bb index 0e810b375..56065db48 100644 --- a/meta-pipewire/recipes-multimedia/wireplumber/wireplumber_git.bb +++ b/meta-pipewire/recipes-multimedia/wireplumber/wireplumber_git.bb @@ -7,34 +7,62 @@ SECTION = "multimedia" LICENSE = "MIT" LIC_FILES_CHKSUM = "file://LICENSE;beginline=3;md5=e8ad01a5182f2c1b3a2640e9ea268264" -inherit meson pkgconfig gobject-introspection +inherit meson pkgconfig systemd -DEPENDS = "glib-2.0 glib-2.0-native pipewire" +DEPENDS = "glib-2.0 glib-2.0-native pipewire lua" SRC_URI = "\ git://gitlab.freedesktop.org/pipewire/wireplumber.git;protocol=https;branch=master \ - https://raw.githubusercontent.com/skystrife/cpptoml/fededad7169e538ca47e11a9ee9251bc361a9a65/include/cpptoml.h \ - file://0001-Build-cpptoml-without-a-cmake-subproject.patch \ " -SRCREV = "0e98e4150b73d0bed9b72bf8d3eba49671962991" -SRC_URI[sha256sum] = "3e4e1d315fa1229921c7a4297ead08775b5bb1220c18a7eac62db9ca7e79df0d" +SRCREV = "ecef960b7859b9b24885840453a3ddf4812845f2" -PV = "0.1.90+git${SRCPV}" +#PV = "0.3.95+git${SRCPV}" +PV = "0.3.95" S = "${WORKDIR}/git" +WPAPI="0.4" + +# use shared lua from the system instead of the static bundled one +EXTRA_OEMESON += "-Dsystem-lua=true" + +# introspection in practice is only used for generating API docs +# API docs are available on the website and we don't need to build them +# (plus they depend on hotdoc which is not available here) +EXTRA_OEMESON += "-Dintrospection=disabled -Ddoc=disabled" + +PACKAGECONFIG = "\ + ${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)} \ +" + +PACKAGECONFIG[systemd] = "-Dsystemd=enabled -Dsystemd-system-service=true -Dsystemd-user-service=false,-Dsystemd=disabled -Dsystemd-system-service=false -Dsystemd-user-service=false,systemd" + do_configure_prepend() { - mkdir -p ${WORKDIR}/git/subprojects/cpptoml/include - cp -f ${WORKDIR}/cpptoml.h ${WORKDIR}/git/subprojects/cpptoml/include/ + # relax meson version requirement + # we only need 0.54 when building with -Dsystem-lua=false + sed "s/meson_version : '>= 0.54.0'/meson_version : '>= 0.51.0'/" ${S}/meson.build > ${S}/tmp.build + mv -f ${S}/tmp.build ${S}/meson.build } -PACKAGES =+ "${PN}-config" +PACKAGES =+ "\ + lib${PN}-${WPAPI} \ + ${PN}-config \ +" -FILES_${PN} += "\ - ${libdir}/wireplumber-*/* \ +SYSTEMD_SERVICE_${PN} = "wireplumber.service" +FILES_${PN} = "\ + ${bindir}/wireplumber \ + ${bindir}/wpctl \ + ${bindir}/wpexec \ + ${libdir}/wireplumber-${WPAPI}/* \ + ${datadir}/wireplumber/* \ + ${systemd_system_unitdir}/* \ " RPROVIDES_${PN} += "virtual/pipewire-sessionmanager" RDEPENDS_${PN} += "virtual/wireplumber-config" +FILES_lib${PN}-${WPAPI} = "\ + ${libdir}/libwireplumber-${WPAPI}.so.* \ +" FILES_${PN}-config += "\ ${sysconfdir}/wireplumber/* \ @@ -42,5 +70,4 @@ FILES_${PN}-config += "\ CONFFILES_${PN}-config += "\ ${sysconfdir}/wireplumber/* \ " - RPROVIDES_${PN}-config += "virtual/wireplumber-config" |