mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-06 08:18:03 +02:00
Merge branch 'error-handling' into 'master'
Error handling and fixes See merge request gkiagia/wireplumber!21
This commit is contained in:
commit
ecfcbd7b6d
13 changed files with 289 additions and 83 deletions
|
|
@ -6,6 +6,7 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "error.h"
|
||||
#include "proxy-link.h"
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ struct _WpProxyLink
|
|||
|
||||
/* The task to signal the proxy is initialized */
|
||||
GTask *init_task;
|
||||
|
||||
|
||||
/* The link proxy listener */
|
||||
struct spa_hook listener;
|
||||
|
||||
|
|
@ -69,6 +70,22 @@ wp_proxy_link_finalize (GObject * object)
|
|||
G_OBJECT_CLASS (wp_proxy_link_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_link_destroy (WpProxy * proxy)
|
||||
{
|
||||
WpProxyLink *self = WP_PROXY_LINK(proxy);
|
||||
GError *error = NULL;
|
||||
|
||||
/* Return error if the pipewire destruction happened while the async creation
|
||||
* of this proxy link object has not finished */
|
||||
if (self->init_task) {
|
||||
g_set_error (&error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"pipewire link proxy destroyed before finishing");
|
||||
g_task_return_error (self->init_task, error);
|
||||
g_clear_object (&self->init_task);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_link_init_async (GAsyncInitable *initable, int io_priority,
|
||||
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
|
||||
|
|
@ -105,8 +122,11 @@ static void
|
|||
wp_proxy_link_class_init (WpProxyLinkClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_proxy_link_finalize;
|
||||
|
||||
proxy_class->destroy = wp_proxy_link_destroy;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "error.h"
|
||||
#include "proxy-node.h"
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ struct _WpProxyNode
|
|||
|
||||
/* The task to signal the proxy is initialized */
|
||||
GTask *init_task;
|
||||
|
||||
|
||||
/* The node proxy listener */
|
||||
struct spa_hook listener;
|
||||
|
||||
|
|
@ -69,6 +70,22 @@ wp_proxy_node_finalize (GObject * object)
|
|||
G_OBJECT_CLASS (wp_proxy_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_node_destroy (WpProxy * proxy)
|
||||
{
|
||||
WpProxyNode *self = WP_PROXY_NODE(proxy);
|
||||
GError *error = NULL;
|
||||
|
||||
/* Return error if the pipewire destruction happened while the async creation
|
||||
* of this proxy node object has not finished */
|
||||
if (self->init_task) {
|
||||
g_set_error (&error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"pipewire node proxy destroyed before finishing");
|
||||
g_task_return_error (self->init_task, error);
|
||||
g_clear_object (&self->init_task);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_node_init_async (GAsyncInitable *initable, int io_priority,
|
||||
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
|
||||
|
|
@ -105,8 +122,11 @@ static void
|
|||
wp_proxy_node_class_init (WpProxyNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_proxy_node_finalize;
|
||||
|
||||
proxy_class->destroy = wp_proxy_node_destroy;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "error.h"
|
||||
#include "proxy-port.h"
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spa/param/audio/format-utils.h>
|
||||
|
|
@ -99,6 +100,22 @@ wp_proxy_port_finalize (GObject * object)
|
|||
G_OBJECT_CLASS (wp_proxy_port_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_port_destroy (WpProxy * proxy)
|
||||
{
|
||||
WpProxyPort *self = WP_PROXY_PORT(proxy);
|
||||
GError *error = NULL;
|
||||
|
||||
/* Return error if the pipewire destruction happened while the async creation
|
||||
* of this proxy port object has not finished */
|
||||
if (self->init_task) {
|
||||
g_set_error (&error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"pipewire port proxy destroyed before finishing");
|
||||
g_task_return_error (self->init_task, error);
|
||||
g_clear_object (&self->init_task);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_port_init_async (GAsyncInitable *initable, int io_priority,
|
||||
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
|
||||
|
|
@ -139,8 +156,11 @@ static void
|
|||
wp_proxy_port_class_init (WpProxyPortClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_proxy_port_finalize;
|
||||
|
||||
proxy_class->destroy = wp_proxy_port_destroy;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -46,10 +46,15 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (WpProxy, wp_proxy, G_TYPE_OBJECT,
|
|||
static void
|
||||
proxy_event_destroy (void *data)
|
||||
{
|
||||
WpProxyPrivate *self = wp_proxy_get_instance_private (WP_PROXY(data));
|
||||
WpProxy *self = WP_PROXY (data);
|
||||
WpProxyPrivate *priv = wp_proxy_get_instance_private (self);
|
||||
|
||||
/* Set the proxy to NULL */
|
||||
self->proxy = NULL;
|
||||
priv->proxy = NULL;
|
||||
|
||||
/* Call the destroy method */
|
||||
if (WP_PROXY_GET_CLASS (self)->destroy)
|
||||
WP_PROXY_GET_CLASS (self)->destroy (self);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -68,24 +73,24 @@ static const struct pw_proxy_events proxy_events = {
|
|||
static void
|
||||
wp_proxy_constructed (GObject * object)
|
||||
{
|
||||
WpProxyPrivate *self = wp_proxy_get_instance_private (WP_PROXY(object));
|
||||
WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object));
|
||||
|
||||
/* Add the event listener */
|
||||
pw_proxy_add_listener (self->proxy, &self->listener, &proxy_events, object);
|
||||
pw_proxy_add_listener (priv->proxy, &priv->listener, &proxy_events, object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_finalize (GObject * object)
|
||||
{
|
||||
WpProxyPrivate *self = wp_proxy_get_instance_private (WP_PROXY(object));
|
||||
WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object));
|
||||
|
||||
g_debug ("%s:%p destroyed (pw proxy %p)", G_OBJECT_TYPE_NAME (object),
|
||||
object, self->proxy);
|
||||
object, priv->proxy);
|
||||
|
||||
/* Destroy the proxy */
|
||||
if (self->proxy) {
|
||||
pw_proxy_destroy (self->proxy);
|
||||
self->proxy = NULL;
|
||||
if (priv->proxy) {
|
||||
pw_proxy_destroy (priv->proxy);
|
||||
priv->proxy = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (wp_proxy_parent_class)->finalize (object);
|
||||
|
|
@ -95,14 +100,14 @@ static void
|
|||
wp_proxy_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpProxyPrivate *self = wp_proxy_get_instance_private (WP_PROXY(object));
|
||||
WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object));
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_GLOBAL_ID:
|
||||
self->global_id = g_value_get_uint (value);
|
||||
priv->global_id = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_PROXY:
|
||||
self->proxy = g_value_get_pointer (value);
|
||||
priv->proxy = g_value_get_pointer (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
|
|
@ -114,14 +119,14 @@ static void
|
|||
wp_proxy_get_property (GObject * object, guint property_id, GValue * value,
|
||||
GParamSpec * pspec)
|
||||
{
|
||||
WpProxyPrivate *self = wp_proxy_get_instance_private (WP_PROXY(object));
|
||||
WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object));
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_GLOBAL_ID:
|
||||
g_value_set_uint (value, self->global_id);
|
||||
g_value_set_uint (value, priv->global_id);
|
||||
break;
|
||||
case PROP_PROXY:
|
||||
g_value_set_pointer (value, self->proxy);
|
||||
g_value_set_pointer (value, priv->proxy);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ struct _WpProxyClass
|
|||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* Methods */
|
||||
void (*destroy) (WpProxy * self);
|
||||
|
||||
/* Signals */
|
||||
void (*done)(WpProxy *wp_proxy, gpointer data);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -229,9 +229,11 @@ simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data,
|
|||
if (in_direction == PW_DIRECTION_OUTPUT)
|
||||
continue;
|
||||
|
||||
/* Skip the port if it is already linked */
|
||||
/* Skip the ports if they are already linked */
|
||||
if (g_hash_table_contains (linked_ports, GUINT_TO_POINTER(in_id)))
|
||||
continue;
|
||||
if (g_hash_table_contains (linked_ports, GUINT_TO_POINTER(out_id)))
|
||||
continue;
|
||||
|
||||
/* Create the properties */
|
||||
props = pw_properties_new(NULL, NULL);
|
||||
|
|
@ -248,8 +250,9 @@ simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data,
|
|||
self);
|
||||
self->link_count++;
|
||||
|
||||
/* Insert the port id in the hash table to know it is linked */
|
||||
/* Insert the port ids in the hash tables to know they are linked */
|
||||
g_hash_table_insert (linked_ports, GUINT_TO_POINTER(in_id), NULL);
|
||||
g_hash_table_insert (linked_ports, GUINT_TO_POINTER(out_id), NULL);
|
||||
|
||||
/* Clean up */
|
||||
pw_properties_free(props);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ struct _WpPipewireSimpleEndpoint
|
|||
|
||||
/* The task to signal the endpoint is initialized */
|
||||
GTask *init_task;
|
||||
gboolean init_abort;
|
||||
|
||||
/* The remote pipewire */
|
||||
WpRemotePipewire *remote_pipewire;
|
||||
|
|
@ -74,6 +75,37 @@ G_DEFINE_TYPE_WITH_CODE (WpPipewireSimpleEndpoint, simple_endpoint,
|
|||
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
|
||||
wp_simple_endpoint_async_initable_init))
|
||||
|
||||
typedef GObject* (*WpObjectNewFinishFunc)(GObject *initable, GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
static GObject *
|
||||
object_safe_new_finish(WpPipewireSimpleEndpoint * self, GObject *initable,
|
||||
GAsyncResult *res, WpObjectNewFinishFunc new_finish_func)
|
||||
{
|
||||
GObject *object = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
/* Return NULL if we are already aborting */
|
||||
if (self->init_abort)
|
||||
return NULL;
|
||||
|
||||
/* Get the object */
|
||||
object = G_OBJECT (new_finish_func (initable, res, &error));
|
||||
g_return_val_if_fail (object, NULL);
|
||||
|
||||
/* Check for error */
|
||||
if (error) {
|
||||
g_clear_object (&object);
|
||||
g_warning ("WpPipewireSimpleEndpoint:%p Aborting construction", self);
|
||||
self->init_abort = TRUE;
|
||||
g_task_return_error (self->init_task, error);
|
||||
g_clear_object (&self->init_task);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static void
|
||||
node_proxy_param (void *object, int seq, uint32_t id,
|
||||
uint32_t index, uint32_t next, const struct spa_pod *param)
|
||||
|
|
@ -146,8 +178,10 @@ on_proxy_port_created(GObject *initable, GAsyncResult *res, gpointer data)
|
|||
WpProxyPort *proxy_port = NULL;
|
||||
|
||||
/* Get the proxy port */
|
||||
proxy_port = wp_proxy_port_new_finish(initable, res, NULL);
|
||||
g_return_if_fail (proxy_port);
|
||||
proxy_port = WP_PROXY_PORT (object_safe_new_finish (self, initable, res,
|
||||
(WpObjectNewFinishFunc)wp_proxy_port_new_finish));
|
||||
if (!proxy_port)
|
||||
return;
|
||||
|
||||
/* Add the proxy port to the array */
|
||||
g_return_if_fail (self->proxies_port);
|
||||
|
|
@ -168,6 +202,10 @@ on_port_added(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
|
|||
WpPipewireSimpleEndpoint *self = d;
|
||||
struct pw_port_proxy *port_proxy = NULL;
|
||||
|
||||
/* Don't do anything if we are aborting */
|
||||
if (self->init_abort)
|
||||
return;
|
||||
|
||||
/* Only handle ports owned by this endpoint */
|
||||
if (parent_id != self->global_id)
|
||||
return;
|
||||
|
|
@ -185,23 +223,22 @@ emit_endpoint_ports(WpPipewireSimpleEndpoint *self)
|
|||
struct pw_node_proxy* node_proxy = NULL;
|
||||
struct spa_audio_info_raw format = { 0, };
|
||||
struct spa_pod *param;
|
||||
struct spa_pod_builder pod_builder = { 0, };
|
||||
char buf[1024];
|
||||
struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||
|
||||
/* Get the pipewire node proxy */
|
||||
node_proxy = wp_proxy_get_pw_proxy(WP_PROXY(self->proxy_node));
|
||||
g_return_if_fail (node_proxy);
|
||||
|
||||
/* TODO: Assume all clients have this format for now */
|
||||
/* The default format for audio clients */
|
||||
format.format = SPA_AUDIO_FORMAT_F32P;
|
||||
format.flags = 1;
|
||||
format.rate = 48000;
|
||||
format.channels = 2;
|
||||
format.position[0] = 0;
|
||||
format.position[1] = 0;
|
||||
format.position[0] = SPA_AUDIO_CHANNEL_FL;
|
||||
format.position[1] = SPA_AUDIO_CHANNEL_FR;
|
||||
|
||||
/* Build the param profile */
|
||||
spa_pod_builder_init(&pod_builder, buf, sizeof(buf));
|
||||
param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format);
|
||||
param = spa_pod_builder_add_object(&pod_builder,
|
||||
SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
|
||||
|
|
@ -222,8 +259,10 @@ on_proxy_node_created(GObject *initable, GAsyncResult *res, gpointer data)
|
|||
struct pw_node_proxy *node_proxy = NULL;
|
||||
|
||||
/* Get the proxy node */
|
||||
self->proxy_node = wp_proxy_node_new_finish(initable, res, NULL);
|
||||
g_return_if_fail (self->proxy_node);
|
||||
self->proxy_node = WP_PROXY_NODE (object_safe_new_finish (self, initable,
|
||||
res, (WpObjectNewFinishFunc)wp_proxy_node_new_finish));
|
||||
if (!self->proxy_node)
|
||||
return;
|
||||
|
||||
self->role = g_strdup (spa_dict_lookup (
|
||||
wp_proxy_node_get_info (self->proxy_node)->props, "media.role"));
|
||||
|
|
@ -319,6 +358,7 @@ wp_simple_endpoint_async_initable_init (gpointer iface, gpointer iface_data)
|
|||
static void
|
||||
simple_endpoint_init (WpPipewireSimpleEndpoint * self)
|
||||
{
|
||||
self->init_abort = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -29,11 +29,18 @@ on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d)
|
|||
struct impl *impl = d;
|
||||
WpEndpoint *endpoint = NULL;
|
||||
guint global_id = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
/* Get the endpoint */
|
||||
endpoint = wp_endpoint_new_finish(initable, res, NULL);
|
||||
if (!endpoint)
|
||||
g_return_if_fail (endpoint);
|
||||
|
||||
/* Check for error */
|
||||
if (error) {
|
||||
g_clear_object (&endpoint);
|
||||
g_warning ("Failed to create alsa endpoint: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the endpoint global id */
|
||||
g_object_get (endpoint, "global-id", &global_id, NULL);
|
||||
|
|
|
|||
|
|
@ -27,11 +27,18 @@ on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d)
|
|||
struct module_data *data = d;
|
||||
WpEndpoint *endpoint = NULL;
|
||||
guint global_id = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
/* Get the endpoint */
|
||||
endpoint = wp_endpoint_new_finish(initable, res, NULL);
|
||||
if (!endpoint)
|
||||
g_return_if_fail (endpoint);
|
||||
|
||||
/* Check for error */
|
||||
if (error) {
|
||||
g_clear_object (&endpoint);
|
||||
g_warning ("Failed to create client endpoint: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the endpoint global id */
|
||||
g_object_get (endpoint, "global-id", &global_id, NULL);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ struct _WpPwAudioSoftdspEndpoint
|
|||
|
||||
/* The task to signal the endpoint is initialized */
|
||||
GTask *init_task;
|
||||
gboolean init_abort;
|
||||
|
||||
/* The remote pipewire */
|
||||
WpRemotePipewire *remote_pipewire;
|
||||
|
|
@ -71,6 +72,37 @@ G_DEFINE_TYPE_WITH_CODE (WpPwAudioSoftdspEndpoint, endpoint, WP_TYPE_ENDPOINT,
|
|||
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
|
||||
wp_endpoint_async_initable_init))
|
||||
|
||||
typedef GObject* (*WpObjectNewFinishFunc)(GObject *initable, GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
static GObject *
|
||||
object_safe_new_finish(WpPwAudioSoftdspEndpoint * self, GObject *initable,
|
||||
GAsyncResult *res, WpObjectNewFinishFunc new_finish_func)
|
||||
{
|
||||
GObject *object = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
/* Return NULL if we are already aborting */
|
||||
if (self->init_abort)
|
||||
return NULL;
|
||||
|
||||
/* Get the object */
|
||||
object = G_OBJECT (new_finish_func (initable, res, &error));
|
||||
g_return_val_if_fail (object, NULL);
|
||||
|
||||
/* Check for error */
|
||||
if (error) {
|
||||
g_clear_object (&object);
|
||||
g_warning ("WpPwAudioSoftdspEndpoint:%p Aborting construction", self);
|
||||
self->init_abort = TRUE;
|
||||
g_task_return_error (self->init_task, error);
|
||||
g_clear_object (&self->init_task);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
endpoint_prepare_link (WpEndpoint * ep, guint32 stream_id,
|
||||
WpEndpointLink * link, GVariant ** properties, GError ** error)
|
||||
|
|
@ -110,8 +142,10 @@ on_audio_dsp_stream_created(GObject *initable, GAsyncResult *res, gpointer data)
|
|||
g_autofree gchar *name = NULL;
|
||||
|
||||
/* Get the stream */
|
||||
dsp = wp_pw_audio_dsp_new_finish(initable, res, NULL);
|
||||
g_return_if_fail (dsp);
|
||||
dsp = WP_PW_AUDIO_DSP (object_safe_new_finish (self, initable, res,
|
||||
(WpObjectNewFinishFunc)wp_pw_audio_dsp_new_finish));
|
||||
if (!dsp)
|
||||
return;
|
||||
|
||||
/* Get the stream id */
|
||||
g_object_get (dsp, "id", &stream_id, "name", &name, NULL);
|
||||
|
|
@ -135,27 +169,26 @@ on_audio_dsp_converter_created(GObject *initable, GAsyncResult *res,
|
|||
WpPwAudioSoftdspEndpoint *self = data;
|
||||
g_autoptr (WpCore) core = wp_endpoint_get_core(WP_ENDPOINT(self));
|
||||
const struct pw_node_info *target = NULL;
|
||||
const struct spa_audio_info_raw *format = NULL;
|
||||
GVariantDict d;
|
||||
GVariantIter iter;
|
||||
const gchar *stream;
|
||||
int i;
|
||||
|
||||
/* Get the proxy dsp converter */
|
||||
self->converter = wp_pw_audio_dsp_new_finish(initable, res, NULL);
|
||||
g_return_if_fail (self->converter);
|
||||
self->converter = WP_PW_AUDIO_DSP (object_safe_new_finish (self, initable,
|
||||
res, (WpObjectNewFinishFunc)wp_pw_audio_dsp_new_finish));
|
||||
if (!self->converter)
|
||||
return;
|
||||
|
||||
/* Get the target and format */
|
||||
target = wp_pw_audio_dsp_get_info (self->converter);
|
||||
g_return_if_fail (target);
|
||||
g_object_get (self->converter, "format", &format, NULL);
|
||||
g_return_if_fail (format);
|
||||
|
||||
/* Create the audio dsp streams */
|
||||
g_variant_iter_init (&iter, self->streams);
|
||||
for (i = 0; g_variant_iter_next (&iter, "&s", &stream); i++) {
|
||||
wp_pw_audio_dsp_new (WP_ENDPOINT(self), i, stream, self->direction,
|
||||
FALSE, target, format, on_audio_dsp_stream_created, self);
|
||||
wp_pw_audio_dsp_new (WP_ENDPOINT(self), i, stream, self->direction, FALSE,
|
||||
target, on_audio_dsp_stream_created, self);
|
||||
|
||||
/* Register the stream */
|
||||
g_variant_dict_init (&d, NULL);
|
||||
|
|
@ -174,11 +207,12 @@ on_proxy_node_created(GObject *initable, GAsyncResult *res, gpointer data)
|
|||
g_autofree gchar *name = NULL;
|
||||
const struct spa_dict *props;
|
||||
const struct pw_node_info *target = NULL;
|
||||
const struct spa_audio_info_raw *format = NULL;
|
||||
|
||||
/* Get the proxy node */
|
||||
self->proxy_node = wp_proxy_node_new_finish(initable, res, NULL);
|
||||
g_return_if_fail (self->proxy_node);
|
||||
self->proxy_node = WP_PROXY_NODE (object_safe_new_finish (self, initable,
|
||||
res, (WpObjectNewFinishFunc)wp_proxy_node_new_finish));
|
||||
if (!self->proxy_node)
|
||||
return;
|
||||
|
||||
/* Give a proper name to this endpoint based on ALSA properties */
|
||||
props = wp_proxy_node_get_info (self->proxy_node)->props;
|
||||
|
|
@ -192,13 +226,8 @@ on_proxy_node_created(GObject *initable, GAsyncResult *res, gpointer data)
|
|||
/* Create the converter proxy */
|
||||
target = wp_proxy_node_get_info (self->proxy_node);
|
||||
g_return_if_fail (target);
|
||||
format = wp_proxy_port_get_format (self->proxy_port);
|
||||
g_return_if_fail (format);
|
||||
/* TODO: For now we create convert as a stream because convert mode does not
|
||||
* generate any ports, not sure why */
|
||||
wp_pw_audio_dsp_new (WP_ENDPOINT(self), WP_STREAM_ID_NONE, "master",
|
||||
self->direction, TRUE, target, format, on_audio_dsp_converter_created,
|
||||
self);
|
||||
self->direction, TRUE, target, on_audio_dsp_converter_created, self);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -208,8 +237,10 @@ on_proxy_port_created(GObject *initable, GAsyncResult *res, gpointer data)
|
|||
struct pw_node_proxy *node_proxy = NULL;
|
||||
|
||||
/* Get the proxy port */
|
||||
self->proxy_port = wp_proxy_port_new_finish(initable, res, NULL);
|
||||
g_return_if_fail (self->proxy_port);
|
||||
self->proxy_port = WP_PROXY_PORT (object_safe_new_finish (self, initable, res,
|
||||
(WpObjectNewFinishFunc)wp_proxy_port_new_finish));
|
||||
if (!self->proxy_port)
|
||||
return;
|
||||
|
||||
/* Create the proxy node async */
|
||||
node_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire,
|
||||
|
|
@ -225,6 +256,10 @@ on_port_added(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
|
|||
WpPwAudioSoftdspEndpoint *self = d;
|
||||
struct pw_port_proxy *port_proxy = NULL;
|
||||
|
||||
/* Don't do anything if we are aborting */
|
||||
if (self->init_abort)
|
||||
return;
|
||||
|
||||
/* Check if it is a node port and handle it */
|
||||
if (self->global_id != parent_id)
|
||||
return;
|
||||
|
|
@ -407,6 +442,7 @@ wp_endpoint_async_initable_init (gpointer iface, gpointer iface_data)
|
|||
static void
|
||||
endpoint_init (WpPwAudioSoftdspEndpoint * self)
|
||||
{
|
||||
self->init_abort = FALSE;
|
||||
self->dsps = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ enum {
|
|||
PROP_DIRECTION,
|
||||
PROP_CONVERT,
|
||||
PROP_TARGET,
|
||||
PROP_FORMAT,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
@ -39,6 +38,7 @@ struct _WpPwAudioDsp
|
|||
|
||||
/* The task to signal the audio dsp is initialized */
|
||||
GTask *init_task;
|
||||
gboolean init_abort;
|
||||
|
||||
/* The remote pipewire */
|
||||
WpRemotePipewire *remote_pipewire;
|
||||
|
|
@ -50,7 +50,6 @@ struct _WpPwAudioDsp
|
|||
enum pw_direction direction;
|
||||
gboolean convert;
|
||||
const struct pw_node_info *target;
|
||||
const struct spa_audio_info_raw *format;
|
||||
|
||||
/* All ports handled by the port added callback */
|
||||
GHashTable *handled_ports;
|
||||
|
|
@ -58,7 +57,7 @@ struct _WpPwAudioDsp
|
|||
/* Proxies */
|
||||
WpProxyNode *proxy;
|
||||
GPtrArray *port_proxies;
|
||||
struct pw_proxy *link_proxy;
|
||||
WpProxyLink *link_proxy;
|
||||
|
||||
/* Listener */
|
||||
struct spa_hook listener;
|
||||
|
|
@ -75,6 +74,37 @@ G_DEFINE_TYPE_WITH_CODE (WpPwAudioDsp, wp_pw_audio_dsp, G_TYPE_OBJECT,
|
|||
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
|
||||
wp_pw_audio_dsp_async_initable_init))
|
||||
|
||||
typedef GObject* (*WpObjectNewFinishFunc)(GObject *initable, GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
static GObject *
|
||||
object_safe_new_finish(WpPwAudioDsp * self, GObject *initable,
|
||||
GAsyncResult *res, WpObjectNewFinishFunc new_finish_func)
|
||||
{
|
||||
GObject *object = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
/* Return NULL if we are already aborting */
|
||||
if (self->init_abort)
|
||||
return NULL;
|
||||
|
||||
/* Get the object */
|
||||
object = G_OBJECT (new_finish_func (initable, res, &error));
|
||||
g_return_val_if_fail (object, NULL);
|
||||
|
||||
/* Check for error */
|
||||
if (error) {
|
||||
g_clear_object (&object);
|
||||
g_warning ("WpPwAudioDsp:%p Aborting construction", self);
|
||||
self->init_abort = TRUE;
|
||||
g_task_return_error (self->init_task, error);
|
||||
g_clear_object (&self->init_task);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
guint
|
||||
wp_pw_audio_dsp_id_encode (guint stream_id, guint control_id)
|
||||
{
|
||||
|
|
@ -147,8 +177,10 @@ on_audio_dsp_port_created(GObject *initable, GAsyncResult *res,
|
|||
WpProxyPort *port_proxy = NULL;
|
||||
|
||||
/* Get the proxy port */
|
||||
port_proxy = wp_proxy_port_new_finish(initable, res, NULL);
|
||||
g_return_if_fail (port_proxy);
|
||||
port_proxy = WP_PROXY_PORT (object_safe_new_finish (self, initable, res,
|
||||
(WpObjectNewFinishFunc)wp_proxy_port_new_finish));
|
||||
if (!port_proxy)
|
||||
return;
|
||||
|
||||
/* Add the proxy port to the array */
|
||||
g_return_if_fail (self->port_proxies);
|
||||
|
|
@ -164,6 +196,10 @@ handled_ports_foreach_func (gpointer key, gpointer value, gpointer data)
|
|||
const guint id = GPOINTER_TO_INT (key);
|
||||
const guint parent_id = GPOINTER_TO_INT (value);
|
||||
|
||||
/* Don't do anything if we are aborting */
|
||||
if (self->init_abort)
|
||||
return;
|
||||
|
||||
/* Get the dsp info */
|
||||
g_return_if_fail (self->proxy);
|
||||
dsp_info = wp_proxy_node_get_info(self->proxy);
|
||||
|
|
@ -218,11 +254,23 @@ on_audio_dsp_port_added(WpRemotePipewire *rp, guint id, guint parent_id,
|
|||
GUINT_TO_POINTER(parent_id));
|
||||
}
|
||||
|
||||
static void
|
||||
on_proxy_link_created(GObject *initable, GAsyncResult *res, gpointer data)
|
||||
{
|
||||
WpPwAudioDsp *self = data;
|
||||
|
||||
/* Get the link */
|
||||
self->link_proxy = WP_PROXY_LINK (object_safe_new_finish (self, initable,
|
||||
res, (WpObjectNewFinishFunc)wp_proxy_link_new_finish));
|
||||
g_return_if_fail (self->link_proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
on_audio_dsp_running(WpPwAudioDsp *self)
|
||||
{
|
||||
struct pw_properties *props;
|
||||
const struct pw_node_info *dsp_info = NULL;
|
||||
struct pw_proxy *proxy = NULL;
|
||||
|
||||
/* Return if the node has already been linked */
|
||||
if (self->link_proxy)
|
||||
|
|
@ -252,8 +300,10 @@ on_audio_dsp_running(WpPwAudioDsp *self)
|
|||
g_debug ("%p linking DSP to node", self);
|
||||
|
||||
/* Create the link */
|
||||
self->link_proxy = wp_remote_pipewire_create_object(self->remote_pipewire,
|
||||
proxy = wp_remote_pipewire_create_object(self->remote_pipewire,
|
||||
"link-factory", PW_TYPE_INTERFACE_Link, &props->dict);
|
||||
wp_proxy_link_new (pw_proxy_get_id(proxy), proxy, on_proxy_link_created,
|
||||
self);
|
||||
|
||||
/* Clean up */
|
||||
pw_properties_free(props);
|
||||
|
|
@ -262,10 +312,8 @@ on_audio_dsp_running(WpPwAudioDsp *self)
|
|||
static void
|
||||
on_audio_dsp_idle (WpPwAudioDsp *self)
|
||||
{
|
||||
if (self->link_proxy != NULL) {
|
||||
pw_proxy_destroy (self->link_proxy);
|
||||
self->link_proxy = NULL;
|
||||
}
|
||||
/* Clear the proxy */
|
||||
g_clear_object (&self->link_proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -351,12 +399,14 @@ on_audio_dsp_proxy_created(GObject *initable, GAsyncResult *res,
|
|||
struct pw_node_proxy *pw_proxy = NULL;
|
||||
struct spa_audio_info_raw format;
|
||||
uint8_t buf[1024];
|
||||
struct spa_pod_builder pod_builder = { 0, };
|
||||
struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||
struct spa_pod *param;
|
||||
|
||||
/* Get the audio dsp proxy */
|
||||
self->proxy = wp_proxy_node_new_finish(initable, res, NULL);
|
||||
g_return_if_fail (self->proxy);
|
||||
self->proxy = WP_PROXY_NODE (object_safe_new_finish (self, initable,
|
||||
res, (WpObjectNewFinishFunc)wp_proxy_node_new_finish));
|
||||
if (!self->proxy)
|
||||
return;
|
||||
|
||||
/* Add a custom dsp listener */
|
||||
pw_proxy = wp_proxy_get_pw_proxy(WP_PROXY(self->proxy));
|
||||
|
|
@ -368,12 +418,15 @@ on_audio_dsp_proxy_created(GObject *initable, GAsyncResult *res,
|
|||
pw_node_proxy_enum_params (pw_proxy, 0, SPA_PARAM_Props, 0, -1, NULL);
|
||||
|
||||
if (!self->convert) {
|
||||
/* Get the port format */
|
||||
g_return_if_fail (self->format);
|
||||
format = *self->format;
|
||||
/* Use the default format */
|
||||
format.format = SPA_AUDIO_FORMAT_F32P;
|
||||
format.flags = 1;
|
||||
format.rate = 48000;
|
||||
format.channels = 2;
|
||||
format.position[0] = SPA_AUDIO_CHANNEL_FL;
|
||||
format.position[1] = SPA_AUDIO_CHANNEL_FR;
|
||||
|
||||
/* Emit the ports */
|
||||
spa_pod_builder_init(&pod_builder, buf, sizeof(buf));
|
||||
param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format);
|
||||
param = spa_pod_builder_add_object(&pod_builder,
|
||||
SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
|
||||
|
|
@ -413,6 +466,9 @@ wp_pw_audio_dsp_finalize (GObject * object)
|
|||
self->port_proxies = NULL;
|
||||
}
|
||||
|
||||
/* Destroy the link proxy */
|
||||
g_clear_object (&self->link_proxy);
|
||||
|
||||
G_OBJECT_CLASS (wp_pw_audio_dsp_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
|
@ -441,9 +497,6 @@ wp_pw_audio_dsp_set_property (GObject * object, guint property_id,
|
|||
case PROP_TARGET:
|
||||
self->target = g_value_get_pointer(value);
|
||||
break;
|
||||
case PROP_FORMAT:
|
||||
self->format = g_value_get_pointer(value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
|
|
@ -475,9 +528,6 @@ wp_pw_audio_dsp_get_property (GObject * object, guint property_id,
|
|||
case PROP_TARGET:
|
||||
g_value_set_pointer (value, (gpointer)self->target);
|
||||
break;
|
||||
case PROP_FORMAT:
|
||||
g_value_set_pointer (value, (gpointer)self->format);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
|
|
@ -561,6 +611,7 @@ wp_pw_audio_dsp_async_initable_init (gpointer iface, gpointer iface_data)
|
|||
static void
|
||||
wp_pw_audio_dsp_init (WpPwAudioDsp * self)
|
||||
{
|
||||
self->init_abort = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -595,17 +646,12 @@ wp_pw_audio_dsp_class_init (WpPwAudioDspClass * klass)
|
|||
g_param_spec_pointer ("target", "target",
|
||||
"The target node info of the audio DSP",
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_FORMAT,
|
||||
g_param_spec_pointer ("format", "format",
|
||||
"The format of the audio DSP ports",
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
void
|
||||
wp_pw_audio_dsp_new (WpEndpoint *endpoint, guint id, const char *name,
|
||||
enum pw_direction direction, gboolean convert,
|
||||
const struct pw_node_info *target, const struct spa_audio_info_raw *format,
|
||||
GAsyncReadyCallback callback,
|
||||
const struct pw_node_info *target, GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_async_initable_new_async (
|
||||
|
|
@ -617,7 +663,6 @@ wp_pw_audio_dsp_new (WpEndpoint *endpoint, guint id, const char *name,
|
|||
"direction", direction,
|
||||
"convert", convert,
|
||||
"target", target,
|
||||
"format", format,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ void wp_pw_audio_dsp_id_decode (guint id, guint *stream_id, guint *control_id);
|
|||
|
||||
void wp_pw_audio_dsp_new (WpEndpoint *endpoint, guint id, const char *name,
|
||||
enum pw_direction direction, gboolean convert,
|
||||
const struct pw_node_info *target, const struct spa_audio_info_raw *format,
|
||||
GAsyncReadyCallback callback, gpointer user_data);
|
||||
const struct pw_node_info *target, GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
WpPwAudioDsp * wp_pw_audio_dsp_new_finish (GObject *initable, GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
|
|
|
|||
|
|
@ -471,7 +471,7 @@ simple_policy_find_endpoint (WpPolicy *policy, GVariant *props,
|
|||
}
|
||||
|
||||
/* If not found, return the first endpoint */
|
||||
ep = (ptr_array->len > 1) ?
|
||||
ep = (ptr_array->len > 0) ?
|
||||
g_object_ref (g_ptr_array_index (ptr_array, 0)) : NULL;
|
||||
|
||||
select_stream:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue