summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--binding/audiomixer.c163
1 files changed, 114 insertions, 49 deletions
diff --git a/binding/audiomixer.c b/binding/audiomixer.c
index 6ef3210..aceaefe 100644
--- a/binding/audiomixer.c
+++ b/binding/audiomixer.c
@@ -19,11 +19,12 @@ struct audiomixer
GCond cond;
WpRemoteState state;
- WpObjectManager *endpoints_om;
GPtrArray *mixer_controls;
+ GWeakRef session;
- /* just pointer, no ref */
- WpSession *session;
+ /* ref held in the thread function */
+ WpObjectManager *sessions_om;
+ WpObjectManager *endpoints_om;
const struct audiomixer_events *events;
void *events_data;
@@ -91,41 +92,95 @@ control_changed (WpEndpoint * ep, guint32 control_id, struct audiomixer * self)
/* called with self->lock locked */
static void
-populate_controls (struct audiomixer * self, WpSession * session)
+check_and_populate_controls (struct audiomixer * self)
{
- guint32 def_id = wp_session_get_default_endpoint (session,
- WP_DEFAULT_ENDPOINT_TYPE_AUDIO_SINK);
- g_autoptr (GPtrArray) arr = wp_object_manager_get_objects (
- self->endpoints_om, 0);
-
- for (guint i = 0; i < arr->len; i++) {
- WpEndpoint *ep = g_ptr_array_index (arr, i);
- guint32 id = wp_proxy_get_global_id (WP_PROXY (ep));
+ g_autoptr (WpSession) session = NULL;
+ g_autoptr (WpEndpoint) def_ep = NULL;
+ guint32 def_id = 0;
+ struct mixer_control_impl *master_ctl = NULL;
+
+ /* find the session */
+ session = g_weak_ref_get (&self->session);
+
+ /* find the default audio sink endpoint */
+ if (session) {
+ g_autoptr (GPtrArray) arr =
+ wp_object_manager_get_objects (self->endpoints_om, 0);
+ def_id = wp_session_get_default_endpoint (session,
+ WP_DEFAULT_ENDPOINT_TYPE_AUDIO_SINK);
+
+ for (guint i = 0; i < arr->len; i++) {
+ WpEndpoint *ep = g_ptr_array_index (arr, i);
+ guint32 id = wp_proxy_get_global_id (WP_PROXY (ep));
+ if (id != def_id)
+ continue;
+ def_ep = g_object_ref (ep);
+ }
+ }
+
+ /* find the audio sink endpoint that was the default before */
+ for (guint i = 0; i < self->mixer_controls->len; i++) {
+ struct mixer_control_impl *ctl = (struct mixer_control_impl *)
+ g_ptr_array_index (self->mixer_controls, i);
+ if (ctl->is_master) {
+ master_ctl = ctl;
+ break;
+ }
+ }
+
+ g_debug ("check_and_populate: session:%p, def_ep:%p, def_id:%u, "
+ "master_ctl:%p, master_id:%u", session, def_ep, def_id,
+ master_ctl, master_ctl ? master_ctl->id : 0);
+
+ /* case 1: there is a default endpoint but it doesn't match
+ what we currently expose -> clear and expose the new one */
+ if (def_ep && (!master_ctl || master_ctl->id != def_id)) {
struct mixer_control_impl *mixctl = NULL;
gfloat volume;
gboolean mute;
- if (id != def_id)
- continue;
+ /* clear previous */
+ if (master_ctl)
+ g_signal_handlers_disconnect_by_data (master_ctl->endpoint, self);
+ g_ptr_array_set_size (self->mixer_controls, 0);
- wp_endpoint_get_control_float (ep, WP_ENDPOINT_CONTROL_VOLUME, &volume);
- wp_endpoint_get_control_boolean (ep, WP_ENDPOINT_CONTROL_MUTE, &mute);
+ /* get current master values */
+ wp_endpoint_get_control_float (def_ep, WP_ENDPOINT_CONTROL_VOLUME, &volume);
+ wp_endpoint_get_control_boolean (def_ep, WP_ENDPOINT_CONTROL_MUTE, &mute);
+ /* create the Master control */
mixctl = g_new0 (struct mixer_control_impl, 1);
strncpy (mixctl->pub.name, "Master", sizeof (mixctl->pub.name));
mixctl->pub.volume = volume;
mixctl->pub.mute = mute;
mixctl->is_master = TRUE;
- mixctl->id = id;
- mixctl->endpoint = g_object_ref (ep);
+ mixctl->id = def_id;
+ mixctl->endpoint = g_object_ref (def_ep);
g_ptr_array_add (self->mixer_controls, mixctl);
- g_signal_connect (ep, "control-changed", (GCallback) control_changed,
+ /* track changes */
+ g_signal_connect (def_ep, "control-changed", (GCallback) control_changed,
self);
/* wake up audiomixer_ensure_controls() */
g_cond_broadcast (&self->cond);
- break;
+
+ g_debug ("controls changed");
+
+ /* notify subscribers */
+ if (self->events && self->events->controls_changed)
+ self->events->controls_changed (self->events_data);
+ }
+ /* case 2: there is no default endpoint but something is exposed -> clear */
+ else if (!def_ep && master_ctl) {
+ g_signal_handlers_disconnect_by_data (master_ctl->endpoint, self);
+ g_ptr_array_set_size (self->mixer_controls, 0);
+
+ g_debug ("controls cleared");
+
+ /* notify subscribers */
+ if (self->events && self->events->controls_changed)
+ self->events->controls_changed (self->events_data);
}
}
@@ -133,44 +188,49 @@ static void
default_endpoint_changed (WpSession * session, WpDefaultEndpointType type,
guint32 new_id, struct audiomixer * self)
{
- if (session != self->session || type != WP_DEFAULT_ENDPOINT_TYPE_AUDIO_SINK)
+ if (type != WP_DEFAULT_ENDPOINT_TYPE_AUDIO_SINK)
return;
g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock);
-
- g_ptr_array_set_size (self->mixer_controls, 0);
- populate_controls (self, session);
-
- if (self->events && self->events->controls_changed)
- self->events->controls_changed (self->events_data);
+ check_and_populate_controls (self);
}
static void
-sessions_changed (WpObjectManager * om, struct audiomixer * self)
+sessions_changed (WpObjectManager * sessions_om, struct audiomixer * self)
{
g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock);
+ g_autoptr (WpSession) old_session = NULL;
g_autoptr (WpSession) session = NULL;
g_autoptr (GPtrArray) arr = NULL;
- g_ptr_array_set_size (self->mixer_controls, 0);
+ old_session = g_weak_ref_get (&self->session);
/* normally there is only 1 session */
- arr = wp_object_manager_get_objects (om, 0);
+ arr = wp_object_manager_get_objects (sessions_om, 0);
if (arr->len > 0)
session = WP_SESSION (g_object_ref (g_ptr_array_index (arr, 0)));
- if (session)
- populate_controls (self, session);
+ g_debug ("sessions changed, count:%d, session:%p, old_session:%p",
+ arr->len, session, old_session);
- /* just to be safe in case there are more sessions around */
- if (session != self->session) {
- g_signal_connect (session, "default-endpoint-changed",
- (GCallback) default_endpoint_changed, self);
- self->session = session;
+ if (session != old_session) {
+ if (old_session)
+ g_signal_handlers_disconnect_by_data (old_session, self);
+ if (session)
+ g_signal_connect (session, "default-endpoint-changed",
+ (GCallback) default_endpoint_changed, self);
+
+ g_weak_ref_set (&self->session, session);
}
- if (self->events && self->events->controls_changed)
- self->events->controls_changed (self->events_data);
+ check_and_populate_controls (self);
+}
+
+static void
+endpoints_changed (WpObjectManager * endpoints_om, struct audiomixer * self)
+{
+ g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock);
+ check_and_populate_controls (self);
}
static void
@@ -182,10 +242,18 @@ remote_state_changed (WpCore *core, WpRemoteState state,
g_cond_broadcast (&self->cond);
}
+static gboolean
+connect_in_idle (struct audiomixer * self)
+{
+ wp_core_connect (self->core);
+ return G_SOURCE_REMOVE;
+}
+
static void *
audiomixer_thread (struct audiomixer * self)
{
- g_autoptr (WpObjectManager) sessions_om = wp_object_manager_new ();
+ g_autoptr (WpObjectManager) sessions_om =
+ self->sessions_om = wp_object_manager_new ();
g_autoptr (WpObjectManager) endpoints_om =
self->endpoints_om = wp_object_manager_new ();
GVariantBuilder b;
@@ -210,11 +278,13 @@ audiomixer_thread (struct audiomixer * self)
g_variant_new_string ("Audio/Sink"));
g_variant_builder_close (&b);
- wp_object_manager_add_proxy_interest (self->endpoints_om,
+ wp_object_manager_add_proxy_interest (endpoints_om,
PW_TYPE_INTERFACE_Endpoint,
g_variant_builder_end (&b),
WP_PROXY_FEATURE_INFO | WP_PROXY_ENDPOINT_FEATURE_CONTROLS);
- wp_core_install_object_manager (self->core, self->endpoints_om);
+ g_signal_connect (endpoints_om, "objects-changed",
+ (GCallback) endpoints_changed, self);
+ wp_core_install_object_manager (self->core, endpoints_om);
g_signal_connect (self->core, "remote-state-changed",
(GCallback) remote_state_changed, self);
@@ -249,6 +319,7 @@ audiomixer_new (void)
self->state = WP_REMOTE_STATE_UNCONNECTED;
self->mixer_controls = g_ptr_array_new_with_free_func (
(GDestroyNotify) mixer_control_impl_free);
+ g_weak_ref_init (&self->session, NULL);
self->thread = g_thread_new ("audiomixer", (GThreadFunc) audiomixer_thread,
self);
@@ -262,6 +333,7 @@ audiomixer_free(struct audiomixer *self)
g_main_loop_quit (self->loop);
g_thread_join (self->thread);
+ g_weak_ref_clear (&self->session);
g_ptr_array_unref (self->mixer_controls);
g_object_unref (self->core);
g_main_loop_unref (self->loop);
@@ -284,13 +356,6 @@ audiomixer_unlock(struct audiomixer *self)
g_mutex_unlock (&self->lock);
}
-static gboolean
-connect_in_idle (struct audiomixer * self)
-{
- wp_core_connect (self->core);
- return G_SOURCE_REMOVE;
-}
-
int
audiomixer_ensure_connected(struct audiomixer *self, int timeout_sec)
{