Rename endpoint concept to virtual session item

This commit is contained in:
Julian Bouzas
2022-12-11 18:45:43 -05:00
parent 9004362cda
commit fb855b00cf
20 changed files with 284 additions and 278 deletions

View File

@@ -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. * 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). applications(for example Music, Voice, Navigation, Gaming etc).
The actual grouping is done based on the `media.role` of the client The actual grouping is done based on the `media.role` of the client
stream node. stream node.
Endpoints allows for that actions to be taken up at group level rather than Virtual session items allows for that actions to be taken up at group level
at individual stream level, which can be cumbersome. rather than at individual stream level, which can be cumbersome.
For example imagine the following scenarios. For example imagine the following scenarios.
* Incoming Navigation message needs to duck the volume of * Incoming Navigation message needs to duck the volume of
Audio playback(all the apps playing audio). Audio playback(all the apps playing audio).
* Incoming voice/voip call needs to stop(cork) the Audio playback. * 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:: Example::
endpoints = { virtual-items = {
endpoint.capture = { virtual-item.capture = {
media.class = "Audio/Source" media.class = "Audio/Source"
role = "Capture" role = "Capture"
} }
endpoint.multimedia = { virtual-item.multimedia = {
media.class = "Audio/Sink" media.class = "Audio/Sink"
role = "Multimedia" role = "Multimedia"
} }
endpoint.navigation = { virtual-item.navigation = {
media.class = "Audio/Sink" media.class = "Audio/Sink"
role = "Navigation" role = "Navigation"
} }
This example creates 3 endpoints, with names ``endpoint.capture``, This example creates 3 virtual session items, with names
``endpoint.multimedia`` and ``endpoint.navigation`` and assigned roles ``virtual-item.capture``, ``virtual-item.multimedia`` and
``Capture``, ``Multimedia`` and ``Navigation`` respectively. ``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 First virtual item has a media class of ``Audio/Source`` used for capture
used for playback. 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:: 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`` The above example defines actions for both ``Multimedia`` and ``Navigation``
roles. Since the Navigation role has more priority than the Multimedia roles. Since the Navigation role has more priority than the Multimedia
role, when a client connects to the Navigation endpoint, it will ``duck`` role, when a client connects to the Navigation virtual session item, it
the volume of all Multimedia clients. If Multiple Navigation clients want will ``duck`` the volume of all Multimedia clients. If Multiple Navigation
to play audio, their audio will be mixed. clients want to play audio, their audio will be mixed.
Possible values of actions are: ``mix`` (Mixes audio), Possible values of actions are: ``mix`` (Mixes audio),
``duck`` (Mixes and lowers the audio volume) or ``cork`` (Pauses 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 Virtual session items are not used for desktop use cases, it is more suitable
embedded use cases. for embedded use cases.
* *Split Configuration files* * *Split Configuration files*

View File

@@ -62,16 +62,16 @@ Native API clients
pw-cat pw-cat
^^^^^^ ^^^^^^
Using the default endpoint: Using the default device:
.. code:: console .. code:: console
$ wpctl status # verify the default endpoints $ wpctl status # verify the default device
$ pw-record test.wav $ pw-record test.wav
$ pw-play test.wav $ pw-play test.wav
Using a non-default endpoint: Using a non-default device:
.. code:: console .. code:: console
@@ -84,19 +84,18 @@ or
.. code:: console .. code:: console
$ wpctl status # find the capture & playback endpoint ids $ wpctl status # find the capture & playback node ids
$ pw-record --target <endpoint_id> test.wav $ pw-record --target <node_id> test.wav
$ pw-play --target <endpoint_id> test.wav $ pw-play --target <node_id> test.wav
.. note:: .. note::
node ids and endpoint ids can be used interchangeably when specifying node ids can be used interchangeably when specifying targets in all use cases
targets in all use cases
video-play video-play
^^^^^^^^^^ ^^^^^^^^^^
Using the default endpoint: Using the default device:
.. code:: console .. code:: console
@@ -104,13 +103,13 @@ Using the default endpoint:
$ ./build/src/examples/video-play $ ./build/src/examples/video-play
Using a non-default endpoint: Using a non-default device:
.. code:: console .. 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 $ cd path/to/pipewire-source-dir
$ ./build/src/examples/video-play <endpoint_id> $ ./build/src/examples/video-play <node_id>
.. tip:: .. tip::
@@ -123,11 +122,11 @@ PulseAudio compat API clients
pacat pacat
^^^^^ ^^^^^
Using the default endpoint: Using the default device:
.. code:: console .. code:: console
$ wpctl status # verify the default endpoints $ wpctl status # verify the default device
$ parecord test.wav $ parecord test.wav
$ paplay test.wav $ paplay test.wav
@@ -158,29 +157,29 @@ aplay / arecord
``pipewire-alsa/conf/50-pipewire.conf`` in your ``~/.asoundrc`` ``pipewire-alsa/conf/50-pipewire.conf`` in your ``~/.asoundrc``
(or anywhere else, system-wide, where libasound can read it) (or anywhere else, system-wide, where libasound can read it)
Using the default endpoint: Using the default device:
.. code:: console .. code:: console
$ wpctl status # verify the default endpoints $ wpctl status # verify the default devices
$ arecord -D pipewire -f S16_LE -r 48000 test.wav $ arecord -D pipewire -f S16_LE -r 48000 test.wav
$ aplay -D pipewire test.wav $ aplay -D pipewire test.wav
Using a non-default endpoint: Using a non-default device:
.. code:: console .. code:: console
$ wpctl status # find the capture & playback endpoint ids $ wpctl status # find the capture & playback node ids
$ PIPEWIRE_NODE=<endpoint_id> arecord -D pipewire -f S16_LE -r 48000 test.wav $ PIPEWIRE_NODE=<node_id> arecord -D pipewire -f S16_LE -r 48000 test.wav
$ PIPEWIRE_NODE=<endpoint_id> aplay -D pipewire test.wav $ PIPEWIRE_NODE=<node_id> aplay -D pipewire test.wav
or or
.. code:: console .. code:: console
$ wpctl status # find the capture & playback endpoint ids $ wpctl status # find the capture & playback device node ids
$ arecord -D pipewire:NODE=<endpoint_id> -f S16_LE -r 48000 test.wav $ arecord -D pipewire:NODE=<node_id> -f S16_LE -r 48000 test.wav
$ aplay -D pipewire:NODE=<endpoint_id> test.wav $ aplay -D pipewire:NODE=<node_id> test.wav
JACK compat API clients JACK compat API clients
@@ -201,15 +200,14 @@ jack_simple_client
.. code:: console .. code:: console
$ wpctl status # find the target endpoint id $ wpctl status # find the target device node id
$ wpctl inspect <endpoint_id> # find the node.id $ wpctl inspect <node_id> # find the node.id
$ PIPEWIRE_NODE=<node_id> pw-jack jack_simple_client $ PIPEWIRE_NODE=<node_id> pw-jack jack_simple_client
.. note:: .. note::
The JACK layer is not controlled by the session manager, it creates its own 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 links; which is why it is required to specify a node id.
work)
Device Reservation Device Reservation
------------------ ------------------
@@ -263,7 +261,7 @@ with JACK
3. Wait a few seconds and run ``wpctl status`` to inspect 3. Wait a few seconds and run ``wpctl status`` to inspect
- The devices taken by JACK should no longer be available - 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``) 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 6. Wait a few seconds and run ``wpctl status`` to inspect
- The devices that were release by JACK should again be available - 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:: .. note::

View File

@@ -76,11 +76,11 @@ shared_library(
) )
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 : true,
install_dir : wireplumber_module_dir, install_dir : wireplumber_module_dir,
dependencies : [wp_dep, pipewire_dep], dependencies : [wp_dep, pipewire_dep],

