Rename endpoint concept to virtual session item
This commit is contained in:
@@ -389,49 +389,51 @@ files and are placed under ``wireplumber.conf.d/``. More on this below.
|
||||
* wp_settings_apply_rule () is WpSettings API for rules.
|
||||
|
||||
|
||||
* *wireplumber.endpoints*
|
||||
* *wireplumber.virtuals*
|
||||
|
||||
Endpoints are a way of grouping different kinds of clients or
|
||||
Virtual session items are a way of grouping different kinds of clients or
|
||||
applications(for example Music, Voice, Navigation, Gaming etc).
|
||||
The actual grouping is done based on the `media.role` of the client
|
||||
stream node.
|
||||
|
||||
Endpoints allows for that actions to be taken up at group level rather than
|
||||
at individual stream level, which can be cumbersome.
|
||||
Virtual session items allows for that actions to be taken up at group level
|
||||
rather than at individual stream level, which can be cumbersome.
|
||||
|
||||
For example imagine the following scenarios.
|
||||
* Incoming Navigation message needs to duck the volume of
|
||||
Audio playback(all the apps playing audio).
|
||||
* Incoming voice/voip call needs to stop(cork) the Audio playback.
|
||||
|
||||
Endpoints realize this functionality with ease.
|
||||
Virtual session items realize this functionality with ease.
|
||||
|
||||
* *Defining Endpoints*
|
||||
* *Defining Virtual session items*
|
||||
|
||||
Example::
|
||||
|
||||
endpoints = {
|
||||
endpoint.capture = {
|
||||
virtual-items = {
|
||||
virtual-item.capture = {
|
||||
media.class = "Audio/Source"
|
||||
role = "Capture"
|
||||
}
|
||||
endpoint.multimedia = {
|
||||
virtual-item.multimedia = {
|
||||
media.class = "Audio/Sink"
|
||||
role = "Multimedia"
|
||||
}
|
||||
endpoint.navigation = {
|
||||
virtual-item.navigation = {
|
||||
media.class = "Audio/Sink"
|
||||
role = "Navigation"
|
||||
}
|
||||
|
||||
This example creates 3 endpoints, with names ``endpoint.capture``,
|
||||
``endpoint.multimedia`` and ``endpoint.navigation`` and assigned roles
|
||||
``Capture``, ``Multimedia`` and ``Navigation`` respectively.
|
||||
This example creates 3 virtual session items, with names
|
||||
``virtual-item.capture``, ``virtual-item.multimedia`` and
|
||||
``virtual-item.navigation`` and assigned roles ``Capture``, ``Multimedia``
|
||||
and ``Navigation`` respectively.
|
||||
|
||||
First end point has a media class of ``Audio/Source`` used for capture and rest of the endpoints have ``Audio/Sink`` media class, and so are only
|
||||
used for playback.
|
||||
First virtual item has a media class of ``Audio/Source`` used for capture
|
||||
and rest of the virtual items have ``Audio/Sink`` media class, and so are
|
||||
only used for playback.
|
||||
|
||||
* *Endpoints config*
|
||||
* *Virtual session items config*
|
||||
|
||||
Example::
|
||||
|
||||
@@ -456,15 +458,15 @@ files and are placed under ``wireplumber.conf.d/``. More on this below.
|
||||
|
||||
The above example defines actions for both ``Multimedia`` and ``Navigation``
|
||||
roles. Since the Navigation role has more priority than the Multimedia
|
||||
role, when a client connects to the Navigation endpoint, it will ``duck``
|
||||
the volume of all Multimedia clients. If Multiple Navigation clients want
|
||||
to play audio, their audio will be mixed.
|
||||
role, when a client connects to the Navigation virtual session item, it
|
||||
will ``duck`` the volume of all Multimedia clients. If Multiple Navigation
|
||||
clients want to play audio, their audio will be mixed.
|
||||
|
||||
Possible values of actions are: ``mix`` (Mixes audio),
|
||||
``duck`` (Mixes and lowers the audio volume) or ``cork`` (Pauses audio).
|
||||
|
||||
Endpoints are not used for desktop use cases, it is more suitable for
|
||||
embedded use cases.
|
||||
Virtual session items are not used for desktop use cases, it is more suitable
|
||||
for embedded use cases.
|
||||
|
||||
* *Split Configuration files*
|
||||
|
||||
|
@@ -62,16 +62,16 @@ Native API clients
|
||||
pw-cat
|
||||
^^^^^^
|
||||
|
||||
Using the default endpoint:
|
||||
Using the default device:
|
||||
|
||||
.. code:: console
|
||||
|
||||
$ wpctl status # verify the default endpoints
|
||||
$ wpctl status # verify the default device
|
||||
$ pw-record test.wav
|
||||
$ pw-play test.wav
|
||||
|
||||
|
||||
Using a non-default endpoint:
|
||||
Using a non-default device:
|
||||
|
||||
.. code:: console
|
||||
|
||||
@@ -84,19 +84,18 @@ or
|
||||
|
||||
.. code:: console
|
||||
|
||||
$ wpctl status # find the capture & playback endpoint ids
|
||||
$ pw-record --target <endpoint_id> test.wav
|
||||
$ pw-play --target <endpoint_id> test.wav
|
||||
$ wpctl status # find the capture & playback node ids
|
||||
$ pw-record --target <node_id> test.wav
|
||||
$ pw-play --target <node_id> test.wav
|
||||
|
||||
.. note::
|
||||
|
||||
node ids and endpoint ids can be used interchangeably when specifying
|
||||
targets in all use cases
|
||||
node ids can be used interchangeably when specifying targets in all use cases
|
||||
|
||||
video-play
|
||||
^^^^^^^^^^
|
||||
|
||||
Using the default endpoint:
|
||||
Using the default device:
|
||||
|
||||
.. code:: console
|
||||
|
||||
@@ -104,13 +103,13 @@ Using the default endpoint:
|
||||
$ ./build/src/examples/video-play
|
||||
|
||||
|
||||
Using a non-default endpoint:
|
||||
Using a non-default device:
|
||||
|
||||
.. code:: console
|
||||
|
||||
$ wpctl status # find the endpoint id from the list
|
||||
$ wpctl status # find the device node id from the list
|
||||
$ cd path/to/pipewire-source-dir
|
||||
$ ./build/src/examples/video-play <endpoint_id>
|
||||
$ ./build/src/examples/video-play <node_id>
|
||||
|
||||
.. tip::
|
||||
|
||||
@@ -123,11 +122,11 @@ PulseAudio compat API clients
|
||||
pacat
|
||||
^^^^^
|
||||
|
||||
Using the default endpoint:
|
||||
Using the default device:
|
||||
|
||||
.. code:: console
|
||||
|
||||
$ wpctl status # verify the default endpoints
|
||||
$ wpctl status # verify the default device
|
||||
$ parecord test.wav
|
||||
$ paplay test.wav
|
||||
|
||||
@@ -158,29 +157,29 @@ aplay / arecord
|
||||
``pipewire-alsa/conf/50-pipewire.conf`` in your ``~/.asoundrc``
|
||||
(or anywhere else, system-wide, where libasound can read it)
|
||||
|
||||
Using the default endpoint:
|
||||
Using the default device:
|
||||
|
||||
.. code:: console
|
||||
|
||||
$ wpctl status # verify the default endpoints
|
||||
$ wpctl status # verify the default devices
|
||||
$ arecord -D pipewire -f S16_LE -r 48000 test.wav
|
||||
$ aplay -D pipewire test.wav
|
||||
|
||||
Using a non-default endpoint:
|
||||
Using a non-default device:
|
||||
|
||||
.. code:: console
|
||||
|
||||
$ wpctl status # find the capture & playback endpoint ids
|
||||
$ PIPEWIRE_NODE=<endpoint_id> arecord -D pipewire -f S16_LE -r 48000 test.wav
|
||||
$ PIPEWIRE_NODE=<endpoint_id> aplay -D pipewire test.wav
|
||||
$ wpctl status # find the capture & playback node ids
|
||||
$ PIPEWIRE_NODE=<node_id> arecord -D pipewire -f S16_LE -r 48000 test.wav
|
||||
$ PIPEWIRE_NODE=<node_id> aplay -D pipewire test.wav
|
||||
|
||||
or
|
||||
|
||||
.. code:: console
|
||||
|
||||
$ wpctl status # find the capture & playback endpoint ids
|
||||
$ arecord -D pipewire:NODE=<endpoint_id> -f S16_LE -r 48000 test.wav
|
||||
$ aplay -D pipewire:NODE=<endpoint_id> test.wav
|
||||
$ wpctl status # find the capture & playback device node ids
|
||||
$ arecord -D pipewire:NODE=<node_id> -f S16_LE -r 48000 test.wav
|
||||
$ aplay -D pipewire:NODE=<node_id> test.wav
|
||||
|
||||
|
||||
JACK compat API clients
|
||||
@@ -201,15 +200,14 @@ jack_simple_client
|
||||
|
||||
.. code:: console
|
||||
|
||||
$ wpctl status # find the target endpoint id
|
||||
$ wpctl inspect <endpoint_id> # find the node.id
|
||||
$ wpctl status # find the target device node id
|
||||
$ wpctl inspect <node_id> # find the node.id
|
||||
$ PIPEWIRE_NODE=<node_id> pw-jack jack_simple_client
|
||||
|
||||
.. note::
|
||||
|
||||
The JACK layer is not controlled by the session manager, it creates its own
|
||||
links; which is why it is required to specify a node id (endpoint id will not
|
||||
work)
|
||||
links; which is why it is required to specify a node id.
|
||||
|
||||
Device Reservation
|
||||
------------------
|
||||
@@ -263,7 +261,7 @@ with JACK
|
||||
3. Wait a few seconds and run ``wpctl status`` to inspect
|
||||
|
||||
- The devices taken by JACK should no longer be available
|
||||
- There should be two *JACK System* endpoints (sink & source)
|
||||
- There should be two *JACK System* nodes (sink & source)
|
||||
|
||||
4. Run an audio client on PipeWire (ex ``pw-play test.wav``)
|
||||
|
||||
@@ -277,7 +275,7 @@ with JACK
|
||||
6. Wait a few seconds and run ``wpctl status`` to inspect
|
||||
|
||||
- The devices that were release by JACK should again be available
|
||||
- There should be no *JACK System* endpoint
|
||||
- There should be no *JACK System* nodes
|
||||
|
||||
.. note::
|
||||
|
||||
|
@@ -76,11 +76,11 @@ shared_library(
|
||||
)
|
||||
|
||||
shared_library(
|
||||
'wireplumber-module-si-audio-endpoint',
|
||||
'wireplumber-module-si-audio-virtual',
|
||||
[
|
||||
'module-si-audio-endpoint.c',
|
||||
'module-si-audio-virtual.c',
|
||||
],
|
||||
c_args : [common_c_args, '-DG_LOG_DOMAIN="m-si-audio-endpoint"'],
|
||||
c_args : [common_c_args, '-DG_LOG_DOMAIN="m-si-audio-virtual"'],
|
||||
install : true,
|
||||
install_dir : wireplumber_module_dir,
|
||||
dependencies : [wp_dep, pipewire_dep],
|
||||
|
@@ -175,13 +175,6 @@ local Feature = {
|
||||
Node = {
|
||||
PORTS = (1 << 16),
|
||||
},
|
||||
Session = {
|
||||
ENDPOINTS = (1 << 16),
|
||||
LINKS = (1 << 17),
|
||||
},
|
||||
Endpoint = {
|
||||
STREAMS = (1 << 16),
|
||||
},
|
||||
Metadata = {
|
||||
DATA = (1 << 16),
|
||||
},
|
||||
|
@@ -12,9 +12,9 @@
|
||||
#include <spa/param/audio/raw.h>
|
||||
#include <spa/param/param.h>
|
||||
|
||||
#define SI_FACTORY_NAME "si-audio-endpoint"
|
||||
#define SI_FACTORY_NAME "si-audio-virtual"
|
||||
|
||||
struct _WpSiAudioEndpoint
|
||||
struct _WpSiAudioVirtual
|
||||
{
|
||||
WpSessionItem parent;
|
||||
|
||||
@@ -31,26 +31,26 @@ struct _WpSiAudioEndpoint
|
||||
WpSiAdapter *adapter;
|
||||
};
|
||||
|
||||
static void si_audio_endpoint_linkable_init (WpSiLinkableInterface * iface);
|
||||
static void si_audio_endpoint_adapter_init (WpSiAdapterInterface * iface);
|
||||
static void si_audio_virtual_linkable_init (WpSiLinkableInterface * iface);
|
||||
static void si_audio_virtual_adapter_init (WpSiAdapterInterface * iface);
|
||||
|
||||
G_DECLARE_FINAL_TYPE(WpSiAudioEndpoint, si_audio_endpoint, WP,
|
||||
SI_AUDIO_ENDPOINT, WpSessionItem)
|
||||
G_DEFINE_TYPE_WITH_CODE (WpSiAudioEndpoint, si_audio_endpoint,
|
||||
G_DECLARE_FINAL_TYPE(WpSiAudioVirtual, si_audio_virtual, WP,
|
||||
SI_AUDIO_VIRTUAL, WpSessionItem)
|
||||
G_DEFINE_TYPE_WITH_CODE (WpSiAudioVirtual, si_audio_virtual,
|
||||
WP_TYPE_SESSION_ITEM,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_SI_LINKABLE,
|
||||
si_audio_endpoint_linkable_init)
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_SI_ADAPTER, si_audio_endpoint_adapter_init))
|
||||
si_audio_virtual_linkable_init)
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_SI_ADAPTER, si_audio_virtual_adapter_init))
|
||||
|
||||
static void
|
||||
si_audio_endpoint_init (WpSiAudioEndpoint * self)
|
||||
si_audio_virtual_init (WpSiAudioVirtual * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
si_audio_endpoint_reset (WpSessionItem * item)
|
||||
si_audio_virtual_reset (WpSessionItem * item)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (item);
|
||||
|
||||
/* deactivate first */
|
||||
wp_object_deactivate (WP_OBJECT (self),
|
||||
@@ -64,18 +64,18 @@ si_audio_endpoint_reset (WpSessionItem * item)
|
||||
self->priority = 0;
|
||||
self->disable_dsp = FALSE;
|
||||
|
||||
WP_SESSION_ITEM_CLASS (si_audio_endpoint_parent_class)->reset (item);
|
||||
WP_SESSION_ITEM_CLASS (si_audio_virtual_parent_class)->reset (item);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
si_audio_endpoint_configure (WpSessionItem * item, WpProperties *p)
|
||||
si_audio_virtual_configure (WpSessionItem * item, WpProperties *p)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (item);
|
||||
g_autoptr (WpProperties) si_props = wp_properties_ensure_unique_owner (p);
|
||||
const gchar *str;
|
||||
|
||||
/* reset previous config */
|
||||
si_audio_endpoint_reset (item);
|
||||
si_audio_virtual_reset (item);
|
||||
|
||||
str = wp_properties_get (si_props, "name");
|
||||
if (!str)
|
||||
@@ -116,18 +116,18 @@ si_audio_endpoint_configure (WpSessionItem * item, WpProperties *p)
|
||||
}
|
||||
|
||||
static gpointer
|
||||
si_audio_endpoint_get_associated_proxy (WpSessionItem * item, GType proxy_type)
|
||||
si_audio_virtual_get_associated_proxy (WpSessionItem * item, GType proxy_type)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (item);
|
||||
|
||||
return wp_session_item_get_associated_proxy (
|
||||
WP_SESSION_ITEM (self->adapter), proxy_type);
|
||||
}
|
||||
|
||||
static void
|
||||
si_audio_endpoint_disable_active (WpSessionItem *si)
|
||||
si_audio_virtual_disable_active (WpSessionItem *si)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (si);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (si);
|
||||
|
||||
g_clear_object (&self->adapter);
|
||||
g_clear_object (&self->node);
|
||||
@@ -136,9 +136,9 @@ si_audio_endpoint_disable_active (WpSessionItem *si)
|
||||
}
|
||||
|
||||
static void
|
||||
si_audio_endpoint_disable_exported (WpSessionItem *si)
|
||||
si_audio_virtual_disable_exported (WpSessionItem *si)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (si);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (si);
|
||||
|
||||
wp_object_update_features (WP_OBJECT (self), 0,
|
||||
WP_SESSION_ITEM_FEATURE_EXPORTED);
|
||||
@@ -148,7 +148,7 @@ static void
|
||||
on_adapter_activate_done (WpObject * adapter, GAsyncResult * res,
|
||||
WpTransition * transition)
|
||||
{
|
||||
WpSiAudioEndpoint *self = wp_transition_get_source_object (transition);
|
||||
WpSiAudioVirtual *self = wp_transition_get_source_object (transition);
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
if (!wp_object_activate_finish (adapter, res, &error)) {
|
||||
@@ -163,7 +163,7 @@ on_adapter_activate_done (WpObject * adapter, GAsyncResult * res,
|
||||
static void
|
||||
on_adapter_port_state_changed (WpSiAdapter *item,
|
||||
WpSiAdapterPortsState old_state, WpSiAdapterPortsState new_state,
|
||||
WpSiAudioEndpoint *self)
|
||||
WpSiAudioVirtual *self)
|
||||
{
|
||||
g_signal_emit_by_name (self, "adapter-ports-state-changed", old_state,
|
||||
new_state);
|
||||
@@ -173,7 +173,7 @@ static void
|
||||
on_node_activate_done (WpObject * node, GAsyncResult * res,
|
||||
WpTransition * transition)
|
||||
{
|
||||
WpSiAudioEndpoint *self = wp_transition_get_source_object (transition);
|
||||
WpSiAudioVirtual *self = wp_transition_get_source_object (transition);
|
||||
g_autoptr (GError) error = NULL;
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
@@ -190,7 +190,7 @@ on_node_activate_done (WpObject * node, GAsyncResult * res,
|
||||
if (!self->adapter) {
|
||||
wp_transition_return_error (transition,
|
||||
g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT,
|
||||
"si-audio-endpoint: could not create si-audio-adapter"));
|
||||
"si-audio-virtual: could not create si-audio-adapter"));
|
||||
}
|
||||
|
||||
/* Forward adapter-ports-state-changed signal */
|
||||
@@ -210,7 +210,7 @@ on_node_activate_done (WpObject * node, GAsyncResult * res,
|
||||
g_steal_pointer (&props))) {
|
||||
wp_transition_return_error (transition,
|
||||
g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT,
|
||||
"si-audio-endpoint: could not configure si-audio-adapter"));
|
||||
"si-audio-virtual: could not configure si-audio-adapter"));
|
||||
}
|
||||
|
||||
/* activate adapter */
|
||||
@@ -219,12 +219,12 @@ on_node_activate_done (WpObject * node, GAsyncResult * res,
|
||||
}
|
||||
|
||||
static void
|
||||
si_audio_endpoint_enable_active (WpSessionItem *si, WpTransition *transition)
|
||||
si_audio_virtual_enable_active (WpSessionItem *si, WpTransition *transition)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (si);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (si);
|
||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
||||
g_autofree gchar *name = g_strdup_printf ("control.%s", self->name);
|
||||
g_autofree gchar *desc = g_strdup_printf ("%s %s Endpoint", self->role,
|
||||
g_autofree gchar *desc = g_strdup_printf ("%s %s Virtual", self->role,
|
||||
(self->direction == WP_DIRECTION_OUTPUT) ? "Capture" : "Playback");
|
||||
g_autofree gchar *media = g_strdup_printf ("Audio/%s",
|
||||
(self->direction == WP_DIRECTION_OUTPUT) ? "Source" : "Sink");
|
||||
@@ -232,7 +232,7 @@ si_audio_endpoint_enable_active (WpSessionItem *si, WpTransition *transition)
|
||||
if (!wp_session_item_is_configured (si)) {
|
||||
wp_transition_return_error (transition,
|
||||
g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT,
|
||||
"si-audio-endpoint: item is not configured"));
|
||||
"si-audio-virtual: item is not configured"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -244,12 +244,12 @@ si_audio_endpoint_enable_active (WpSessionItem *si, WpTransition *transition)
|
||||
PW_KEY_FACTORY_NAME, "support.null-audio-sink",
|
||||
PW_KEY_NODE_DESCRIPTION, desc,
|
||||
"monitor.channel-volumes", "true",
|
||||
"wireplumber.is-endpoint", "true",
|
||||
"wireplumber.is-virtual", "true",
|
||||
NULL));
|
||||
if (!self->node) {
|
||||
wp_transition_return_error (transition,
|
||||
g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT,
|
||||
"si-audio-endpoint: could not create null-audio-sink node"));
|
||||
"si-audio-virtual: could not create null-audio-sink node"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -260,84 +260,84 @@ si_audio_endpoint_enable_active (WpSessionItem *si, WpTransition *transition)
|
||||
}
|
||||
|
||||
static void
|
||||
si_audio_endpoint_enable_exported (WpSessionItem *si, WpTransition *transition)
|
||||
si_audio_virtual_enable_exported (WpSessionItem *si, WpTransition *transition)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (si);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (si);
|
||||
|
||||
wp_object_update_features (WP_OBJECT (self),
|
||||
WP_SESSION_ITEM_FEATURE_EXPORTED, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
si_audio_endpoint_class_init (WpSiAudioEndpointClass * klass)
|
||||
si_audio_virtual_class_init (WpSiAudioVirtualClass * klass)
|
||||
{
|
||||
WpSessionItemClass *si_class = (WpSessionItemClass *) klass;
|
||||
|
||||
si_class->reset = si_audio_endpoint_reset;
|
||||
si_class->configure = si_audio_endpoint_configure;
|
||||
si_class->get_associated_proxy = si_audio_endpoint_get_associated_proxy;
|
||||
si_class->disable_active = si_audio_endpoint_disable_active;
|
||||
si_class->disable_exported = si_audio_endpoint_disable_exported;
|
||||
si_class->enable_active = si_audio_endpoint_enable_active;
|
||||
si_class->enable_exported = si_audio_endpoint_enable_exported;
|
||||
si_class->reset = si_audio_virtual_reset;
|
||||
si_class->configure = si_audio_virtual_configure;
|
||||
si_class->get_associated_proxy = si_audio_virtual_get_associated_proxy;
|
||||
si_class->disable_active = si_audio_virtual_disable_active;
|
||||
si_class->disable_exported = si_audio_virtual_disable_exported;
|
||||
si_class->enable_active = si_audio_virtual_enable_active;
|
||||
si_class->enable_exported = si_audio_virtual_enable_exported;
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
si_audio_endpoint_get_ports (WpSiLinkable * item, const gchar * context)
|
||||
si_audio_virtual_get_ports (WpSiLinkable * item, const gchar * context)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (item);
|
||||
return wp_si_linkable_get_ports (WP_SI_LINKABLE (self->adapter), context);
|
||||
}
|
||||
|
||||
static void
|
||||
si_audio_endpoint_linkable_init (WpSiLinkableInterface * iface)
|
||||
si_audio_virtual_linkable_init (WpSiLinkableInterface * iface)
|
||||
{
|
||||
iface->get_ports = si_audio_endpoint_get_ports;
|
||||
iface->get_ports = si_audio_virtual_get_ports;
|
||||
}
|
||||
|
||||
static WpSiAdapterPortsState
|
||||
si_audio_endpoint_get_ports_state (WpSiAdapter * item)
|
||||
si_audio_virtual_get_ports_state (WpSiAdapter * item)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (item);
|
||||
return wp_si_adapter_get_ports_state (self->adapter);
|
||||
}
|
||||
|
||||
static WpSpaPod *
|
||||
si_audio_endpoint_get_ports_format (WpSiAdapter * item, const gchar **mode)
|
||||
si_audio_virtual_get_ports_format (WpSiAdapter * item, const gchar **mode)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (item);
|
||||
return wp_si_adapter_get_ports_format (self->adapter, mode);
|
||||
}
|
||||
|
||||
static void
|
||||
si_audio_endpoint_set_ports_format (WpSiAdapter * item, WpSpaPod *f,
|
||||
si_audio_virtual_set_ports_format (WpSiAdapter * item, WpSpaPod *f,
|
||||
const gchar *mode, GAsyncReadyCallback callback, gpointer data)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (item);
|
||||
wp_si_adapter_set_ports_format (self->adapter, f, mode, callback, data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
si_audio_endpoint_set_ports_format_finish (WpSiAdapter * item,
|
||||
si_audio_virtual_set_ports_format_finish (WpSiAdapter * item,
|
||||
GAsyncResult * res, GError ** error)
|
||||
{
|
||||
WpSiAudioEndpoint *self = WP_SI_AUDIO_ENDPOINT (item);
|
||||
WpSiAudioVirtual *self = WP_SI_AUDIO_VIRTUAL (item);
|
||||
return wp_si_adapter_set_ports_format_finish (self->adapter, res, error);
|
||||
}
|
||||
|
||||
static void
|
||||
si_audio_endpoint_adapter_init (WpSiAdapterInterface * iface)
|
||||
si_audio_virtual_adapter_init (WpSiAdapterInterface * iface)
|
||||
{
|
||||
iface->get_ports_state = si_audio_endpoint_get_ports_state;
|
||||
iface->get_ports_format = si_audio_endpoint_get_ports_format;
|
||||
iface->set_ports_format = si_audio_endpoint_set_ports_format;
|
||||
iface->set_ports_format_finish = si_audio_endpoint_set_ports_format_finish;
|
||||
iface->get_ports_state = si_audio_virtual_get_ports_state;
|
||||
iface->get_ports_format = si_audio_virtual_get_ports_format;
|
||||
iface->set_ports_format = si_audio_virtual_set_ports_format;
|
||||
iface->set_ports_format_finish = si_audio_virtual_set_ports_format_finish;
|
||||
}
|
||||
|
||||
WP_PLUGIN_EXPORT gboolean
|
||||
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
|
||||
{
|
||||
wp_si_factory_register (core, wp_si_factory_new_simple (SI_FACTORY_NAME,
|
||||
si_audio_endpoint_get_type ()));
|
||||
si_audio_virtual_get_type ()));
|
||||
return TRUE;
|
||||
}
|
@@ -550,10 +550,10 @@ configure_and_link_adapters (WpSiStandardLink *self, WpTransition *transition)
|
||||
in->is_device = !g_strcmp0 (str, "device");
|
||||
|
||||
str = wp_session_item_get_property (WP_SESSION_ITEM (out->si), "item.factory.name");
|
||||
out->is_device = (str && !g_strcmp0 (str, "si-audio-endpoint") && !in->is_device)
|
||||
out->is_device = (str && !g_strcmp0 (str, "si-audio-virtual") && !in->is_device)
|
||||
|| out->is_device;
|
||||
str = wp_session_item_get_property (WP_SESSION_ITEM (in->si), "item.factory.name");
|
||||
in->is_device = (str && !g_strcmp0 (str, "si-audio-endpoint") && !out->is_device)
|
||||
in->is_device = (str && !g_strcmp0 (str, "si-audio-virtual") && !out->is_device)
|
||||
|| in->is_device;
|
||||
|
||||
str = wp_session_item_get_property (WP_SESSION_ITEM (out->si), "stream.dont-remix");
|
||||
|
@@ -131,7 +131,7 @@ wireplumber.components = [
|
||||
}
|
||||
|
||||
{
|
||||
name = libwireplumber-module-si-audio-endpoint,
|
||||
name = libwireplumber-module-si-audio-virtual,
|
||||
type = module
|
||||
## default priority for session item modules
|
||||
priority = 130
|
||||
|
@@ -1,48 +1,48 @@
|
||||
## The WirePlumber endpoint configuration
|
||||
## The WirePlumber virtual item configuration
|
||||
|
||||
endpoints = {
|
||||
## The list of endpoints to create
|
||||
virtual-items = {
|
||||
## The list of virtual items to create
|
||||
|
||||
# endpoint.capture = {
|
||||
# virtual-item.capture = {
|
||||
# media.class = "Audio/Source"
|
||||
# role = "Capture"
|
||||
# }
|
||||
# endpoint.multimedia = {
|
||||
# virtual-item.multimedia = {
|
||||
# media.class = "Audio/Sink"
|
||||
# role = "Multimedia"
|
||||
# }
|
||||
# endpoint.speech_low = {
|
||||
# virtual-item.speech_low = {
|
||||
# media.class = "Audio/Sink"
|
||||
# role = "Speech-Low"
|
||||
# }
|
||||
# endpoint.custom_low = {
|
||||
# virtual-item.custom_low = {
|
||||
# media.class = "Audio/Sink"
|
||||
# role = "Custom-Low"
|
||||
# }
|
||||
# endpoint.navigation = {
|
||||
# virtual-item.navigation = {
|
||||
# media.class = "Audio/Sink"
|
||||
# role = "Navigation"
|
||||
# }
|
||||
# endpoint.speech_high = {
|
||||
# virtual-item.speech_high = {
|
||||
# media.class = "Audio/Sink"
|
||||
# role = "Speech-High"
|
||||
# }
|
||||
# endpoint.custom_high = {
|
||||
# virtual-item.custom_high = {
|
||||
# media.class = "Audio/Sink"
|
||||
# role = "Custom-High"
|
||||
# }
|
||||
# endpoint.communication = {
|
||||
# virtual-item.communication = {
|
||||
# media.class = "Audio/Sink"
|
||||
# role = "Communication"
|
||||
# }
|
||||
# endpoint.emergency = {
|
||||
# virtual-item.emergency = {
|
||||
# media.class = "Audio/Sink"
|
||||
# role = "Emergency"
|
||||
# }
|
||||
}
|
||||
|
||||
endpoint-roles = {
|
||||
## The list of endpoint roles to use
|
||||
virtual-item-roles = {
|
||||
## The list of virtual item roles to use
|
||||
|
||||
# Capture = {
|
||||
# alias = [ "Multimedia", "Music", "Voice", "Capture" ]
|
42
src/scripts/create-virtual-item.lua
Normal file
42
src/scripts/create-virtual-item.lua
Normal file
@@ -0,0 +1,42 @@
|
||||
-- WirePlumber
|
||||
--
|
||||
-- Copyright © 2021 Collabora Ltd.
|
||||
-- @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
--
|
||||
-- SPDX-License-Identifier: MIT
|
||||
|
||||
-- Receive script arguments from config.lua
|
||||
|
||||
local defaults = {}
|
||||
defaults.virtual_items = Json.Object {}
|
||||
|
||||
local config = {}
|
||||
config.virtual_items = Conf.get_section (
|
||||
"virtual-items", defaults.virtual_items):parse ()
|
||||
|
||||
function createVirtualItem (factory_name, properties)
|
||||
-- create virtual item
|
||||
local si_v = SessionItem ( factory_name )
|
||||
if not si_v then
|
||||
Log.warning (si_v, "could not create virtual item of type " .. factory_name)
|
||||
return
|
||||
end
|
||||
|
||||
-- configure virtual item
|
||||
if not si_v:configure(properties) then
|
||||
Log.warning(si_v, "failed to configure virtual item " .. properties.name)
|
||||
return
|
||||
end
|
||||
|
||||
-- activate and register virtual item
|
||||
si_v:activate (Features.ALL, function (item)
|
||||
item:register ()
|
||||
Log.info(item, "registered virtual item " .. properties.name)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
for name, properties in pairs(config.virtual_items) do
|
||||
properties["name"] = name
|
||||
createVirtualItem ("si-audio-virtual", properties)
|
||||
end
|
@@ -12,8 +12,8 @@ node_om = ObjectManager {
|
||||
Interest {
|
||||
type = "node",
|
||||
Constraint { "media.class", "matches", "Audio/Sink", type = "pw-global" },
|
||||
-- Do not consider endpoints created by WirePlumber
|
||||
Constraint { "wireplumber.is-endpoint", "!", true, type = "pw" },
|
||||
-- Do not consider virtual items created by WirePlumber
|
||||
Constraint { "wireplumber.is-virtual", "!", true, type = "pw" },
|
||||
-- or the fallback sink itself
|
||||
Constraint { "wireplumber.is-fallback", "!", true, type = "pw" },
|
||||
}
|
||||
|
@@ -22,8 +22,7 @@ function checkLinkable (si, om, handle_nonstreams)
|
||||
end
|
||||
|
||||
-- Determine if we can handle item by this policy
|
||||
if om:lookup { type = "SiEndpoint" } and
|
||||
si_props ["item.factory.name"] == "si-audio-adapter" then
|
||||
if si_props ["item.factory.name"] == "si-audio-virtual" then
|
||||
return false
|
||||
end
|
||||
|
||||
|
@@ -74,7 +74,7 @@ AsyncEventHook {
|
||||
EventInterest {
|
||||
Constraint { "event.type", "=", "node-added" },
|
||||
Constraint { "media.class", "#", "Audio/*", type = "pw-global" },
|
||||
Constraint { "wireplumber.is-endpoint", "-", type = "pw" },
|
||||
Constraint { "wireplumber.is-virtual", "-", type = "pw" },
|
||||
},
|
||||
},
|
||||
steps = {
|
||||
@@ -144,7 +144,7 @@ SimpleEventHook {
|
||||
EventInterest {
|
||||
Constraint { "event.type", "=", "node-removed" },
|
||||
Constraint { "media.class", "#", "Audio/*", type = "pw-global" },
|
||||
Constraint { "wireplumber.is-endpoint", "-", type = "pw" },
|
||||
Constraint { "wireplumber.is-virtual", "-", type = "pw" },
|
||||
},
|
||||
},
|
||||
execute = function (event)
|
||||
|
@@ -13,7 +13,7 @@ local config = {}
|
||||
config.duck_level = Conf.get_value_float ("wireplumber.settings",
|
||||
"policy.default.duck-level", defaults.duck_level)
|
||||
config.roles = Conf.get_section (
|
||||
"endpoint-roles", defaults.roles):parse ()
|
||||
"virtual-item-roles", defaults.roles):parse ()
|
||||
|
||||
function findRole(role)
|
||||
if role and not config.roles[role] then
|
||||
@@ -50,32 +50,38 @@ end
|
||||
function restoreVolume(role, media_class)
|
||||
if not mixer_api then return end
|
||||
|
||||
local ep = endpoints_om:lookup {
|
||||
local si_v = virtuals_om:lookup {
|
||||
Constraint { "media.role", "=", role, type = "pw" },
|
||||
Constraint { "media.class", "=", media_class, type = "pw" },
|
||||
}
|
||||
|
||||
if ep and ep.properties["node.id"] then
|
||||
Log.debug(ep, "restore role " .. role)
|
||||
mixer_api:call("set-volume", ep.properties["node.id"], {
|
||||
monitorVolume = 1.0,
|
||||
})
|
||||
if si_v then
|
||||
local n = si_v:get_associated_proxy ("node")
|
||||
if n then
|
||||
Log.debug(si_v, "restore role " .. role)
|
||||
mixer_api:call("set-volume", n["bound-id"], {
|
||||
monitorVolume = 1.0,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function duck(role, media_class)
|
||||
if not mixer_api then return end
|
||||
|
||||
local ep = endpoints_om:lookup {
|
||||
local si_v = virtuals_om:lookup {
|
||||
Constraint { "media.role", "=", role, type = "pw" },
|
||||
Constraint { "media.class", "=", media_class, type = "pw" },
|
||||
}
|
||||
|
||||
if ep and ep.properties["node.id"] then
|
||||
Log.debug(ep, "duck role " .. role)
|
||||
mixer_api:call("set-volume", ep.properties["node.id"], {
|
||||
monitorVolume = config.duck_level,
|
||||
})
|
||||
if si_v then
|
||||
local n = si_v:get_associated_proxy ("node")
|
||||
if n then
|
||||
Log.debug(si_v, "duck role " .. role)
|
||||
mixer_api:call("set-volume", n["bound-id"], {
|
||||
monitorVolume = config.duck_level,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -98,7 +104,7 @@ function rescan()
|
||||
["Video/Source"] = {},
|
||||
}
|
||||
|
||||
Log.info("Rescan endpoint links")
|
||||
Log.info("Rescan virtual links")
|
||||
|
||||
-- deactivate all links if suspend playback metadata is present
|
||||
local suspend = getSuspendPlaybackMetadata()
|
||||
@@ -193,7 +199,7 @@ end
|
||||
silinks_om = ObjectManager {
|
||||
Interest {
|
||||
type = "SiLink",
|
||||
Constraint { "is.policy.endpoint.client.link", "=", true },
|
||||
Constraint { "is.policy.virtual.client.link", "=", true },
|
||||
},
|
||||
}
|
||||
silinks_om:connect("objects-changed", maybeRescan)
|
||||
@@ -202,10 +208,12 @@ silinks_om:activate()
|
||||
-- enable ducking if mixer-api is loaded
|
||||
mixer_api = Plugin.find("mixer-api")
|
||||
if mixer_api then
|
||||
endpoints_om = ObjectManager {
|
||||
Interest { type = "endpoint" },
|
||||
virtuals_om = ObjectManager { Interest { type = "SiLinkable",
|
||||
Constraint {
|
||||
"item.factory.name", "=", "si-audio-virtual", type = "pw-global" },
|
||||
}
|
||||
}
|
||||
endpoints_om:activate()
|
||||
virtuals_om:activate()
|
||||
end
|
||||
|
||||
metadata_om = ObjectManager {
|
@@ -12,7 +12,7 @@ defaults.roles = Json.Object {}
|
||||
|
||||
local config = {}
|
||||
config.roles = Conf.get_section (
|
||||
"endpoint-roles", defaults.roles):parse ()
|
||||
"virtual-item-roles", defaults.roles):parse ()
|
||||
|
||||
local self = {}
|
||||
self.scanning = false
|
||||
@@ -73,7 +73,7 @@ function findRole(role, tmc)
|
||||
return role
|
||||
end
|
||||
|
||||
function findTargetEndpoint (node, media_class, role)
|
||||
function findTargetVirtual (node, media_class, role)
|
||||
local target_class_assoc = {
|
||||
["Stream/Input/Audio"] = "Audio/Source",
|
||||
["Stream/Output/Audio"] = "Audio/Sink",
|
||||
@@ -89,9 +89,9 @@ function findTargetEndpoint (node, media_class, role)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- find highest priority endpoint by role
|
||||
-- find highest priority virtual by role
|
||||
media_role = findRole(role, target_media_class)
|
||||
for si_target_ep in endpoints_om:iterate {
|
||||
for si_target_ep in virtuals_om:iterate {
|
||||
Constraint { "role", "=", media_role, type = "pw-global" },
|
||||
Constraint { "media.class", "=", target_media_class, type = "pw-global" },
|
||||
} do
|
||||
@@ -132,7 +132,7 @@ function createLink (si, si_target_ep)
|
||||
["in.item"] = in_item,
|
||||
["out.item.port.context"] = "output",
|
||||
["in.item.port.context"] = "input",
|
||||
["is.policy.endpoint.client.link"] = true,
|
||||
["is.policy.virtual.client.link"] = true,
|
||||
["media.role"] = target_ep_props["role"],
|
||||
["target.media.class"] = target_ep_props["media.class"],
|
||||
["item.plugged.usec"] = si_props["item.plugged.usec"],
|
||||
@@ -159,7 +159,7 @@ function checkLinkable (si)
|
||||
end
|
||||
|
||||
-- Determine if we can handle item by this policy
|
||||
if endpoints_om:get_n_objects () == 0 then
|
||||
if virtuals_om:get_n_objects () == 0 then
|
||||
Log.debug (si, "item won't be handled by this policy")
|
||||
return false
|
||||
end
|
||||
@@ -178,10 +178,10 @@ function handleLinkable (si)
|
||||
Log.info (si, "handling item " .. tostring(node.properties["node.name"]) ..
|
||||
" with role " .. media_role)
|
||||
|
||||
-- find proper target endpoint
|
||||
local si_target_ep = findTargetEndpoint (node, media_class, media_role)
|
||||
-- find proper target virtual
|
||||
local si_target_ep = findTargetVirtual (node, media_class, media_role)
|
||||
if not si_target_ep then
|
||||
Log.info (si, "... target endpoint not found")
|
||||
Log.info (si, "... target virtual not found")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -191,11 +191,11 @@ function handleLinkable (si)
|
||||
local in_id = tonumber(link.properties["in.item.id"])
|
||||
if out_id == si.id or in_id == si.id then
|
||||
local is_out = out_id == si.id and true or false
|
||||
for peer_ep in endpoints_om:iterate() do
|
||||
for peer_ep in virtuals_om:iterate() do
|
||||
if peer_ep.id == (is_out and in_id or out_id) then
|
||||
|
||||
if peer_ep.id == si_target_ep.id then
|
||||
Log.info (si, "... already linked to proper target endpoint")
|
||||
Log.info (si, "... already linked to proper target virtual")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -237,7 +237,11 @@ function unhandleLinkable (si)
|
||||
end
|
||||
end
|
||||
|
||||
endpoints_om = ObjectManager { Interest { type = "SiEndpoint" }}
|
||||
virtuals_om = ObjectManager { Interest { type = "SiLinkable",
|
||||
Constraint {
|
||||
"item.factory.name", "=", "si-audio-virtual", type = "pw-global" },
|
||||
}
|
||||
}
|
||||
linkables_om = ObjectManager { Interest { type = "SiLinkable",
|
||||
-- only handle si-audio-adapter and si-node
|
||||
Constraint {
|
||||
@@ -248,7 +252,7 @@ linkables_om = ObjectManager { Interest { type = "SiLinkable",
|
||||
}
|
||||
links_om = ObjectManager { Interest { type = "SiLink",
|
||||
-- only handle links created by this policy
|
||||
Constraint { "is.policy.endpoint.client.link", "=", true, type = "pw-global" },
|
||||
Constraint { "is.policy.virtual.client.link", "=", true, type = "pw-global" },
|
||||
} }
|
||||
|
||||
linkables_om:connect("objects-changed", function (om)
|
||||
@@ -259,6 +263,6 @@ linkables_om:connect("object-removed", function (om, si)
|
||||
unhandleLinkable (si)
|
||||
end)
|
||||
|
||||
endpoints_om:activate()
|
||||
virtuals_om:activate()
|
||||
linkables_om:activate()
|
||||
links_om:activate()
|
@@ -17,9 +17,9 @@ self.scanning = false
|
||||
self.pending_rescan = false
|
||||
|
||||
function rescan ()
|
||||
-- check endpoints and register new links
|
||||
for si_ep in endpoints_om:iterate() do
|
||||
handleEndpoint (si_ep)
|
||||
-- check virtuals and register new links
|
||||
for si_v in virtuals_om:iterate() do
|
||||
handleVirtual (si_v)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -111,7 +111,7 @@ function createLink (si_ep, si_target)
|
||||
["out.item.port.context"] = "output",
|
||||
["in.item.port.context"] = "input",
|
||||
["passive"] = true,
|
||||
["is.policy.endpoint.device.link"] = true,
|
||||
["is.policy.virtual.device.link"] = true,
|
||||
} then
|
||||
Log.warning (si_link, "failed to configure si-standard-link")
|
||||
return
|
||||
@@ -131,8 +131,8 @@ function createLink (si_ep, si_target)
|
||||
end)
|
||||
end
|
||||
|
||||
function handleEndpoint (si_ep)
|
||||
Log.info (si_ep, "handling endpoint " .. si_ep.properties["name"])
|
||||
function handleVirtual (si_ep)
|
||||
Log.info (si_ep, "handling virtual " .. si_ep.properties["name"])
|
||||
|
||||
-- find proper target item
|
||||
local si_target = findUndefinedTarget (si_ep)
|
||||
@@ -192,7 +192,11 @@ function unhandleLinkable (si)
|
||||
end
|
||||
|
||||
default_nodes = Plugin.find("default-nodes-api")
|
||||
endpoints_om = ObjectManager { Interest { type = "SiEndpoint" }}
|
||||
virtuals_om = ObjectManager { Interest { type = "SiLinkable",
|
||||
Constraint {
|
||||
"item.factory.name", "=", "si-audio-virtual", type = "pw-global" },
|
||||
}
|
||||
}
|
||||
linkables_om = ObjectManager {
|
||||
Interest {
|
||||
type = "SiLinkable",
|
||||
@@ -206,7 +210,7 @@ links_om = ObjectManager {
|
||||
Interest {
|
||||
type = "SiLink",
|
||||
-- only handle links created by this policy
|
||||
Constraint { "is.policy.endpoint.device.link", "=", true, type = "pw-global" },
|
||||
Constraint { "is.policy.virtual.device.link", "=", true, type = "pw-global" },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +225,7 @@ linkables_om:connect("objects-changed", function (om)
|
||||
scheduleRescan ()
|
||||
end)
|
||||
|
||||
endpoints_om:connect("object-added", function (om)
|
||||
virtuals_om:connect("object-added", function (om)
|
||||
scheduleRescan ()
|
||||
end)
|
||||
|
||||
@@ -229,6 +233,6 @@ linkables_om:connect("object-removed", function (om, si)
|
||||
unhandleLinkable (si)
|
||||
end)
|
||||
|
||||
endpoints_om:activate()
|
||||
virtuals_om:activate()
|
||||
linkables_om:activate()
|
||||
links_om:activate()
|
@@ -1,42 +0,0 @@
|
||||
-- WirePlumber
|
||||
--
|
||||
-- Copyright © 2021 Collabora Ltd.
|
||||
-- @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
--
|
||||
-- SPDX-License-Identifier: MIT
|
||||
|
||||
-- Receive script arguments from config.lua
|
||||
|
||||
local defaults = {}
|
||||
defaults.endpoints = Json.Object {}
|
||||
|
||||
local config = {}
|
||||
config.endpoints = Conf.get_section (
|
||||
"endpoints", defaults.endpoints):parse ()
|
||||
|
||||
function createEndpoint (factory_name, properties)
|
||||
-- create endpoint
|
||||
local ep = SessionItem ( factory_name )
|
||||
if not ep then
|
||||
Log.warning (ep, "could not create endpoint of type " .. factory_name)
|
||||
return
|
||||
end
|
||||
|
||||
-- configure endpoint
|
||||
if not ep:configure(properties) then
|
||||
Log.warning(ep, "failed to configure endpoint " .. properties.name)
|
||||
return
|
||||
end
|
||||
|
||||
-- activate and register endpoint
|
||||
ep:activate (Features.ALL, function (item)
|
||||
item:register ()
|
||||
Log.info(item, "registered endpoint " .. properties.name)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
for name, properties in pairs(config.endpoints) do
|
||||
properties["name"] = name
|
||||
createEndpoint ("si-audio-endpoint", properties)
|
||||
end
|
@@ -38,8 +38,8 @@ test(
|
||||
)
|
||||
|
||||
test(
|
||||
'test-si-audio-endpoint',
|
||||
executable('test-si-audio-endpoint', 'si-audio-endpoint.c',
|
||||
'test-si-audio-virtual',
|
||||
executable('test-si-audio-virtual', 'si-audio-virtual.c',
|
||||
dependencies: common_deps, c_args: common_args),
|
||||
env: common_env,
|
||||
)
|
||||
|
@@ -13,7 +13,7 @@ typedef struct {
|
||||
} TestFixture;
|
||||
|
||||
static void
|
||||
test_si_audio_endpoint_setup (TestFixture * f, gconstpointer user_data)
|
||||
test_si_audio_virtual_setup (TestFixture * f, gconstpointer user_data)
|
||||
{
|
||||
wp_base_test_fixture_setup (&f->base, 0);
|
||||
|
||||
@@ -36,22 +36,22 @@ test_si_audio_endpoint_setup (TestFixture * f, gconstpointer user_data)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
wp_core_load_component (f->base.core,
|
||||
"libwireplumber-module-si-audio-endpoint", "module", NULL, &error);
|
||||
"libwireplumber-module-si-audio-virtual", "module", NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_si_audio_endpoint_teardown (TestFixture * f, gconstpointer user_data)
|
||||
test_si_audio_virtual_teardown (TestFixture * f, gconstpointer user_data)
|
||||
{
|
||||
wp_base_test_fixture_teardown (&f->base);
|
||||
}
|
||||
|
||||
static void
|
||||
test_si_audio_endpoint_configure_activate (TestFixture * f,
|
||||
test_si_audio_virtual_configure_activate (TestFixture * f,
|
||||
gconstpointer user_data)
|
||||
{
|
||||
g_autoptr (WpSessionItem) endpoint = NULL;
|
||||
g_autoptr (WpSessionItem) item = NULL;
|
||||
|
||||
/* skip the test if null-audio-sink factory is not installed */
|
||||
if (!test_is_spa_lib_installed (&f->base, "support.null-audio-sink")) {
|
||||
@@ -59,53 +59,53 @@ test_si_audio_endpoint_configure_activate (TestFixture * f,
|
||||
return;
|
||||
}
|
||||
|
||||
/* create endpoint */
|
||||
/* create item */
|
||||
|
||||
endpoint = wp_session_item_make (f->base.core, "si-audio-endpoint");
|
||||
g_assert_nonnull (endpoint);
|
||||
item = wp_session_item_make (f->base.core, "si-audio-virtual");
|
||||
g_assert_nonnull (item);
|
||||
|
||||
/* configure endpoint */
|
||||
/* configure item */
|
||||
|
||||
{
|
||||
WpProperties *props = wp_properties_new_empty ();
|
||||
wp_properties_set (props, "name", "endpoint");
|
||||
wp_properties_set (props, "name", "virtual");
|
||||
wp_properties_set (props, "media.class", "Audio/Source");
|
||||
g_assert_true (wp_session_item_configure (endpoint, props));
|
||||
g_assert_true (wp_session_item_is_configured (endpoint));
|
||||
g_assert_true (wp_session_item_configure (item, props));
|
||||
g_assert_true (wp_session_item_is_configured (item));
|
||||
}
|
||||
|
||||
{
|
||||
const gchar *str = NULL;
|
||||
g_autoptr (WpProperties) props = wp_session_item_get_properties (endpoint);
|
||||
g_autoptr (WpProperties) props = wp_session_item_get_properties (item);
|
||||
g_assert_nonnull (props);
|
||||
str = wp_properties_get (props, "name");
|
||||
g_assert_nonnull (str);
|
||||
g_assert_cmpstr ("endpoint", ==, str);
|
||||
g_assert_cmpstr ("virtual", ==, str);
|
||||
str = wp_properties_get (props, "direction");
|
||||
g_assert_nonnull (str);
|
||||
g_assert_cmpstr ("1", ==, str);
|
||||
str = wp_properties_get (props, "item.factory.name");
|
||||
g_assert_nonnull (str);
|
||||
g_assert_cmpstr ("si-audio-endpoint", ==, str);
|
||||
g_assert_cmpstr ("si-audio-virtual", ==, str);
|
||||
}
|
||||
|
||||
/* activate endpoint */
|
||||
/* activate item */
|
||||
|
||||
wp_object_activate (WP_OBJECT (endpoint), WP_SESSION_ITEM_FEATURE_ACTIVE,
|
||||
wp_object_activate (WP_OBJECT (item), WP_SESSION_ITEM_FEATURE_ACTIVE,
|
||||
NULL, (GAsyncReadyCallback) test_object_activate_finish_cb, f);
|
||||
g_main_loop_run (f->base.loop);
|
||||
g_assert_cmphex (wp_object_get_active_features (WP_OBJECT (endpoint)), ==,
|
||||
g_assert_cmphex (wp_object_get_active_features (WP_OBJECT (item)), ==,
|
||||
WP_SESSION_ITEM_FEATURE_ACTIVE);
|
||||
|
||||
/* reset */
|
||||
wp_session_item_reset (endpoint);
|
||||
g_assert_false (wp_session_item_is_configured (endpoint));
|
||||
wp_session_item_reset (item);
|
||||
g_assert_false (wp_session_item_is_configured (item));
|
||||
}
|
||||
|
||||
static void
|
||||
test_si_audio_endpoint_export (TestFixture * f, gconstpointer user_data)
|
||||
test_si_audio_virtual_export (TestFixture * f, gconstpointer user_data)
|
||||
{
|
||||
g_autoptr (WpSessionItem) endpoint = NULL;
|
||||
g_autoptr (WpSessionItem) item = NULL;
|
||||
g_autoptr (WpObjectManager) clients_om = NULL;
|
||||
g_autoptr (WpClient) self_client = NULL;
|
||||
|
||||
@@ -115,8 +115,6 @@ test_si_audio_endpoint_export (TestFixture * f, gconstpointer user_data)
|
||||
return;
|
||||
}
|
||||
|
||||
/* find self_client, to be used for verifying endpoint.client.id */
|
||||
|
||||
clients_om = wp_object_manager_new ();
|
||||
wp_object_manager_add_interest (clients_om, WP_TYPE_CLIENT, NULL);
|
||||
wp_object_manager_request_object_features (clients_om,
|
||||
@@ -128,28 +126,28 @@ test_si_audio_endpoint_export (TestFixture * f, gconstpointer user_data)
|
||||
self_client = wp_object_manager_lookup (clients_om, WP_TYPE_CLIENT, NULL);
|
||||
g_assert_nonnull (self_client);
|
||||
|
||||
/* create endpoint */
|
||||
/* create item */
|
||||
|
||||
endpoint = wp_session_item_make (f->base.core, "si-audio-endpoint");
|
||||
g_assert_nonnull (endpoint);
|
||||
item = wp_session_item_make (f->base.core, "si-audio-virtual");
|
||||
g_assert_nonnull (item);
|
||||
|
||||
/* configure endpoint */
|
||||
/* configure item */
|
||||
{
|
||||
WpProperties *props = wp_properties_new_empty ();
|
||||
wp_properties_set (props, "name", "endpoint");
|
||||
wp_properties_set (props, "name", "virtual");
|
||||
wp_properties_set (props, "media.class", "Audio/Source");
|
||||
g_assert_true (wp_session_item_configure (endpoint, props));
|
||||
g_assert_true (wp_session_item_is_configured (endpoint));
|
||||
g_assert_true (wp_session_item_configure (item, props));
|
||||
g_assert_true (wp_session_item_is_configured (item));
|
||||
}
|
||||
|
||||
/* activate endpoint */
|
||||
/* activate item */
|
||||
|
||||
{
|
||||
wp_object_activate (WP_OBJECT (endpoint),
|
||||
wp_object_activate (WP_OBJECT (item),
|
||||
WP_SESSION_ITEM_FEATURE_ACTIVE | WP_SESSION_ITEM_FEATURE_EXPORTED,
|
||||
NULL, (GAsyncReadyCallback) test_object_activate_finish_cb, f);
|
||||
g_main_loop_run (f->base.loop);
|
||||
g_assert_cmphex (wp_object_get_active_features (WP_OBJECT (endpoint)), ==,
|
||||
g_assert_cmphex (wp_object_get_active_features (WP_OBJECT (item)), ==,
|
||||
WP_SESSION_ITEM_FEATURE_ACTIVE | WP_SESSION_ITEM_FEATURE_EXPORTED);
|
||||
}
|
||||
|
||||
@@ -157,7 +155,7 @@ test_si_audio_endpoint_export (TestFixture * f, gconstpointer user_data)
|
||||
g_autoptr (WpNode) n = NULL;
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
|
||||
n = wp_session_item_get_associated_proxy (endpoint, WP_TYPE_NODE);
|
||||
n = wp_session_item_get_associated_proxy (item, WP_TYPE_NODE);
|
||||
g_assert_nonnull (n);
|
||||
props = wp_pipewire_object_get_properties (WP_PIPEWIRE_OBJECT (n));
|
||||
g_assert_nonnull (props);
|
||||
@@ -167,8 +165,8 @@ test_si_audio_endpoint_export (TestFixture * f, gconstpointer user_data)
|
||||
}
|
||||
|
||||
/* reset */
|
||||
wp_session_item_reset (endpoint);
|
||||
g_assert_false (wp_session_item_is_configured (endpoint));
|
||||
wp_session_item_reset (item);
|
||||
g_assert_false (wp_session_item_is_configured (item));
|
||||
}
|
||||
|
||||
gint
|
||||
@@ -178,18 +176,18 @@ main (gint argc, gchar *argv[])
|
||||
wp_init (WP_INIT_ALL);
|
||||
|
||||
/* configure-activate */
|
||||
g_test_add ("/modules/si-audio-endpoint/configure-activate",
|
||||
g_test_add ("/modules/si-audio-virtual/configure-activate",
|
||||
TestFixture, NULL,
|
||||
test_si_audio_endpoint_setup,
|
||||
test_si_audio_endpoint_configure_activate,
|
||||
test_si_audio_endpoint_teardown);
|
||||
test_si_audio_virtual_setup,
|
||||
test_si_audio_virtual_configure_activate,
|
||||
test_si_audio_virtual_teardown);
|
||||
|
||||
/* export */
|
||||
g_test_add ("/modules/si-audio-endpoint/export",
|
||||
g_test_add ("/modules/si-audio-virtual/export",
|
||||
TestFixture, NULL,
|
||||
test_si_audio_endpoint_setup,
|
||||
test_si_audio_endpoint_export,
|
||||
test_si_audio_endpoint_teardown);
|
||||
test_si_audio_virtual_setup,
|
||||
test_si_audio_virtual_export,
|
||||
test_si_audio_virtual_teardown);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
@@ -112,13 +112,13 @@ test_si_standard_link_main (TestFixture * f, gconstpointer user_data)
|
||||
{
|
||||
g_autoptr (WpSessionItem) link = NULL;
|
||||
|
||||
/* skip the test if audiotestsrc endpoint could not be loaded */
|
||||
/* skip the test if audiotestsrc could not be loaded */
|
||||
if (!f->src_item) {
|
||||
g_test_skip ("The pipewire audiotestsrc factory was not found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* skip the test if null-audio-sink endpoint could not be loaded */
|
||||
/* skip the test if null-audio-sink could not be loaded */
|
||||
if (!f->sink_item) {
|
||||
g_test_skip ("The pipewire null-audio-sink factory was not found");
|
||||
return;
|
||||
|
@@ -209,7 +209,7 @@ load_components (ScriptRunnerFixture *f, gconstpointer argv)
|
||||
|
||||
load_component (f, "si-audio-adapter", "module");
|
||||
load_component (f, "si-standard-link", "module");
|
||||
load_component (f, "si-audio-endpoint", "module");
|
||||
load_component (f, "si-audio-virtual", "module");
|
||||
|
||||
load_component (f, "metadata", "module");
|
||||
load_component (f, "default-nodes-api", "module");
|
||||
|
Reference in New Issue
Block a user