Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Julian Bouzas
WirePlumber
Commits
acda80d7
Commit
acda80d7
authored
Dec 11, 2019
by
Julian Bouzas
Browse files
modules: add config endpoint module
parent
50f06baf
Changes
20
Hide whitespace changes
Inline
Side-by-side
modules/meson.build
View file @
acda80d7
...
...
@@ -43,26 +43,17 @@ shared_library(
)
shared_library(
'wireplumber-module-
pw-audio-clie
nt',
'wireplumber-module-
config-endpoi
nt',
[
'module-pw-audio-client.c',
'module-config-endpoint/parser-endpoint.c',
'module-config-endpoint/parser-streams.c',
'module-config-endpoint/context.c',
'module-config-endpoint.c',
],
c_args : [common_c_args, '-DG_LOG_DOMAIN="m-
pw-audio-clie
nt"'],
c_args : [common_c_args, '-DG_LOG_DOMAIN="m-
config-endpoi
nt"'],
install : true,
install_dir : wireplumber_module_dir,
dependencies : [wp_dep, pipewire_dep],
)
shared_library(
'wireplumber-module-pw-alsa-udev',
[
'module-pw-alsa-udev.c',
],
c_args : [common_c_args, '-DG_LOG_DOMAIN="m-pw-alsa-udev"'],
install : true,
install_dir : wireplumber_module_dir,
dependencies : [wp_dep, pipewire_dep],
dependencies : [wp_dep, wptoml_dep, pipewire_dep],
)
shared_library(
...
...
modules/module-config-endpoint.c
0 → 100644
View file @
acda80d7
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include
<pipewire/pipewire.h>
#include
<wp/wp.h>
#include
"module-config-endpoint/context.h"
struct
module_data
{
WpConfigEndpointContext
*
ctx
;
};
static
void
module_destroy
(
gpointer
d
)
{
struct
module_data
*
data
=
d
;
g_clear_object
(
&
data
->
ctx
);
g_slice_free
(
struct
module_data
,
data
);
}
void
wireplumber__module_init
(
WpModule
*
module
,
WpCore
*
core
,
GVariant
*
args
)
{
struct
module_data
*
data
;
/* Create the module data */
data
=
g_slice_new0
(
struct
module_data
);
data
->
ctx
=
wp_config_endpoint_context_new
(
core
);
/* Set the module destroy callback */
wp_module_set_destroy_callback
(
module
,
module_destroy
,
data
);
}
modules/module-config-endpoint/context.c
0 → 100644
View file @
acda80d7
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include
<pipewire/pipewire.h>
#include
<wp/wp.h>
#include
"parser-endpoint.h"
#include
"parser-streams.h"
#include
"context.h"
struct
_WpConfigEndpointContext
{
GObject
parent
;
/* Props */
GWeakRef
core
;
WpObjectManager
*
om
;
GHashTable
*
registered_endpoints
;
};
enum
{
PROP_0
,
PROP_CORE
,
};
enum
{
SIGNAL_ENDPOINT_CREATED
,
N_SIGNALS
};
static
guint
signals
[
N_SIGNALS
];
G_DEFINE_TYPE
(
WpConfigEndpointContext
,
wp_config_endpoint_context
,
G_TYPE_OBJECT
)
static
void
on_endpoint_created
(
GObject
*
initable
,
GAsyncResult
*
res
,
gpointer
d
)
{
WpConfigEndpointContext
*
self
=
d
;
g_autoptr
(
WpBaseEndpoint
)
endpoint
=
NULL
;
g_autoptr
(
WpProxy
)
proxy
=
NULL
;
guint
global_id
=
0
;
GError
*
error
=
NULL
;
/* Get the endpoint */
endpoint
=
wp_base_endpoint_new_finish
(
initable
,
res
,
&
error
);
if
(
error
)
{
g_warning
(
"Failed to create endpoint: %s"
,
error
->
message
);
return
;
}
/* Get the endpoint global id */
g_object_get
(
endpoint
,
"proxy-node"
,
&
proxy
,
NULL
);
global_id
=
wp_proxy_get_global_id
(
proxy
);
/* Register the endpoint and add it to the table */
wp_base_endpoint_register
(
endpoint
);
g_hash_table_insert
(
self
->
registered_endpoints
,
GUINT_TO_POINTER
(
global_id
),
g_object_ref
(
endpoint
));
/* Emit the endpoint-created signal */
g_signal_emit
(
self
,
signals
[
SIGNAL_ENDPOINT_CREATED
],
0
,
endpoint
);
}
static
GVariant
*
create_streams_variant
(
WpConfiguration
*
config
,
const
char
*
streams
)
{
g_autoptr
(
WpConfigParser
)
parser
=
NULL
;
const
struct
WpParserStreamsData
*
streams_data
=
NULL
;
g_autoptr
(
GVariantBuilder
)
ba
=
NULL
;
if
(
!
streams
||
!
config
)
return
NULL
;
/* Get the streams parser */
parser
=
wp_configuration_get_parser
(
config
,
WP_PARSER_STREAMS_EXTENSION
);
if
(
!
parser
)
return
NULL
;
/* Get the streams data */
streams_data
=
wp_config_parser_get_matched_data
(
parser
,
(
gpointer
)
streams
);
if
(
!
streams_data
||
streams_data
->
n_streams
<=
0
)
return
NULL
;
/* Build the variant array with the stream name and priority */
ba
=
g_variant_builder_new
(
G_VARIANT_TYPE
(
"a(su)"
));
g_variant_builder_init
(
ba
,
G_VARIANT_TYPE_ARRAY
);
for
(
guint
i
=
0
;
i
<
streams_data
->
n_streams
;
i
++
)
g_variant_builder_add
(
ba
,
"(su)"
,
streams_data
->
streams
[
i
].
name
,
streams_data
->
streams
[
i
].
priority
);
return
g_variant_new
(
"a(su)"
,
ba
);
}
static
void
on_node_added
(
WpObjectManager
*
om
,
WpProxy
*
proxy
,
gpointer
d
)
{
WpConfigEndpointContext
*
self
=
d
;
g_autoptr
(
WpCore
)
core
=
g_weak_ref_get
(
&
self
->
core
);
g_autoptr
(
WpConfiguration
)
config
=
wp_configuration_get_instance
(
core
);
WpProxyNode
*
proxy_node
=
WP_PROXY_NODE
(
proxy
);
g_autoptr
(
WpProperties
)
props
=
wp_proxy_node_get_properties
(
proxy_node
);
g_autoptr
(
WpConfigParser
)
parser
=
NULL
;
const
struct
WpParserEndpointData
*
endpoint_data
=
NULL
;
GVariantBuilder
b
;
g_autoptr
(
GVariant
)
endpoint_props
=
NULL
;
const
char
*
media_class
=
NULL
,
*
name
=
NULL
;
g_autoptr
(
GVariant
)
streams_variant
=
NULL
;
/* Get the linked and ep streams data */
parser
=
wp_configuration_get_parser
(
config
,
WP_PARSER_ENDPOINT_EXTENSION
);
endpoint_data
=
wp_config_parser_get_matched_data
(
parser
,
proxy_node
);
if
(
!
endpoint_data
)
return
;
/* Set the name if it is null */
name
=
endpoint_data
->
e
.
name
;
if
(
!
name
)
name
=
wp_properties_get
(
props
,
PW_KEY_NODE_NAME
);
/* Set the media class if it is null */
media_class
=
endpoint_data
->
e
.
media_class
;
if
(
!
media_class
)
media_class
=
wp_properties_get
(
props
,
PW_KEY_MEDIA_CLASS
);
/* Create the streams variant */
streams_variant
=
create_streams_variant
(
config
,
endpoint_data
->
e
.
streams
);
/* Set the properties */
g_variant_builder_init
(
&
b
,
G_VARIANT_TYPE_VARDICT
);
g_variant_builder_add
(
&
b
,
"{sv}"
,
"name"
,
g_variant_new_take_string
(
g_strdup_printf
(
"%s"
,
name
)));
g_variant_builder_add
(
&
b
,
"{sv}"
,
"media-class"
,
g_variant_new_string
(
media_class
));
g_variant_builder_add
(
&
b
,
"{sv}"
,
"direction"
,
g_variant_new_uint32
(
endpoint_data
->
e
.
direction
));
g_variant_builder_add
(
&
b
,
"{sv}"
,
"proxy-node"
,
g_variant_new_uint64
((
guint64
)
proxy
));
if
(
streams_variant
)
g_variant_builder_add
(
&
b
,
"{sv}"
,
"streams"
,
g_steal_pointer
(
&
streams_variant
));
endpoint_props
=
g_variant_builder_end
(
&
b
);
/* Create the endpoint async */
wp_factory_make
(
core
,
endpoint_data
->
e
.
type
,
WP_TYPE_BASE_ENDPOINT
,
endpoint_props
,
on_endpoint_created
,
self
);
}
static
void
on_node_removed
(
WpObjectManager
*
om
,
WpProxy
*
proxy
,
gpointer
d
)
{
WpConfigEndpointContext
*
self
=
d
;
WpBaseEndpoint
*
endpoint
=
NULL
;
guint32
id
=
wp_proxy_get_global_id
(
proxy
);
/* Get the endpoint */
endpoint
=
g_hash_table_lookup
(
self
->
registered_endpoints
,
GUINT_TO_POINTER
(
id
));
if
(
!
endpoint
)
return
;
/* Unregister the endpoint and remove it from the table */
wp_base_endpoint_unregister
(
endpoint
);
g_hash_table_remove
(
self
->
registered_endpoints
,
GUINT_TO_POINTER
(
id
));
}
static
void
wp_config_endpoint_context_constructed
(
GObject
*
object
)
{
WpConfigEndpointContext
*
self
=
WP_CONFIG_ENDPOINT_CONTEXT
(
object
);
g_autoptr
(
WpCore
)
core
=
g_weak_ref_get
(
&
self
->
core
);
g_return_if_fail
(
core
);
g_autoptr
(
WpConfiguration
)
config
=
wp_configuration_get_instance
(
core
);
g_return_if_fail
(
config
);
/* Add the endpoint and streams parsers */
wp_configuration_add_extension
(
config
,
WP_PARSER_ENDPOINT_EXTENSION
,
WP_TYPE_PARSER_ENDPOINT
);
wp_configuration_add_extension
(
config
,
WP_PARSER_STREAMS_EXTENSION
,
WP_TYPE_PARSER_STREAMS
);
/* Parse the files */
wp_configuration_reload
(
config
,
WP_PARSER_ENDPOINT_EXTENSION
);
wp_configuration_reload
(
config
,
WP_PARSER_STREAMS_EXTENSION
);
/* Install the object manager */
wp_core_install_object_manager
(
core
,
self
->
om
);
G_OBJECT_CLASS
(
wp_config_endpoint_context_parent_class
)
->
constructed
(
object
);
}
static
void
wp_config_endpoint_context_set_property
(
GObject
*
object
,
guint
property_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
WpConfigEndpointContext
*
self
=
WP_CONFIG_ENDPOINT_CONTEXT
(
object
);
switch
(
property_id
)
{
case
PROP_CORE
:
g_weak_ref_set
(
&
self
->
core
,
g_value_get_object
(
value
));
break
;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
object
,
property_id
,
pspec
);
break
;
}
}
static
void
wp_config_endpoint_context_get_property
(
GObject
*
object
,
guint
property_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
WpConfigEndpointContext
*
self
=
WP_CONFIG_ENDPOINT_CONTEXT
(
object
);
switch
(
property_id
)
{
case
PROP_CORE
:
g_value_take_object
(
value
,
g_weak_ref_get
(
&
self
->
core
));
break
;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
object
,
property_id
,
pspec
);
break
;
}
}
static
void
wp_config_endpoint_context_finalize
(
GObject
*
object
)
{
WpConfigEndpointContext
*
self
=
WP_CONFIG_ENDPOINT_CONTEXT
(
object
);
g_autoptr
(
WpCore
)
core
=
g_weak_ref_get
(
&
self
->
core
);
if
(
core
)
{
g_autoptr
(
WpConfiguration
)
config
=
wp_configuration_get_instance
(
core
);
wp_configuration_remove_extension
(
config
,
WP_PARSER_ENDPOINT_EXTENSION
);
wp_configuration_remove_extension
(
config
,
WP_PARSER_STREAMS_EXTENSION
);
}
g_weak_ref_clear
(
&
self
->
core
);
g_clear_object
(
&
self
->
om
);
g_clear_pointer
(
&
self
->
registered_endpoints
,
g_hash_table_unref
);
G_OBJECT_CLASS
(
wp_config_endpoint_context_parent_class
)
->
finalize
(
object
);
}
static
void
wp_config_endpoint_context_init
(
WpConfigEndpointContext
*
self
)
{
self
->
om
=
wp_object_manager_new
();
self
->
registered_endpoints
=
g_hash_table_new_full
(
g_direct_hash
,
g_direct_equal
,
NULL
,
(
GDestroyNotify
)
g_object_unref
);
/* Only handle augmented nodes with info set */
wp_object_manager_add_proxy_interest
(
self
->
om
,
PW_TYPE_INTERFACE_Node
,
NULL
,
WP_PROXY_FEATURE_INFO
);
/* Register the global added/removed callbacks */
g_signal_connect
(
self
->
om
,
"object-added"
,
(
GCallback
)
on_node_added
,
self
);
g_signal_connect
(
self
->
om
,
"object-removed"
,
(
GCallback
)
on_node_removed
,
self
);
}
static
void
wp_config_endpoint_context_class_init
(
WpConfigEndpointContextClass
*
klass
)
{
GObjectClass
*
object_class
=
(
GObjectClass
*
)
klass
;
object_class
->
constructed
=
wp_config_endpoint_context_constructed
;
object_class
->
finalize
=
wp_config_endpoint_context_finalize
;
object_class
->
set_property
=
wp_config_endpoint_context_set_property
;
object_class
->
get_property
=
wp_config_endpoint_context_get_property
;
/* Properties */
g_object_class_install_property
(
object_class
,
PROP_CORE
,
g_param_spec_object
(
"core"
,
"core"
,
"The wireplumber core"
,
WP_TYPE_CORE
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT_ONLY
|
G_PARAM_STATIC_STRINGS
));
/* Signals */
signals
[
SIGNAL_ENDPOINT_CREATED
]
=
g_signal_new
(
"endpoint-created"
,
G_TYPE_FROM_CLASS
(
klass
),
G_SIGNAL_RUN_LAST
,
0
,
NULL
,
NULL
,
NULL
,
G_TYPE_NONE
,
1
,
WP_TYPE_ENDPOINT
);
}
WpConfigEndpointContext
*
wp_config_endpoint_context_new
(
WpCore
*
core
)
{
return
g_object_new
(
wp_config_endpoint_context_get_type
(),
"core"
,
core
,
NULL
);
}
modules/module-config-endpoint/context.h
0 → 100644
View file @
acda80d7
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#ifndef __WIREPLUMBER_CONFIG_ENDPOINT_CONTEXT_H__
#define __WIREPLUMBER_CONFIG_ENDPOINT_CONTEXT_H__
#include
<wp/wp.h>
G_BEGIN_DECLS
#define WP_TYPE_CONFIG_ENDPOINT_CONTEXT (wp_config_endpoint_context_get_type ())
G_DECLARE_FINAL_TYPE
(
WpConfigEndpointContext
,
wp_config_endpoint_context
,
WP
,
CONFIG_ENDPOINT_CONTEXT
,
GObject
);
WpConfigEndpointContext
*
wp_config_endpoint_context_new
(
WpCore
*
core
);
G_END_DECLS
#endif
modules/module-config-endpoint/parser-endpoint.c
0 → 100644
View file @
acda80d7
/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include
<wptoml/wptoml.h>
#include
<pipewire/pipewire.h>
#include
"parser-endpoint.h"
struct
_WpParserEndpoint
{
GObject
parent
;
GPtrArray
*
datas
;
};
static
void
wp_parser_endpoint_config_parser_init
(
gpointer
iface
,
gpointer
iface_data
);
G_DEFINE_TYPE_WITH_CODE
(
WpParserEndpoint
,
wp_parser_endpoint
,
G_TYPE_OBJECT
,
G_IMPLEMENT_INTERFACE
(
WP_TYPE_CONFIG_PARSER
,
wp_parser_endpoint_config_parser_init
))
static
void
wp_parser_endpoint_data_destroy
(
gpointer
p
)
{
struct
WpParserEndpointData
*
data
=
p
;
/* Free the strings */
g_clear_pointer
(
&
data
->
mn
.
props
,
wp_properties_unref
);
g_clear_pointer
(
&
data
->
e
.
name
,
g_free
);
g_clear_pointer
(
&
data
->
e
.
media_class
,
g_free
);
g_clear_pointer
(
&
data
->
e
.
props
,
wp_properties_unref
);
g_clear_pointer
(
&
data
->
e
.
type
,
g_free
);
g_clear_pointer
(
&
data
->
e
.
streams
,
g_free
);
g_slice_free
(
struct
WpParserEndpointData
,
data
);
}
static
void
parse_properties_for_each
(
const
WpTomlTable
*
table
,
gpointer
user_data
)
{
WpProperties
*
props
=
user_data
;
g_return_if_fail
(
props
);
/* Skip unparsed tables */
if
(
!
table
)
return
;
/* Parse the name and value */
g_autofree
gchar
*
name
=
wp_toml_table_get_string
(
table
,
"name"
);
g_autofree
gchar
*
value
=
wp_toml_table_get_string
(
table
,
"value"
);
/* Set the property */
if
(
name
&&
value
)
wp_properties_set
(
props
,
name
,
value
);
}
static
WpProperties
*
parse_properties
(
WpTomlTable
*
table
,
const
char
*
name
)
{
WpProperties
*
props
=
wp_properties_new_empty
();
g_autoptr
(
WpTomlTableArray
)
properties
=
NULL
;
properties
=
wp_toml_table_get_array_table
(
table
,
name
);
if
(
properties
)
wp_toml_table_array_for_each
(
properties
,
parse_properties_for_each
,
props
);
return
props
;
}
static
guint
parse_endpoint_direction
(
const
char
*
direction
)
{
if
(
g_strcmp0
(
direction
,
"input"
)
==
0
)
return
PW_DIRECTION_INPUT
;
else
if
(
g_strcmp0
(
direction
,
"output"
)
==
0
)
return
PW_DIRECTION_OUTPUT
;
g_return_val_if_reached
(
PW_DIRECTION_INPUT
);
}
static
struct
WpParserEndpointData
*
wp_parser_endpoint_data_new
(
const
gchar
*
location
)
{
g_autoptr
(
WpTomlFile
)
file
=
NULL
;
g_autoptr
(
WpTomlTable
)
table
=
NULL
,
mn
=
NULL
,
e
=
NULL
;
g_autoptr
(
WpTomlArray
)
streams
=
NULL
;
struct
WpParserEndpointData
*
res
=
NULL
;
g_autofree
char
*
direction
=
NULL
;
/* File format:
* ------------
* [match-node]
* priority (uint32)
* properties (WpProperties)
*
* [endpoint]
* name (string)
* media_class (string)
* direction (string)
* priority (uint32)
* properties (WpProperties)
* type (string)
* streams (string)
*/
/* Get the TOML file */
file
=
wp_toml_file_new
(
location
);
if
(
!
file
)
return
NULL
;
/* Get the file table */
table
=
wp_toml_file_get_table
(
file
);
if
(
!
table
)
return
NULL
;
/* Create the endpoint data */
res
=
g_slice_new0
(
struct
WpParserEndpointData
);
/* Get the match-node table */
mn
=
wp_toml_table_get_table
(
table
,
"match-node"
);
if
(
!
mn
)
goto
error
;
/* Get the priority from the match-node table */
res
->
mn
.
priority
=
0
;
wp_toml_table_get_uint32
(
mn
,
"priority"
,
&
res
->
mn
.
priority
);
/* Get the match node properties */
res
->
mn
.
props
=
parse_properties
(
mn
,
"properties"
);
/* Get the endpoint table */
e
=
wp_toml_table_get_table
(
table
,
"endpoint"
);
if
(
!
e
)
goto
error
;
/* Get the name from the endpoint table */
res
->
e
.
name
=
wp_toml_table_get_string
(
e
,
"name"
);
/* Get the media class from the endpoint table */
res
->
e
.
media_class
=
wp_toml_table_get_string
(
e
,
"media_class"
);
/* Get the direction from the endpoint table */
direction
=
wp_toml_table_get_string
(
e
,
"direction"
);
if
(
!
direction
)
goto
error
;
res
->
e
.
direction
=
parse_endpoint_direction
(
direction
);
/* Get the priority from the endpoint table */
res
->
mn
.
priority
=
0
;
wp_toml_table_get_uint32
(
e
,
"priority"
,
&
res
->
e
.
priority
);
/* Get the endpoint properties */
res
->
e
.
props
=
parse_properties
(
e
,
"properties"
);
/* Get the endpoint type */
res
->
e
.
type
=
wp_toml_table_get_string
(
e
,
"type"
);
if
(
!
res
->
e
.
type
)
goto
error
;
/* Get the endpoint streams */
res
->
e
.
streams
=
wp_toml_table_get_string
(
e
,
"streams"
);
return
res
;
error:
g_clear_pointer
(
&
res
,
wp_parser_endpoint_data_destroy
);
return
NULL
;
}
static
gint
compare_datas_func
(
gconstpointer
a
,
gconstpointer
b
)
{
struct
WpParserEndpointData
*
da
=
*
(
struct
WpParserEndpointData
*
const
*
)
a
;
struct
WpParserEndpointData
*
db
=
*
(
struct
WpParserEndpointData
*
const
*
)
b
;
return
db
->
mn
.
priority
-
da
->
mn
.
priority
;
}
static
gboolean
wp_parser_endpoint_add_file
(
WpConfigParser
*
parser
,
const
gchar
*
name
)
{
WpParserEndpoint
*
self
=
WP_PARSER_ENDPOINT
(
parser
);
struct
WpParserEndpointData
*
data
;
/* Parse the file */
data
=
wp_parser_endpoint_data_new
(
name
);
if
(
!
data
)
{
g_warning
(
"Failed to parse configuration file '%s'"
,
name
);
return
FALSE
;
}
/* Add the data to the array */
g_ptr_array_add
(
self
->
datas
,
data
);
/* Sort the array by priority */
g_ptr_array_sort
(
self
->
datas
,
compare_datas_func
);
return
TRUE
;
}
static
gconstpointer
wp_parser_endpoint_get_matched_data
(
WpConfigParser