View File

@@ -175,13 +175,6 @@ local Feature = {
Node = { Node = {
PORTS = (1 << 16), PORTS = (1 << 16),
}, },
Session = {
ENDPOINTS = (1 << 16),
LINKS = (1 << 17),
},
Endpoint = {
STREAMS = (1 << 16),
},
Metadata = { Metadata = {
DATA = (1 << 16), DATA = (1 << 16),
}, },

View File

@@ -12,9 +12,9 @@
#include <spa/param/audio/raw.h> #include <spa/param/audio/raw.h>
#include <spa/param/param.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; WpSessionItem parent;
@@ -31,26 +31,26 @@ struct _WpSiAudioEndpoint
WpSiAdapter *adapter; WpSiAdapter *adapter;
}; };
static void si_audio_endpoint_linkable_init (WpSiLinkableInterface * iface); static void si_audio_virtual_linkable_init (WpSiLinkableInterface * iface);
static void si_audio_endpoint_adapter_init (WpSiAdapterInterface * iface); static void si_audio_virtual_adapter_init (WpSiAdapterInterface * iface);
G_DECLARE_FINAL_TYPE(WpSiAudioEndpoint, si_audio_endpoint, WP, G_DECLARE_FINAL_TYPE(WpSiAudioVirtual, si_audio_virtual, WP,
SI_AUDIO_ENDPOINT, WpSessionItem) SI_AUDIO_VIRTUAL, WpSessionItem)
G_DEFINE_TYPE_WITH_CODE (WpSiAudioEndpoint, si_audio_endpoint, G_DEFINE_TYPE_WITH_CODE (WpSiAudioVirtual, si_audio_virtual,
WP_TYPE_SESSION_ITEM, WP_TYPE_SESSION_ITEM,
G_IMPLEMENT_INTERFACE (WP_TYPE_SI_LINKABLE, G_IMPLEMENT_INTERFACE (WP_TYPE_SI_LINKABLE,
si_audio_endpoint_linkable_init) si_audio_virtual_linkable_init)
G_IMPLEMENT_INTERFACE (WP_TYPE_SI_ADAPTER, si_audio_endpoint_adapter_init)) G_IMPLEMENT_INTERFACE (WP_TYPE_SI_ADAPTER, si_audio_virtual_adapter_init))
static void static void
si_audio_endpoint_init (WpSiAudioEndpoint * self) si_audio_virtual_init (WpSiAudioVirtual * self)
{ {
} }
static void 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 */ /* deactivate first */
wp_object_deactivate (WP_OBJECT (self), wp_object_deactivate (WP_OBJECT (self),
@@ -64,18 +64,18 @@ si_audio_endpoint_reset (WpSessionItem * item)
self->priority = 0; self->priority = 0;
self->disable_dsp = FALSE; 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 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); g_autoptr (WpProperties) si_props = wp_properties_ensure_unique_owner (p);
const gchar *str; const gchar *str;
/* reset previous config */ /* reset previous config */
si_audio_endpoint_reset (item); si_audio_virtual_reset (item);
str = wp_properties_get (si_props, "name"); str = wp_properties_get (si_props, "name");
if (!str) if (!str)
@@ -116,18 +116,18 @@ si_audio_endpoint_configure (WpSessionItem * item, WpProperties *p)
} }
static gpointer 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 ( return wp_session_item_get_associated_proxy (
WP_SESSION_ITEM (self->adapter), proxy_type); WP_SESSION_ITEM (self->adapter), proxy_type);
} }
static void 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->adapter);
g_clear_object (&self->node); g_clear_object (&self->node);
@@ -136,9 +136,9 @@ si_audio_endpoint_disable_active (WpSessionItem *si)
} }
static void 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_object_update_features (WP_OBJECT (self), 0,
WP_SESSION_ITEM_FEATURE_EXPORTED); WP_SESSION_ITEM_FEATURE_EXPORTED);
@@ -148,7 +148,7 @@ static void
on_adapter_activate_done (WpObject * adapter, GAsyncResult * res, on_adapter_activate_done (WpObject * adapter, GAsyncResult * res,
WpTransition * transition) 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 (GError) error = NULL;
if (!wp_object_activate_finish (adapter, res, &error)) { if (!wp_object_activate_finish (adapter, res, &error)) {
@@ -163,7 +163,7 @@ on_adapter_activate_done (WpObject * adapter, GAsyncResult * res,
static void static void
on_adapter_port_state_changed (WpSiAdapter *item, on_adapter_port_state_changed (WpSiAdapter *item,
WpSiAdapterPortsState old_state, WpSiAdapterPortsState new_state, WpSiAdapterPortsState old_state, WpSiAdapterPortsState new_state,
WpSiAudioEndpoint *self) WpSiAudioVirtual *self)
{ {
g_signal_emit_by_name (self, "adapter-ports-state-changed", old_state, g_signal_emit_by_name (self, "adapter-ports-state-changed", old_state,
new_state); new_state);
@@ -173,7 +173,7 @@ static void
on_node_activate_done (WpObject * node, GAsyncResult * res, on_node_activate_done (WpObject * node, GAsyncResult * res,
WpTransition * transition) 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 (GError) error = NULL;
g_autoptr (WpCore) core = NULL; g_autoptr (WpCore) core = NULL;
g_autoptr (WpProperties) props = NULL; g_autoptr (WpProperties) props = NULL;
@@ -190,7 +190,7 @@ on_node_activate_done (WpObject * node, GAsyncResult * res,
if (!self->adapter) { if (!self->adapter) {
wp_transition_return_error (transition, wp_transition_return_error (transition,
g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT, 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 */ /* Forward adapter-ports-state-changed signal */
@@ -210,7 +210,7 @@ on_node_activate_done (WpObject * node, GAsyncResult * res,
g_steal_pointer (&props))) { g_steal_pointer (&props))) {
wp_transition_return_error (transition, wp_transition_return_error (transition,
g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT, 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 */ /* activate adapter */
@@ -219,12 +219,12 @@ on_node_activate_done (WpObject * node, GAsyncResult * res,
} }
static void 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_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
g_autofree gchar *name = g_strdup_printf ("control.%s", self->name); 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"); (self->direction == WP_DIRECTION_OUTPUT) ? "Capture" : "Playback");
g_autofree gchar *media = g_strdup_printf ("Audio/%s", g_autofree gchar *media = g_strdup_printf ("Audio/%s",
(self->direction == WP_DIRECTION_OUTPUT) ? "Source" : "Sink"); (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)) { if (!wp_session_item_is_configured (si)) {
wp_transition_return_error (transition, wp_transition_return_error (transition,
g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT, 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; return;
} }
@@ -244,12 +244,12 @@ si_audio_endpoint_enable_active (WpSessionItem *si, WpTransition *transition)
PW_KEY_FACTORY_NAME, "support.null-audio-sink", PW_KEY_FACTORY_NAME, "support.null-audio-sink",
PW_KEY_NODE_DESCRIPTION, desc, PW_KEY_NODE_DESCRIPTION, desc,
"monitor.channel-volumes", "true", "monitor.channel-volumes", "true",
"wireplumber.is-endpoint", "true", "wireplumber.is-virtual", "true",
NULL)); NULL));
if (!self->node) { if (!self->node) {
wp_transition_return_error (transition, wp_transition_return_error (transition,
g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT, 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; return;
} }
@@ -260,84 +260,84 @@ si_audio_endpoint_enable_active (WpSessionItem *si, WpTransition *transition)
} }
static void 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_object_update_features (WP_OBJECT (self),
WP_SESSION_ITEM_FEATURE_EXPORTED, 0); WP_SESSION_ITEM_FEATURE_EXPORTED, 0);
} }
static void static void
si_audio_endpoint_class_init (WpSiAudioEndpointClass * klass) si_audio_virtual_class_init (WpSiAudioVirtualClass * klass)
{ {
WpSessionItemClass *si_class = (WpSessionItemClass *) klass; WpSessionItemClass *si_class = (WpSessionItemClass *) klass;
si_class->reset = si_audio_endpoint_reset; si_class->reset = si_audio_virtual_reset;
si_class->configure = si_audio_endpoint_configure; si_class->configure = si_audio_virtual_configure;
si_class->get_associated_proxy = si_audio_endpoint_get_associated_proxy; si_class->get_associated_proxy = si_audio_virtual_get_associated_proxy;
si_class->disable_active = si_audio_endpoint_disable_active; si_class->disable_active = si_audio_virtual_disable_active;
si_class->disable_exported = si_audio_endpoint_disable_exported; si_class->disable_exported = si_audio_virtual_disable_exported;
si_class->enable_active = si_audio_endpoint_enable_active; si_class->enable_active = si_audio_virtual_enable_active;
si_class->enable_exported = si_audio_endpoint_enable_exported; si_class->enable_exported = si_audio_virtual_enable_exported;
} }
static GVariant * 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); return wp_si_linkable_get_ports (WP_SI_LINKABLE (self->adapter), context);
} }
static void 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 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); return wp_si_adapter_get_ports_state (self->adapter);
} }
static WpSpaPod * 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); return wp_si_adapter_get_ports_format (self->adapter, mode);
} }
static void 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) 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); wp_si_adapter_set_ports_format (self->adapter, f, mode, callback, data);
} }
static gboolean static gboolean
si_audio_endpoint_set_ports_format_finish (WpSiAdapter * item, si_audio_virtual_set_ports_format_finish (WpSiAdapter * item,
GAsyncResult * res, GError ** error) 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); return wp_si_adapter_set_ports_format_finish (self->adapter, res, error);
} }
static void 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_state = si_audio_virtual_get_ports_state;
iface->get_ports_format = si_audio_endpoint_get_ports_format; iface->get_ports_format = si_audio_virtual_get_ports_format;
iface->set_ports_format = si_audio_endpoint_set_ports_format; iface->set_ports_format = si_audio_virtual_set_ports_format;
iface->set_ports_format_finish = si_audio_endpoint_set_ports_format_finish; iface->set_ports_format_finish = si_audio_virtual_set_ports_format_finish;
} }
WP_PLUGIN_EXPORT gboolean WP_PLUGIN_EXPORT gboolean
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error) wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{ {
wp_si_factory_register (core, wp_si_factory_new_simple (SI_FACTORY_NAME, 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; return TRUE;
} }

View File

@@ -550,10 +550,10 @@ configure_and_link_adapters (WpSiStandardLink *self, WpTransition *transition)
in->is_device = !g_strcmp0 (str, "device"); in->is_device = !g_strcmp0 (str, "device");
str = wp_session_item_get_property (WP_SESSION_ITEM (out->si), "item.factory.name"); 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; || out->is_device;
str = wp_session_item_get_property (WP_SESSION_ITEM (in->si), "item.factory.name"); 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; || in->is_device;
str = wp_session_item_get_property (WP_SESSION_ITEM (out->si), "stream.dont-remix"); str = wp_session_item_get_property (WP_SESSION_ITEM (out->si), "stream.dont-remix");

View File

@@ -131,7 +131,7 @@ wireplumber.components = [
} }
{ {
name = libwireplumber-module-si-audio-endpoint, name = libwireplumber-module-si-audio-virtual,
type = module type = module
## default priority for session item modules ## default priority for session item modules
priority = 130 priority = 130

View File

@@ -1,48 +1,48 @@
## The WirePlumber endpoint configuration ## The WirePlumber virtual item configuration
endpoints = { virtual-items = {
## The list of endpoints to create ## The list of virtual items to create
# endpoint.capture = { # virtual-item.capture = {
# media.class = "Audio/Source" # media.class = "Audio/Source"
# role = "Capture" # role = "Capture"
# } # }
# endpoint.multimedia = { # virtual-item.multimedia = {
# media.class = "Audio/Sink" # media.class = "Audio/Sink"
# role = "Multimedia" # role = "Multimedia"
# } # }
# endpoint.speech_low = { # virtual-item.speech_low = {
# media.class = "Audio/Sink" # media.class = "Audio/Sink"
# role = "Speech-Low" # role = "Speech-Low"
# } # }
# endpoint.custom_low = { # virtual-item.custom_low = {
# media.class = "Audio/Sink" # media.class = "Audio/Sink"
# role = "Custom-Low" # role = "Custom-Low"
# } # }
# endpoint.navigation = { # virtual-item.navigation = {
# media.class = "Audio/Sink" # media.class = "Audio/Sink"
# role = "Navigation" # role = "Navigation"
# } # }
# endpoint.speech_high = { # virtual-item.speech_high = {
# media.class = "Audio/Sink" # media.class = "Audio/Sink"
# role = "Speech-High" # role = "Speech-High"
# } # }
# endpoint.custom_high = { # virtual-item.custom_high = {
# media.class = "Audio/Sink" # media.class = "Audio/Sink"
# role = "Custom-High" # role = "Custom-High"
# } # }
# endpoint.communication = { # virtual-item.communication = {
# media.class = "Audio/Sink" # media.class = "Audio/Sink"
# role = "Communication" # role = "Communication"
# } # }
# endpoint.emergency = { # virtual-item.emergency = {
# media.class = "Audio/Sink" # media.class = "Audio/Sink"
# role = "Emergency" # role = "Emergency"
# } # }
} }
endpoint-roles = { virtual-item-roles = {
## The list of endpoint roles to use ## The list of virtual item roles to use
# Capture = { # Capture = {
# alias = [ "Multimedia", "Music", "Voice", "Capture" ] # alias = [ "Multimedia", "Music", "Voice", "Capture" ]

View 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

View File

@@ -12,8 +12,8 @@ node_om = ObjectManager {
Interest { Interest {
type = "node", type = "node",
Constraint { "media.class", "matches", "Audio/Sink", type = "pw-global" }, Constraint { "media.class", "matches", "Audio/Sink", type = "pw-global" },
-- Do not consider endpoints created by WirePlumber -- Do not consider virtual items created by WirePlumber
Constraint { "wireplumber.is-endpoint", "!", true, type = "pw" }, Constraint { "wireplumber.is-virtual", "!", true, type = "pw" },
-- or the fallback sink itself -- or the fallback sink itself
Constraint { "wireplumber.is-fallback", "!", true, type = "pw" }, Constraint { "wireplumber.is-fallback", "!", true, type = "pw" },
} }

View File

@@ -22,8 +22,7 @@ function checkLinkable (si, om, handle_nonstreams)
end end
-- Determine if we can handle item by this policy -- Determine if we can handle item by this policy
if om:lookup { type = "SiEndpoint" } and if si_props ["item.factory.name"] == "si-audio-virtual" then
si_props ["item.factory.name"] == "si-audio-adapter" then
return false return false
end end

View File

@@ -74,7 +74,7 @@ AsyncEventHook {
EventInterest { EventInterest {
Constraint { "event.type", "=", "node-added" }, Constraint { "event.type", "=", "node-added" },
Constraint { "media.class", "#", "Audio/*", type = "pw-global" }, Constraint { "media.class", "#", "Audio/*", type = "pw-global" },
Constraint { "wireplumber.is-endpoint", "-", type = "pw" }, Constraint { "wireplumber.is-virtual", "-", type = "pw" },
}, },
}, },
steps = { steps = {
@@ -144,7 +144,7 @@ SimpleEventHook {
EventInterest { EventInterest {
Constraint { "event.type", "=", "node-removed" }, Constraint { "event.type", "=", "node-removed" },
Constraint { "media.class", "#", "Audio/*", type = "pw-global" }, Constraint { "media.class", "#", "Audio/*", type = "pw-global" },
Constraint { "wireplumber.is-endpoint", "-", type = "pw" }, Constraint { "wireplumber.is-virtual", "-", type = "pw" },
}, },
}, },
execute = function (event) execute = function (event)

View File

@@ -13,7 +13,7 @@ local config = {}
config.duck_level = Conf.get_value_float ("wireplumber.settings", config.duck_level = Conf.get_value_float ("wireplumber.settings",
"policy.default.duck-level", defaults.duck_level) "policy.default.duck-level", defaults.duck_level)
config.roles = Conf.get_section ( config.roles = Conf.get_section (
"endpoint-roles", defaults.roles):parse () "virtual-item-roles", defaults.roles):parse ()
function findRole(role) function findRole(role)
if role and not config.roles[role] then if role and not config.roles[role] then
@@ -50,32 +50,38 @@ end
function restoreVolume(role, media_class) function restoreVolume(role, media_class)
if not mixer_api then return end 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.role", "=", role, type = "pw" },
Constraint { "media.class", "=", media_class, type = "pw" }, Constraint { "media.class", "=", media_class, type = "pw" },
} }
if ep and ep.properties["node.id"] then if si_v then
Log.debug(ep, "restore role " .. role) local n = si_v:get_associated_proxy ("node")
mixer_api:call("set-volume", ep.properties["node.id"], { if n then
monitorVolume = 1.0, Log.debug(si_v, "restore role " .. role)
}) mixer_api:call("set-volume", n["bound-id"], {
monitorVolume = 1.0,
})
end
end end
end end
function duck(role, media_class) function duck(role, media_class)
if not mixer_api then return end 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.role", "=", role, type = "pw" },
Constraint { "media.class", "=", media_class, type = "pw" }, Constraint { "media.class", "=", media_class, type = "pw" },
} }
if ep and ep.properties["node.id"] then if si_v then
Log.debug(ep, "duck role " .. role) local n = si_v:get_associated_proxy ("node")
mixer_api:call("set-volume", ep.properties["node.id"], { if n then
monitorVolume = config.duck_level, Log.debug(si_v, "duck role " .. role)
}) mixer_api:call("set-volume", n["bound-id"], {
monitorVolume = config.duck_level,
})
end
end end
end end
@@ -98,7 +104,7 @@ function rescan()
["Video/Source"] = {}, ["Video/Source"] = {},
} }
Log.info("Rescan endpoint links") Log.info("Rescan virtual links")
-- deactivate all links if suspend playback metadata is present -- deactivate all links if suspend playback metadata is present
local suspend = getSuspendPlaybackMetadata() local suspend = getSuspendPlaybackMetadata()
@@ -193,7 +199,7 @@ end
silinks_om = ObjectManager { silinks_om = ObjectManager {
Interest { Interest {
type = "SiLink", type = "SiLink",
Constraint { "is.policy.endpoint.client.link", "=", true }, Constraint { "is.policy.virtual.client.link", "=", true },
}, },
} }
silinks_om:connect("objects-changed", maybeRescan) silinks_om:connect("objects-changed", maybeRescan)
@@ -202,10 +208,12 @@ silinks_om:activate()
-- enable ducking if mixer-api is loaded -- enable ducking if mixer-api is loaded
mixer_api = Plugin.find("mixer-api") mixer_api = Plugin.find("mixer-api")
if mixer_api then if mixer_api then
endpoints_om = ObjectManager { virtuals_om = ObjectManager { Interest { type = "SiLinkable",
Interest { type = "endpoint" }, Constraint {
"item.factory.name", "=", "si-audio-virtual", type = "pw-global" },
}
} }
endpoints_om:activate() virtuals_om:activate()
end end
metadata_om = ObjectManager { metadata_om = ObjectManager {

View File

@@ -12,7 +12,7 @@ defaults.roles = Json.Object {}
local config = {} local config = {}
config.roles = Conf.get_section ( config.roles = Conf.get_section (
"endpoint-roles", defaults.roles):parse () "virtual-item-roles", defaults.roles):parse ()
local self = {} local self = {}
self.scanning = false self.scanning = false
@@ -73,7 +73,7 @@ function findRole(role, tmc)
return role return role
end end
function findTargetEndpoint (node, media_class, role) function findTargetVirtual (node, media_class, role)
local target_class_assoc = { local target_class_assoc = {
["Stream/Input/Audio"] = "Audio/Source", ["Stream/Input/Audio"] = "Audio/Source",
["Stream/Output/Audio"] = "Audio/Sink", ["Stream/Output/Audio"] = "Audio/Sink",
@@ -89,9 +89,9 @@ function findTargetEndpoint (node, media_class, role)
return nil return nil
end end
-- find highest priority endpoint by role -- find highest priority virtual by role
media_role = findRole(role, target_media_class) 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 { "role", "=", media_role, type = "pw-global" },
Constraint { "media.class", "=", target_media_class, type = "pw-global" }, Constraint { "media.class", "=", target_media_class, type = "pw-global" },
} do } do
@@ -132,7 +132,7 @@ function createLink (si, si_target_ep)
["in.item"] = in_item, ["in.item"] = in_item,
["out.item.port.context"] = "output", ["out.item.port.context"] = "output",
["in.item.port.context"] = "input", ["in.item.port.context"] = "input",
["is.policy.endpoint.client.link"] = true, ["is.policy.virtual.client.link"] = true,
["media.role"] = target_ep_props["role"], ["media.role"] = target_ep_props["role"],
["target.media.class"] = target_ep_props["media.class"], ["target.media.class"] = target_ep_props["media.class"],
["item.plugged.usec"] = si_props["item.plugged.usec"], ["item.plugged.usec"] = si_props["item.plugged.usec"],
@@ -159,7 +159,7 @@ function checkLinkable (si)
end end
-- Determine if we can handle item by this policy -- 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") Log.debug (si, "item won't be handled by this policy")
return false return false
end end
@@ -178,10 +178,10 @@ function handleLinkable (si)
Log.info (si, "handling item " .. tostring(node.properties["node.name"]) .. Log.info (si, "handling item " .. tostring(node.properties["node.name"]) ..
" with role " .. media_role) " with role " .. media_role)
-- find proper target endpoint -- find proper target virtual
local si_target_ep = findTargetEndpoint (node, media_class, media_role) local si_target_ep = findTargetVirtual (node, media_class, media_role)
if not si_target_ep then if not si_target_ep then
Log.info (si, "... target endpoint not found") Log.info (si, "... target virtual not found")
return return
end end
@@ -191,11 +191,11 @@ function handleLinkable (si)
local in_id = tonumber(link.properties["in.item.id"]) local in_id = tonumber(link.properties["in.item.id"])
if out_id == si.id or in_id == si.id then if out_id == si.id or in_id == si.id then
local is_out = out_id == si.id and true or false 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 == (is_out and in_id or out_id) then
if peer_ep.id == si_target_ep.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 return
end end
@@ -237,7 +237,11 @@ function unhandleLinkable (si)
end end
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", linkables_om = ObjectManager { Interest { type = "SiLinkable",
-- only handle si-audio-adapter and si-node -- only handle si-audio-adapter and si-node
Constraint { Constraint {
@@ -248,7 +252,7 @@ linkables_om = ObjectManager { Interest { type = "SiLinkable",
} }
links_om = ObjectManager { Interest { type = "SiLink", links_om = ObjectManager { Interest { type = "SiLink",
-- only handle links created by this policy -- 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) linkables_om:connect("objects-changed", function (om)
@@ -259,6 +263,6 @@ linkables_om:connect("object-removed", function (om, si)
unhandleLinkable (si) unhandleLinkable (si)
end) end)
endpoints_om:activate() virtuals_om:activate()
linkables_om:activate() linkables_om:activate()
links_om:activate() links_om:activate()

View File

@@ -17,9 +17,9 @@ self.scanning = false
self.pending_rescan = false self.pending_rescan = false
function rescan () function rescan ()
-- check endpoints and register new links -- check virtuals and register new links
for si_ep in endpoints_om:iterate() do for si_v in virtuals_om:iterate() do
handleEndpoint (si_ep) handleVirtual (si_v)
end end
end end
@@ -111,7 +111,7 @@ function createLink (si_ep, si_target)
["out.item.port.context"] = "output", ["out.item.port.context"] = "output",
["in.item.port.context"] = "input", ["in.item.port.context"] = "input",
["passive"] = true, ["passive"] = true,
["is.policy.endpoint.device.link"] = true, ["is.policy.virtual.device.link"] = true,
} then } then
Log.warning (si_link, "failed to configure si-standard-link") Log.warning (si_link, "failed to configure si-standard-link")
return return
@@ -131,8 +131,8 @@ function createLink (si_ep, si_target)
end) end)
end end
function handleEndpoint (si_ep) function handleVirtual (si_ep)
Log.info (si_ep, "handling endpoint " .. si_ep.properties["name"]) Log.info (si_ep, "handling virtual " .. si_ep.properties["name"])
-- find proper target item -- find proper target item
local si_target = findUndefinedTarget (si_ep) local si_target = findUndefinedTarget (si_ep)
@@ -192,7 +192,11 @@ function unhandleLinkable (si)
end end
default_nodes = Plugin.find("default-nodes-api") 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 { linkables_om = ObjectManager {
Interest { Interest {
type = "SiLinkable", type = "SiLinkable",
@@ -206,7 +210,7 @@ links_om = ObjectManager {
Interest { Interest {
type = "SiLink", type = "SiLink",
-- only handle links created by this policy -- 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 () scheduleRescan ()
end) end)
endpoints_om:connect("object-added", function (om) virtuals_om:connect("object-added", function (om)
scheduleRescan () scheduleRescan ()
end) end)
@@ -229,6 +233,6 @@ linkables_om:connect("object-removed", function (om, si)
unhandleLinkable (si) unhandleLinkable (si)
end) end)
endpoints_om:activate() virtuals_om:activate()
linkables_om:activate() linkables_om:activate()
links_om:activate() links_om:activate()

View File

@@ -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

View File

@@ -38,8 +38,8 @@ test(
) )
test( test(
'test-si-audio-endpoint', 'test-si-audio-virtual',
executable('test-si-audio-endpoint', 'si-audio-endpoint.c', executable('test-si-audio-virtual', 'si-audio-virtual.c',
dependencies: common_deps, c_args: common_args), dependencies: common_deps, c_args: common_args),
env: common_env, env: common_env,
) )

View File

@@ -13,7 +13,7 @@ typedef struct {
} TestFixture; } TestFixture;
static void 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); 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; g_autoptr (GError) error = NULL;
wp_core_load_component (f->base.core, 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); g_assert_no_error (error);
} }
} }
static void 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); wp_base_test_fixture_teardown (&f->base);
} }
static void static void
test_si_audio_endpoint_configure_activate (TestFixture * f, test_si_audio_virtual_configure_activate (TestFixture * f,
gconstpointer user_data) gconstpointer user_data)
{ {
g_autoptr (WpSessionItem) endpoint = NULL; g_autoptr (WpSessionItem) item = NULL;
/* skip the test if null-audio-sink factory is not installed */ /* skip the test if null-audio-sink factory is not installed */
if (!test_is_spa_lib_installed (&f->base, "support.null-audio-sink")) { 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; return;
} }
/* create endpoint */ /* create item */
endpoint = wp_session_item_make (f->base.core, "si-audio-endpoint"); item = wp_session_item_make (f->base.core, "si-audio-virtual");
g_assert_nonnull (endpoint); g_assert_nonnull (item);
/* configure endpoint */ /* configure item */
{ {
WpProperties *props = wp_properties_new_empty (); 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"); wp_properties_set (props, "media.class", "Audio/Source");
g_assert_true (wp_session_item_configure (endpoint, props)); g_assert_true (wp_session_item_configure (item, props));
g_assert_true (wp_session_item_is_configured (endpoint)); g_assert_true (wp_session_item_is_configured (item));
} }
{ {
const gchar *str = NULL; 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); g_assert_nonnull (props);
str = wp_properties_get (props, "name"); str = wp_properties_get (props, "name");
g_assert_nonnull (str); g_assert_nonnull (str);
g_assert_cmpstr ("endpoint", ==, str); g_assert_cmpstr ("virtual", ==, str);
str = wp_properties_get (props, "direction"); str = wp_properties_get (props, "direction");
g_assert_nonnull (str); g_assert_nonnull (str);
g_assert_cmpstr ("1", ==, str); g_assert_cmpstr ("1", ==, str);
str = wp_properties_get (props, "item.factory.name"); str = wp_properties_get (props, "item.factory.name");
g_assert_nonnull (str); 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); NULL, (GAsyncReadyCallback) test_object_activate_finish_cb, f);
g_main_loop_run (f->base.loop); 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_ACTIVE);
/* reset */ /* reset */
wp_session_item_reset (endpoint); wp_session_item_reset (item);
g_assert_false (wp_session_item_is_configured (endpoint)); g_assert_false (wp_session_item_is_configured (item));
} }
static void 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 (WpObjectManager) clients_om = NULL;
g_autoptr (WpClient) self_client = NULL; g_autoptr (WpClient) self_client = NULL;
@@ -115,8 +115,6 @@ test_si_audio_endpoint_export (TestFixture * f, gconstpointer user_data)
return; return;
} }
/* find self_client, to be used for verifying endpoint.client.id */
clients_om = wp_object_manager_new (); clients_om = wp_object_manager_new ();
wp_object_manager_add_interest (clients_om, WP_TYPE_CLIENT, NULL); wp_object_manager_add_interest (clients_om, WP_TYPE_CLIENT, NULL);
wp_object_manager_request_object_features (clients_om, 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); self_client = wp_object_manager_lookup (clients_om, WP_TYPE_CLIENT, NULL);
g_assert_nonnull (self_client); g_assert_nonnull (self_client);
/* create endpoint */ /* create item */
endpoint = wp_session_item_make (f->base.core, "si-audio-endpoint"); item = wp_session_item_make (f->base.core, "si-audio-virtual");
g_assert_nonnull (endpoint); g_assert_nonnull (item);
/* configure endpoint */ /* configure item */
{ {
WpProperties *props = wp_properties_new_empty (); 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"); wp_properties_set (props, "media.class", "Audio/Source");
g_assert_true (wp_session_item_configure (endpoint, props)); g_assert_true (wp_session_item_configure (item, props));
g_assert_true (wp_session_item_is_configured (endpoint)); 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, WP_SESSION_ITEM_FEATURE_ACTIVE | WP_SESSION_ITEM_FEATURE_EXPORTED,
NULL, (GAsyncReadyCallback) test_object_activate_finish_cb, f); NULL, (GAsyncReadyCallback) test_object_activate_finish_cb, f);
g_main_loop_run (f->base.loop); 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); 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 (WpNode) n = NULL;
g_autoptr (WpProperties) props = 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); g_assert_nonnull (n);
props = wp_pipewire_object_get_properties (WP_PIPEWIRE_OBJECT (n)); props = wp_pipewire_object_get_properties (WP_PIPEWIRE_OBJECT (n));
g_assert_nonnull (props); g_assert_nonnull (props);
@@ -167,8 +165,8 @@ test_si_audio_endpoint_export (TestFixture * f, gconstpointer user_data)
} }
/* reset */ /* reset */
wp_session_item_reset (endpoint); wp_session_item_reset (item);
g_assert_false (wp_session_item_is_configured (endpoint)); g_assert_false (wp_session_item_is_configured (item));
} }
gint gint
@@ -178,18 +176,18 @@ main (gint argc, gchar *argv[])
wp_init (WP_INIT_ALL); wp_init (WP_INIT_ALL);
/* configure-activate */ /* configure-activate */
g_test_add ("/modules/si-audio-endpoint/configure-activate", g_test_add ("/modules/si-audio-virtual/configure-activate",
TestFixture, NULL, TestFixture, NULL,
test_si_audio_endpoint_setup, test_si_audio_virtual_setup,
test_si_audio_endpoint_configure_activate, test_si_audio_virtual_configure_activate,
test_si_audio_endpoint_teardown); test_si_audio_virtual_teardown);
/* export */ /* export */
g_test_add ("/modules/si-audio-endpoint/export", g_test_add ("/modules/si-audio-virtual/export",
TestFixture, NULL, TestFixture, NULL,
test_si_audio_endpoint_setup, test_si_audio_virtual_setup,
test_si_audio_endpoint_export, test_si_audio_virtual_export,
test_si_audio_endpoint_teardown); test_si_audio_virtual_teardown);
return g_test_run (); return g_test_run ();
} }

View File

@@ -112,13 +112,13 @@ test_si_standard_link_main (TestFixture * f, gconstpointer user_data)
{ {
g_autoptr (WpSessionItem) link = NULL; 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) { if (!f->src_item) {
g_test_skip ("The pipewire audiotestsrc factory was not found"); g_test_skip ("The pipewire audiotestsrc factory was not found");
return; 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) { if (!f->sink_item) {
g_test_skip ("The pipewire null-audio-sink factory was not found"); g_test_skip ("The pipewire null-audio-sink factory was not found");
return; return;

View File

@@ -209,7 +209,7 @@ load_components (ScriptRunnerFixture *f, gconstpointer argv)
load_component (f, "si-audio-adapter", "module"); load_component (f, "si-audio-adapter", "module");
load_component (f, "si-standard-link", "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, "metadata", "module");
load_component (f, "default-nodes-api", "module"); load_component (f, "default-nodes-api", "module");