m-default-nodes{-api}: optimize for event-stack
- Sharpen the hooks, so that they are called only when needed. - Make settings live, apply them when they are changed. - Remove the state saver after events hook, call it directly. - Remove the settings bookkeeping as the gobject properties. - Remove the scheduling of default-nodes-changed signal via core.
This commit is contained in:

committed by
Julian Bouzas

parent
a5a6300662
commit
a060c6a130
@@ -326,8 +326,7 @@ wp_event_source_dispatch (GSource * s, GSourceFunc callback, gpointer user_data)
|
|||||||
g_steal_pointer (&lhook));
|
g_steal_pointer (&lhook));
|
||||||
|
|
||||||
event->hooks_chain = build_chain (name, priority, event->hooks_chain);
|
event->hooks_chain = build_chain (name, priority, event->hooks_chain);
|
||||||
wp_trace_object (d, "running hook <%p>(%s) and hooks chain (%s)", hook, name,
|
wp_trace_object (d, "running hook <%p>(%s)", hook, name);
|
||||||
event->hooks_chain);
|
|
||||||
|
|
||||||
if ((event == d->rescan_event) &&
|
if ((event == d->rescan_event) &&
|
||||||
(type == WP_EVENT_HOOK_EXEC_TYPE_AFTER_EVENTS_WITH_EVENT)) {
|
(type == WP_EVENT_HOOK_EXEC_TYPE_AFTER_EVENTS_WITH_EVENT)) {
|
||||||
|
@@ -36,8 +36,7 @@ typedef enum
|
|||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_FIND_DEFAULT_TARGET_SI = WP_EVENT_HOOK_DEFAULT_PRIORITY_FIND_BEST_TARGET_SI + PRIORITY_STEP,
|
WP_EVENT_HOOK_DEFAULT_PRIORITY_FIND_DEFAULT_TARGET_SI = WP_EVENT_HOOK_DEFAULT_PRIORITY_FIND_BEST_TARGET_SI + PRIORITY_STEP,
|
||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_FIND_DEFINED_TARGET_SI = WP_EVENT_HOOK_DEFAULT_PRIORITY_FIND_DEFAULT_TARGET_SI + PRIORITY_STEP,
|
WP_EVENT_HOOK_DEFAULT_PRIORITY_FIND_DEFINED_TARGET_SI = WP_EVENT_HOOK_DEFAULT_PRIORITY_FIND_DEFAULT_TARGET_SI + PRIORITY_STEP,
|
||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_RESCAN_POLICY = WP_EVENT_HOOK_DEFAULT_PRIORITY_FIND_DEFINED_TARGET_SI + PRIORITY_STEP,
|
WP_EVENT_HOOK_DEFAULT_PRIORITY_RESCAN_POLICY = WP_EVENT_HOOK_DEFAULT_PRIORITY_FIND_DEFINED_TARGET_SI + PRIORITY_STEP,
|
||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_AFTER_EVENTS_DEFAULT_NODES_STATE_SAVE = WP_EVENT_HOOK_DEFAULT_PRIORITY_RESCAN_POLICY + PRIORITY_STEP,
|
WP_EVENT_HOOK_DEFAULT_PRIORITY_RESCAN_DEFAULT_NODES = WP_EVENT_HOOK_DEFAULT_PRIORITY_RESCAN_POLICY + PRIORITY_STEP,
|
||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_RESCAN_DEFAULT_NODES = WP_EVENT_HOOK_DEFAULT_PRIORITY_AFTER_EVENTS_DEFAULT_NODES_STATE_SAVE + PRIORITY_STEP,
|
|
||||||
|
|
||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_NODE_ADDED = WP_EVENT_HOOK_DEFAULT_PRIORITY_RESCAN_POLICY + PRIORITY_JUMP,
|
WP_EVENT_HOOK_DEFAULT_PRIORITY_NODE_ADDED = WP_EVENT_HOOK_DEFAULT_PRIORITY_RESCAN_POLICY + PRIORITY_JUMP,
|
||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_NODE_ADDED_RESTORE_STREAM = WP_EVENT_HOOK_DEFAULT_PRIORITY_NODE_ADDED,
|
WP_EVENT_HOOK_DEFAULT_PRIORITY_NODE_ADDED_RESTORE_STREAM = WP_EVENT_HOOK_DEFAULT_PRIORITY_NODE_ADDED,
|
||||||
|
@@ -50,31 +50,6 @@ wp_default_nodes_api_init (WpDefaultNodesApi * self)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
sync_changed_notification (WpCore * core, GAsyncResult * res,
|
|
||||||
WpDefaultNodesApi * self)
|
|
||||||
{
|
|
||||||
g_autoptr (GError) error = NULL;
|
|
||||||
if (!wp_core_sync_finish (core, res, &error)) {
|
|
||||||
wp_warning_object (self, "core sync error: %s", error->message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_signal_emit (self, signals[SIGNAL_CHANGED], 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
schedule_changed_notification (WpDefaultNodesApi *self)
|
|
||||||
{
|
|
||||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
|
||||||
g_return_if_fail (core);
|
|
||||||
// Event-Stack TBD: do we need to retain this behavior? or push this as a
|
|
||||||
// event & hook pair on to event stack
|
|
||||||
wp_core_sync_closure (core, NULL, g_cclosure_new_object (
|
|
||||||
G_CALLBACK (sync_changed_notification), G_OBJECT (self)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_metadata_changed (WpMetadata *m, guint32 subject,
|
on_metadata_changed (WpMetadata *m, guint32 subject,
|
||||||
const gchar *key, const gchar *type, const gchar *value, gpointer d)
|
const gchar *key, const gchar *type, const gchar *value, gpointer d)
|
||||||
@@ -98,7 +73,7 @@ on_metadata_changed (WpMetadata *m, guint32 subject,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule_changed_notification (self);
|
g_signal_emit (self, signals [SIGNAL_CHANGED], 0);
|
||||||
break;
|
break;
|
||||||
} else if (!g_strcmp0 (key, DEFAULT_CONFIG_KEY[i])) {
|
} else if (!g_strcmp0 (key, DEFAULT_CONFIG_KEY[i])) {
|
||||||
|
|
||||||
@@ -149,8 +124,10 @@ on_metadata_added (WpEvent *event, gpointer d)
|
|||||||
for (; wp_iterator_next (it, &val); g_value_unset (&val)) {
|
for (; wp_iterator_next (it, &val); g_value_unset (&val)) {
|
||||||
guint32 subject_id;
|
guint32 subject_id;
|
||||||
const gchar *key, *type, *value;
|
const gchar *key, *type, *value;
|
||||||
wp_metadata_iterator_item_extract (&val, &subject_id, &key, &type, &value);
|
wp_metadata_iterator_item_extract (&val, &subject_id, &key, &type,
|
||||||
on_metadata_changed (WP_METADATA (obj), subject_id, key, type, value, self);
|
&value);
|
||||||
|
on_metadata_changed (WP_METADATA (obj), subject_id, key, type, value,
|
||||||
|
self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,11 +167,31 @@ wp_default_nodes_api_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_DEFAULT_METADATA_CHANGED_DEFAULT_NODES_API,
|
WP_EVENT_HOOK_DEFAULT_PRIORITY_DEFAULT_METADATA_CHANGED_DEFAULT_NODES_API,
|
||||||
WP_EVENT_HOOK_EXEC_TYPE_ON_EVENT,
|
WP_EVENT_HOOK_EXEC_TYPE_ON_EVENT,
|
||||||
g_cclosure_new ((GCallback) on_metadata_changed_hook, self, NULL));
|
g_cclosure_new ((GCallback) on_metadata_changed_hook, self, NULL));
|
||||||
|
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
||||||
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.key", "=s",
|
||||||
NULL);
|
"default.audio.sink",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.key", "=s",
|
||||||
|
"default.video.sink",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.key", "=s",
|
||||||
|
"default.audio.source",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
||||||
|
NULL);
|
||||||
|
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object(&hook);
|
g_clear_object(&hook);
|
||||||
|
|
||||||
|
@@ -21,22 +21,13 @@
|
|||||||
#define DEFAULT_ECHO_CANCEL_SOURCE_NAME "echo-cancel-source"
|
#define DEFAULT_ECHO_CANCEL_SOURCE_NAME "echo-cancel-source"
|
||||||
#define N_PREV_CONFIGS 16
|
#define N_PREV_CONFIGS 16
|
||||||
|
|
||||||
enum {
|
|
||||||
PROP_0,
|
|
||||||
PROP_SAVE_INTERVAL_MS,
|
|
||||||
PROP_USE_PERSISTENT_STORAGE,
|
|
||||||
PROP_AUTO_ECHO_CANCEL,
|
|
||||||
PROP_ECHO_CANCEL_SINK_NAME,
|
|
||||||
PROP_ECHO_CANCEL_SOURCE_NAME,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Module maintains the default devices to be used for a given media class. The
|
* Module comes up with the default audio and video devices. It looks for
|
||||||
* module looks for changes in user preference and the changes in devices(when
|
* changes in user preference and the changes in devices(when new devices like
|
||||||
* new devices like headsets, BT devices, HDMI etc are plugged in). User
|
* headsets, BT devices, HDMI etc are plugged in or removed). User preference
|
||||||
* preference can be expressed via pavuctrl, gnome settings or metadata etc.
|
* can be expressed via pavuctrl, gnome settings or metadata etc. These apps
|
||||||
* These apps typically update the
|
* typically update the default.configured.*(default-configured-nodes) keys.
|
||||||
* default.configured.*(default-configured-nodes) keys.
|
* Additionally the user preferences are remembered across reboots.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -62,19 +53,34 @@ struct _WpDefaultNodes
|
|||||||
GSource *timeout_source;
|
GSource *timeout_source;
|
||||||
|
|
||||||
/* properties */
|
/* properties */
|
||||||
guint save_interval_ms;
|
gint save_interval_ms;
|
||||||
gboolean use_persistent_storage;
|
gboolean use_persistent_storage;
|
||||||
gboolean auto_echo_cancel;
|
gboolean auto_echo_cancel;
|
||||||
gchar *echo_cancel_names[2];
|
gchar *echo_cancel_names [2];
|
||||||
|
WpSettings *settings;
|
||||||
|
guintptr settings_sub_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DECLARE_FINAL_TYPE (WpDefaultNodes, wp_default_nodes,
|
G_DECLARE_FINAL_TYPE (WpDefaultNodes, wp_default_nodes,
|
||||||
WP, DEFAULT_NODES, WpPlugin)
|
WP, DEFAULT_NODES, WpPlugin)
|
||||||
G_DEFINE_TYPE (WpDefaultNodes, wp_default_nodes, WP_TYPE_PLUGIN)
|
G_DEFINE_TYPE (WpDefaultNodes, wp_default_nodes, WP_TYPE_PLUGIN)
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_settings (WpDefaultNodes *self)
|
||||||
|
{
|
||||||
|
self->save_interval_ms = DEFAULT_SAVE_INTERVAL_MS;
|
||||||
|
self->use_persistent_storage = DEFAULT_USE_PERSISTENT_STORAGE;
|
||||||
|
self->auto_echo_cancel = DEFAULT_AUTO_ECHO_CANCEL;
|
||||||
|
self->echo_cancel_names [WP_DIRECTION_INPUT] =
|
||||||
|
g_strdup (DEFAULT_ECHO_CANCEL_SOURCE_NAME);
|
||||||
|
self->echo_cancel_names [WP_DIRECTION_OUTPUT] =
|
||||||
|
g_strdup (DEFAULT_ECHO_CANCEL_SINK_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wp_default_nodes_init (WpDefaultNodes * self)
|
wp_default_nodes_init (WpDefaultNodes * self)
|
||||||
{
|
{
|
||||||
|
init_settings (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -149,9 +155,8 @@ timeout_save_state_callback (WpDefaultNodes *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
timer_start (WpEvent *event, gpointer d)
|
timer_start (WpDefaultNodes *self)
|
||||||
{
|
{
|
||||||
WpDefaultNodes * self = WP_DEFAULT_NODES (d);
|
|
||||||
if (!self->timeout_source && self->use_persistent_storage) {
|
if (!self->timeout_source && self->use_persistent_storage) {
|
||||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
||||||
g_return_if_fail (core);
|
g_return_if_fail (core);
|
||||||
@@ -419,15 +424,10 @@ reevaluate_default_node (WpDefaultNodes * self, WpMetadata *m, gint node_t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sync_rescan (WpCore * core, GAsyncResult * res, WpDefaultNodes * self)
|
rescan (WpEvent *event, gpointer d)
|
||||||
{
|
{
|
||||||
|
WpDefaultNodes *self = WP_DEFAULT_NODES (d);
|
||||||
g_autoptr (WpMetadata) metadata = NULL;
|
g_autoptr (WpMetadata) metadata = NULL;
|
||||||
g_autoptr (GError) error = NULL;
|
|
||||||
|
|
||||||
if (!wp_core_sync_finish (core, res, &error)) {
|
|
||||||
wp_warning_object (self, "core sync error: %s", error->message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the metadata */
|
/* Get the metadata */
|
||||||
metadata = wp_object_manager_lookup (self->metadata_om, WP_TYPE_METADATA,
|
metadata = wp_object_manager_lookup (self->metadata_om, WP_TYPE_METADATA,
|
||||||
@@ -441,20 +441,6 @@ sync_rescan (WpCore * core, GAsyncResult * res, WpDefaultNodes * self)
|
|||||||
reevaluate_default_node (self, metadata, VIDEO_SOURCE);
|
reevaluate_default_node (self, metadata, VIDEO_SOURCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
schedule_rescan (WpEvent *event, gpointer d)
|
|
||||||
{
|
|
||||||
WpDefaultNodes * self = WP_DEFAULT_NODES (d);
|
|
||||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
|
||||||
g_return_if_fail (core);
|
|
||||||
|
|
||||||
wp_debug_object (self, "scheduling default nodes rescan");
|
|
||||||
// Event-Stack TBD: do we need to retain this behavior? or push this as a
|
|
||||||
// event & hook pair on to event stack
|
|
||||||
wp_core_sync_closure (core, NULL, g_cclosure_new_object (
|
|
||||||
G_CALLBACK (sync_rescan), G_OBJECT (self)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_metadata_changed (WpEvent *event, gpointer d)
|
on_metadata_changed (WpEvent *event, gpointer d)
|
||||||
{
|
{
|
||||||
@@ -493,9 +479,6 @@ on_metadata_changed (WpEvent *event, gpointer d)
|
|||||||
wp_debug_object (m, "changed '%s' -> '%s'", key,
|
wp_debug_object (m, "changed '%s' -> '%s'", key,
|
||||||
self->defaults[node_t].config_value);
|
self->defaults[node_t].config_value);
|
||||||
|
|
||||||
/* schedule rescan */
|
|
||||||
schedule_rescan (self);
|
|
||||||
|
|
||||||
/* Save state after specific interval */
|
/* Save state after specific interval */
|
||||||
timer_start (self);
|
timer_start (self);
|
||||||
}
|
}
|
||||||
@@ -520,6 +503,75 @@ on_metadata_added (WpEvent *event, gpointer d)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_settings (WpDefaultNodes *self, const gchar *setting)
|
||||||
|
{
|
||||||
|
if (!setting || (g_str_equal ("device.save-interval-ms", setting)))
|
||||||
|
{
|
||||||
|
g_autoptr (WpSpaJson) j = wp_settings_get (self->settings,
|
||||||
|
"device.save-interval-ms");
|
||||||
|
if (j && !wp_spa_json_parse_int (j, &self->save_interval_ms))
|
||||||
|
wp_warning ("Failed to parse integer in device.save-interval-ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setting || (g_str_equal ("device.use-persistent-storage", setting)))
|
||||||
|
{
|
||||||
|
g_autoptr (WpSpaJson) j = wp_settings_get (self->settings,
|
||||||
|
"device.use-persistent-storage");
|
||||||
|
if (j && !wp_spa_json_parse_boolean (j, &self->use_persistent_storage))
|
||||||
|
wp_warning ("Failed to parse boolean in device.use-persistent-storage");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setting || (g_str_equal ("device.auto-echo-cancel", setting)))
|
||||||
|
{
|
||||||
|
g_autoptr (WpSpaJson) j = wp_settings_get (self->settings,
|
||||||
|
"device.auto-echo-cancel");
|
||||||
|
if (j && !wp_spa_json_parse_boolean (j, &self->auto_echo_cancel))
|
||||||
|
wp_warning ("Failed to parse boolean in device.auto-echo-cancel");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setting || (g_str_equal ("device.echo-cancel-sink-name", setting)))
|
||||||
|
{
|
||||||
|
g_autoptr (WpSpaJson) j = wp_settings_get (self->settings,
|
||||||
|
"device.echo-cancel-sink-name");
|
||||||
|
if (j) {
|
||||||
|
gchar *value = wp_spa_json_parse_string (j);
|
||||||
|
if (!value)
|
||||||
|
wp_warning ("Failed to parse string in device.echo-cancel-sink-name");
|
||||||
|
else {
|
||||||
|
g_free (self->echo_cancel_names [WP_DIRECTION_OUTPUT]);
|
||||||
|
self->echo_cancel_names [WP_DIRECTION_OUTPUT] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setting || (g_str_equal ("device.echo-cancel-source-name", setting)))
|
||||||
|
{
|
||||||
|
g_autoptr (WpSpaJson) j = wp_settings_get (self->settings,
|
||||||
|
"device.echo-cancel-source-name");
|
||||||
|
if (j) {
|
||||||
|
gchar *value = wp_spa_json_parse_string (j);
|
||||||
|
if (!value)
|
||||||
|
wp_warning ("Failed to parse string in device.echo-cancel-source-name");
|
||||||
|
else {
|
||||||
|
g_free (self->echo_cancel_names [WP_DIRECTION_INPUT]);
|
||||||
|
self->echo_cancel_names [WP_DIRECTION_INPUT] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wp_settings_changed_callback (WpSettings *obj, const gchar *setting,
|
||||||
|
const gchar *raw_value, gpointer user_data)
|
||||||
|
{
|
||||||
|
WpDefaultNodes *self = WP_DEFAULT_NODES (user_data);
|
||||||
|
g_return_if_fail (self);
|
||||||
|
g_return_if_fail (setting);
|
||||||
|
|
||||||
|
get_settings (self, setting);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wp_default_nodes_enable (WpPlugin * plugin, WpTransition * transition)
|
wp_default_nodes_enable (WpPlugin * plugin, WpTransition * transition)
|
||||||
{
|
{
|
||||||
@@ -531,6 +583,13 @@ wp_default_nodes_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
g_autoptr (WpEventHook) hook = NULL;
|
g_autoptr (WpEventHook) hook = NULL;
|
||||||
g_return_if_fail (dispatcher);
|
g_return_if_fail (dispatcher);
|
||||||
|
|
||||||
|
self->settings = wp_settings_get_instance (core, NULL);
|
||||||
|
g_return_if_fail (self->settings);
|
||||||
|
self->settings_sub_id = wp_settings_subscribe (self->settings, "device*",
|
||||||
|
wp_settings_changed_callback, (gpointer) self);
|
||||||
|
|
||||||
|
get_settings (self, NULL);
|
||||||
|
|
||||||
/* default metadata added */
|
/* default metadata added */
|
||||||
hook = wp_simple_event_hook_new ("metadata-added@default-nodes",
|
hook = wp_simple_event_hook_new ("metadata-added@default-nodes",
|
||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_DEFAULT_METADATA_ADDED_DEFAULT_NODES,
|
WP_EVENT_HOOK_DEFAULT_PRIORITY_DEFAULT_METADATA_ADDED_DEFAULT_NODES,
|
||||||
@@ -549,58 +608,92 @@ wp_default_nodes_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_DEFAULT_METADATA_CHANGED_DEFAULT_NODES,
|
WP_EVENT_HOOK_DEFAULT_PRIORITY_DEFAULT_METADATA_CHANGED_DEFAULT_NODES,
|
||||||
WP_EVENT_HOOK_EXEC_TYPE_ON_EVENT,
|
WP_EVENT_HOOK_EXEC_TYPE_ON_EVENT,
|
||||||
g_cclosure_new ((GCallback) on_metadata_changed, self, NULL));
|
g_cclosure_new ((GCallback) on_metadata_changed, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
|
||||||
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
|
||||||
NULL);
|
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
|
||||||
g_clear_object(&hook);
|
|
||||||
|
|
||||||
/* register rescan hook as an after event */
|
|
||||||
hook = wp_simple_event_hook_new("rescan-default-nodes",
|
|
||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_RESCAN_DEFAULT_NODES,
|
|
||||||
WP_EVENT_HOOK_EXEC_TYPE_AFTER_EVENTS,
|
|
||||||
g_cclosure_new ((GCallback) schedule_rescan, self, NULL));
|
|
||||||
/* default metadata changed */
|
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
||||||
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.key", "=s",
|
||||||
NULL);
|
"default.configured.audio.sink",
|
||||||
/* device changed */
|
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
||||||
|
NULL);
|
||||||
|
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "device",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
||||||
NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.key", "=s",
|
||||||
/* node changed */
|
"default.configured.video.sink",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
||||||
|
NULL);
|
||||||
|
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "node",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
||||||
NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.key", "=s",
|
||||||
/* device parms changed */
|
"default.configured.audio.source",
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "params-changed",
|
NULL);
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "device",
|
|
||||||
NULL);
|
|
||||||
/* node parms changed */
|
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "params-changed",
|
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "node",
|
|
||||||
NULL);
|
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object (&hook);
|
g_clear_object (&hook);
|
||||||
|
|
||||||
/* register state save hook as an after event */
|
/* register rescan hook as an after event */
|
||||||
hook = wp_simple_event_hook_new ("default-nodes-state-saver",
|
hook = wp_simple_event_hook_new("rescan@default-nodes",
|
||||||
WP_EVENT_HOOK_DEFAULT_PRIORITY_AFTER_EVENTS_DEFAULT_NODES_STATE_SAVE,
|
WP_EVENT_HOOK_DEFAULT_PRIORITY_RESCAN_DEFAULT_NODES,
|
||||||
WP_EVENT_HOOK_EXEC_TYPE_AFTER_EVENTS,
|
WP_EVENT_HOOK_EXEC_TYPE_AFTER_EVENTS,
|
||||||
g_cclosure_new ((GCallback) timer_start, self, NULL));
|
g_cclosure_new ((GCallback) rescan, self, NULL));
|
||||||
|
|
||||||
|
/* default.configured.audio.sink changed */
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
||||||
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.key", "=s", "default.configured.audio.sink",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* default.configured.video.sink changed */
|
||||||
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.key", "=s", "default.configured.video.sink",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* default.configured.audio.source changed */
|
||||||
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-changed",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "metadata",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.key", "=s", "default.configured.audio.source",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "metadata.name", "=s", "default",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* new video device node added */
|
||||||
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-added",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "node",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", "#s", "Video/*",
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
/* new audio device node added */
|
||||||
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-added",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "node",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "media.class", "#s", "Audio/*",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* video device node removed */
|
||||||
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-removed",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "node",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", "#s", "Video/*",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* audio device node removed */
|
||||||
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "object-removed",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.subject.type", "=s", "node",
|
||||||
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", "#s", "Audio/*",
|
||||||
|
NULL);
|
||||||
|
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object (&hook);
|
g_clear_object (&hook);
|
||||||
|
|
||||||
@@ -657,35 +750,6 @@ wp_default_nodes_disable (WpPlugin * plugin)
|
|||||||
g_clear_object (&self->state);
|
g_clear_object (&self->state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
wp_default_nodes_set_property (GObject * object, guint property_id,
|
|
||||||
const GValue * value, GParamSpec * pspec)
|
|
||||||
{
|
|
||||||
WpDefaultNodes * self = WP_DEFAULT_NODES (object);
|
|
||||||
|
|
||||||
switch (property_id) {
|
|
||||||
case PROP_SAVE_INTERVAL_MS:
|
|
||||||
self->save_interval_ms = g_value_get_uint (value);
|
|
||||||
break;
|
|
||||||
case PROP_USE_PERSISTENT_STORAGE:
|
|
||||||
self->use_persistent_storage = g_value_get_boolean (value);
|
|
||||||
break;
|
|
||||||
case PROP_AUTO_ECHO_CANCEL:
|
|
||||||
self->auto_echo_cancel = g_value_get_boolean (value);
|
|
||||||
break;
|
|
||||||
case PROP_ECHO_CANCEL_SINK_NAME:
|
|
||||||
g_clear_pointer (&self->echo_cancel_names[WP_DIRECTION_INPUT], g_free);
|
|
||||||
self->echo_cancel_names[WP_DIRECTION_INPUT] = g_value_dup_string (value);
|
|
||||||
break;
|
|
||||||
case PROP_ECHO_CANCEL_SOURCE_NAME:
|
|
||||||
g_clear_pointer (&self->echo_cancel_names[WP_DIRECTION_OUTPUT], g_free);
|
|
||||||
self->echo_cancel_names[WP_DIRECTION_OUTPUT] = g_value_dup_string (value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wp_default_nodes_finalize (GObject * object)
|
wp_default_nodes_finalize (GObject * object)
|
||||||
@@ -695,6 +759,9 @@ wp_default_nodes_finalize (GObject * object)
|
|||||||
g_clear_pointer (&self->echo_cancel_names[WP_DIRECTION_INPUT], g_free);
|
g_clear_pointer (&self->echo_cancel_names[WP_DIRECTION_INPUT], g_free);
|
||||||
g_clear_pointer (&self->echo_cancel_names[WP_DIRECTION_OUTPUT], g_free);
|
g_clear_pointer (&self->echo_cancel_names[WP_DIRECTION_OUTPUT], g_free);
|
||||||
|
|
||||||
|
if (self->settings_sub_id)
|
||||||
|
wp_settings_unsubscribe (self->settings, self->settings_sub_id);
|
||||||
|
|
||||||
G_OBJECT_CLASS (wp_default_nodes_parent_class)->finalize (object);
|
G_OBJECT_CLASS (wp_default_nodes_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,101 +772,20 @@ wp_default_nodes_class_init (WpDefaultNodesClass * klass)
|
|||||||
WpPluginClass *plugin_class = (WpPluginClass *) klass;
|
WpPluginClass *plugin_class = (WpPluginClass *) klass;
|
||||||
|
|
||||||
object_class->finalize = wp_default_nodes_finalize;
|
object_class->finalize = wp_default_nodes_finalize;
|
||||||
object_class->set_property = wp_default_nodes_set_property;
|
|
||||||
|
|
||||||
plugin_class->enable = wp_default_nodes_enable;
|
plugin_class->enable = wp_default_nodes_enable;
|
||||||
plugin_class->disable = wp_default_nodes_disable;
|
plugin_class->disable = wp_default_nodes_disable;
|
||||||
|
|
||||||
g_object_class_install_property (object_class, PROP_SAVE_INTERVAL_MS,
|
|
||||||
g_param_spec_uint ("save-interval-ms", "save-interval-ms",
|
|
||||||
"save-interval-ms", 1, G_MAXUINT32, DEFAULT_SAVE_INTERVAL_MS,
|
|
||||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
g_object_class_install_property (object_class, PROP_USE_PERSISTENT_STORAGE,
|
|
||||||
g_param_spec_boolean ("use-persistent-storage", "use-persistent-storage",
|
|
||||||
"use-persistent-storage", DEFAULT_USE_PERSISTENT_STORAGE,
|
|
||||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
g_object_class_install_property (object_class, PROP_AUTO_ECHO_CANCEL,
|
|
||||||
g_param_spec_boolean ("auto-echo-cancel", "auto-echo-cancel",
|
|
||||||
"auto-echo-cancel", DEFAULT_AUTO_ECHO_CANCEL,
|
|
||||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
g_object_class_install_property (object_class, PROP_ECHO_CANCEL_SINK_NAME,
|
|
||||||
g_param_spec_string ("echo-cancel-sink-name", "echo-cancel-sink-name",
|
|
||||||
"echo-cancel-sink-name", DEFAULT_ECHO_CANCEL_SINK_NAME,
|
|
||||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
g_object_class_install_property (object_class, PROP_ECHO_CANCEL_SOURCE_NAME,
|
|
||||||
g_param_spec_string ("echo-cancel-source-name", "echo-cancel-source-name",
|
|
||||||
"echo-cancel-source-name", DEFAULT_ECHO_CANCEL_SOURCE_NAME,
|
|
||||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
gint save_interval_ms = DEFAULT_SAVE_INTERVAL_MS;
|
|
||||||
gboolean use_persistent_storage = DEFAULT_USE_PERSISTENT_STORAGE;
|
|
||||||
gboolean auto_echo_cancel = DEFAULT_AUTO_ECHO_CANCEL;
|
|
||||||
g_autofree gchar *echo_cancel_sink_name = NULL;
|
|
||||||
g_autofree gchar *echo_cancel_source_name = NULL;
|
|
||||||
|
|
||||||
g_autoptr (WpSettings) settings = wp_settings_get_instance(core, NULL);
|
|
||||||
|
|
||||||
{
|
|
||||||
g_autoptr (WpSpaJson) j = wp_settings_get (settings,
|
|
||||||
"device.save-interval-ms");
|
|
||||||
if (j && !wp_spa_json_parse_int (j, &save_interval_ms))
|
|
||||||
wp_warning ("Failed to parse integer in device.save-interval-ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
g_autoptr (WpSpaJson) j = wp_settings_get (settings,
|
|
||||||
"device.use-persistent-storage");
|
|
||||||
if (j && !wp_spa_json_parse_boolean (j, &save_interval_ms))
|
|
||||||
wp_warning ("Failed to parse boolean in device.use-persistent-storage");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
g_autoptr (WpSpaJson) j = wp_settings_get (settings,
|
|
||||||
"device.auto-echo-cancel");
|
|
||||||
if (j && !wp_spa_json_parse_boolean (j, &auto_echo_cancel))
|
|
||||||
wp_warning ("Failed to parse boolean in device.auto-echo-cancel");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
g_autoptr (WpSpaJson) j = wp_settings_get (settings,
|
|
||||||
"device.echo-cancel-sink-name");
|
|
||||||
if (j) {
|
|
||||||
echo_cancel_sink_name = wp_spa_json_parse_string (j);
|
|
||||||
if (!echo_cancel_sink_name)
|
|
||||||
wp_warning ("Failed to parse string in device.echo-cancel-sink-name");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
g_autoptr (WpSpaJson) j = wp_settings_get (settings,
|
|
||||||
"device.echo-cancel-source-name");
|
|
||||||
if (j) {
|
|
||||||
echo_cancel_sink_name = wp_spa_json_parse_string (j);
|
|
||||||
if (!echo_cancel_sink_name)
|
|
||||||
wp_warning ("Failed to parse string in device.echo-cancel-source-name");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wp_plugin_register (g_object_new (wp_default_nodes_get_type (),
|
wp_plugin_register (g_object_new (wp_default_nodes_get_type (),
|
||||||
"name", NAME,
|
"name", NAME,
|
||||||
"core", core,
|
"core", core,
|
||||||
"save-interval-ms", save_interval_ms > 0 ?
|
|
||||||
(guint)save_interval_ms : DEFAULT_SAVE_INTERVAL_MS,
|
|
||||||
"use-persistent-storage", use_persistent_storage,
|
|
||||||
"auto-echo-cancel", auto_echo_cancel,
|
|
||||||
"echo-cancel-sink-name", echo_cancel_sink_name ?
|
|
||||||
echo_cancel_sink_name : DEFAULT_ECHO_CANCEL_SINK_NAME,
|
|
||||||
"echo-cancel-source-name", echo_cancel_source_name ?
|
|
||||||
echo_cancel_source_name : DEFAULT_ECHO_CANCEL_SOURCE_NAME,
|
|
||||||
NULL));
|
NULL));
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,17 +24,14 @@ wireplumber.settings = {
|
|||||||
# their priorities and any runtime changes do not persist after restart
|
# their priorities and any runtime changes do not persist after restart
|
||||||
device.use-persistent-storage = true
|
device.use-persistent-storage = true
|
||||||
|
|
||||||
# the default volume to apply to ACP device nodes, in the linear scale
|
|
||||||
# default-volume = 0.4
|
|
||||||
|
|
||||||
# Whether to auto-switch to echo cancel sink and source nodes or not
|
# Whether to auto-switch to echo cancel sink and source nodes or not
|
||||||
device.auto-echo-cancel = true
|
device.auto-echo-cancel = true
|
||||||
|
|
||||||
# Sets the default echo-cancel-sink node name to automatically switch to
|
# Sets the default echo-cancel-sink node name to automatically switch to
|
||||||
device.echo-cancel-sink-name = echo-cancel-sink
|
# device.echo-cancel-sink-name = "echo-cancel-sink"
|
||||||
|
|
||||||
# Sets the default echo-cancel-source node name to automatically switch to
|
# Sets the default echo-cancel-source node name to automatically switch to
|
||||||
device.echo-cancel-source-name = echo-cancel-source
|
# device.echo-cancel-source-name = "echo-cancel-source"
|
||||||
|
|
||||||
# device default volume level
|
# device default volume level
|
||||||
# device.default-volume = 0.5
|
# device.default-volume = 0.5
|
||||||
|
Reference in New Issue
Block a user