The alsa sink calls pa_sink_suspend() from the set_port() callback. pa_sink_suspend() can only be called from the main thread, but the set_port() callback is often called from the IO thread. That caused an assertion to be hit in pa_sink_suspend() when switching ports. Another issue was that pa_sink_suspend() called the set_port() callback, and if the callback calls pa_sink_suspend() again recursively, nothing good can be expected from that, so the thread mismatch was not the only problem. This patch moves the mixer syncing logic out of pa_sink/source_suspend() to be handled internally by the alsa sink/source. This removes the recursive pa_sink_suspend() call. This also removes the need to have the mixer_dirty flag in pa_sink/source, so the flag and the pa_sink/source_set_mixer_dirty() functions can be removed. This patch also changes the threading rules of set_port(). Previously it was called sometimes from the main thread and sometimes from the IO thread. Now it's always called from the main thread. When deferred volumes are used, the alsa sink and source still have to update the mixer from the IO thread when switching ports, but the thread synchronization is now handled internally by the alsa sink and source. The SET_PORT messages are not needed any more and can be removed. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=104761 --- src/modules/alsa/alsa-sink.c | 94 +++++++++++++++++++++++++++++++++--------- src/modules/alsa/alsa-source.c | 94 +++++++++++++++++++++++++++++++++--------- src/pulsecore/sink.c | 52 +---------------------- src/pulsecore/sink.h | 7 +--- src/pulsecore/source.c | 52 +---------------------- src/pulsecore/source.h | 7 +--- 6 files changed, 156 insertions(+), 150 deletions(-) diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index adc6c0c82..fe5fd369c 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -161,6 +161,10 @@ struct userdata { pa_alsa_ucm_mapping_context *ucm_context; }; +enum { + SINK_MESSAGE_SYNC_MIXER = PA_SINK_MESSAGE_MAX +}; + static void userdata_free(struct userdata *u); /* FIXME: Is there a better way to do this than device names? */ @@ -1168,6 +1172,42 @@ fail: return -PA_ERR_IO; } +/* Called from the IO thread or the main thread depending on whether deferred + * volume is enabled or not (with deferred volume all mixer handling is done + * from the IO thread). + * + * Sets the mixer settings to match the current sink and port state (the port + * is given as an argument, because active_port may still point to the old + * port, if we're switching ports). */ +static void sync_mixer(struct userdata *u, pa_device_port *port) { + pa_alsa_setting *setting = NULL; + + pa_assert(u); + + if (!u->mixer_path) + return; + + /* If we use a synthesized mixer path, then the sink has no ports. */ + if (port) { + pa_alsa_port_data *data; + + data = PA_DEVICE_PORT_DATA(u->sink->active_port); + setting = data->setting; + } + + pa_alsa_path_select(u->mixer_path, setting, u->mixer_handle, u->sink->muted); + + if (u->sink->set_mute) + u->sink->set_mute(u->sink); + if (u->sink->flags & PA_SINK_DEFERRED_VOLUME) { + if (u->sink->write_volume) + u->sink->write_volume(u->sink); + } else { + if (u->sink->set_volume) + u->sink->set_volume(u->sink); + } +} + /* Called from IO context */ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; @@ -1186,10 +1226,21 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse } case PA_SINK_MESSAGE_SET_STATE: { + pa_suspend_cause_t new_suspend_cause = ((pa_sink_message_set_state *) data)->suspend_cause; pa_sink_state_t new_state = ((pa_sink_message_set_state *) data)->state; + /* When our session becomes active, we need to sync the mixer, + * because another user may have changed the mixer settings. + * + * If deferred volume is disabled, the syncing is done in the + * set_state() callback instead. */ + if ((u->sink->flags & PA_SINK_DEFERRED_VOLUME) + && (u->sink->suspend_cause & PA_SUSPEND_SESSION) + && !(new_suspend_cause & PA_SUSPEND_SESSION)) + sync_mixer(u, u->sink->active_port); + /* It may be that only the suspend cause is changing, in which - * case there's nothing to do. */ + * case there's nothing more to do. */ if (new_state == u->sink->thread_info.state) break; @@ -1231,6 +1282,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse break; } + + case SINK_MESSAGE_SYNC_MIXER: { + pa_device_port *port = data; + + sync_mixer(u, port); + return 0; + } } return pa_sink_process_msg(o, code, data, offset, chunk); @@ -1244,6 +1302,16 @@ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_c pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); + /* When our session becomes active, we need to sync the mixer, because + * another user may have changed the mixer settings. + * + * If deferred volume is enabled, the syncing is done in the SET_STATE + * message handler instead. */ + if (!(u->sink->flags & PA_SINK_DEFERRED_VOLUME) + && (u->sink->suspend_cause & PA_SUSPEND_SESSION) + && !(new_suspend_cause & PA_SUSPEND_SESSION)) + sync_mixer(u, u->sink->active_port); + old_state = pa_sink_get_state(u->sink); if (PA_SINK_IS_OPENED(old_state) && new_state == PA_SINK_SUSPENDED) @@ -1267,10 +1335,8 @@ static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { if (!PA_SINK_IS_LINKED(u->sink->state)) return 0; - if (u->sink->suspend_cause & PA_SUSPEND_SESSION) { - pa_sink_set_mixer_dirty(u->sink, true); + if (u->sink->suspend_cause & PA_SUSPEND_SESSION) return 0; - } if (mask & SND_CTL_EVENT_MASK_VALUE) { pa_sink_get_volume(u->sink, true); @@ -1289,10 +1355,8 @@ static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { if (mask == SND_CTL_EVENT_MASK_REMOVE) return 0; - if (u->sink->suspend_cause & PA_SUSPEND_SESSION) { - pa_sink_set_mixer_dirty(u->sink, true); + if (u->sink->suspend_cause & PA_SUSPEND_SESSION) return 0; - } if (mask & SND_CTL_EVENT_MASK_VALUE) pa_sink_update_volume_and_mute(u->sink); @@ -1516,21 +1580,13 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) { pa_assert(u->mixer_handle); data = PA_DEVICE_PORT_DATA(p); - pa_assert_se(u->mixer_path = data->path); - pa_alsa_path_select(u->mixer_path, data->setting, u->mixer_handle, s->muted); - mixer_volume_init(u); - if (s->set_mute) - s->set_mute(s); - if (s->flags & PA_SINK_DEFERRED_VOLUME) { - if (s->write_volume) - s->write_volume(s); - } else { - if (s->set_volume) - s->set_volume(s); - } + if (s->flags & PA_SINK_DEFERRED_VOLUME) + pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_SYNC_MIXER, p, 0, NULL); + else + sync_mixer(u, p); if (data->suspend_when_unavailable && p->available == PA_AVAILABLE_NO) pa_sink_suspend(s, true, PA_SUSPEND_UNAVAILABLE); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 61062d230..bb42c39a1 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -143,6 +143,10 @@ struct userdata { pa_alsa_ucm_mapping_context *ucm_context; }; +enum { + SOURCE_MESSAGE_SYNC_MIXER = PA_SOURCE_MESSAGE_MAX +}; + static void userdata_free(struct userdata *u); static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct userdata *u) { @@ -1023,6 +1027,42 @@ fail: return -PA_ERR_IO; } +/* Called from the IO thread or the main thread depending on whether deferred + * volume is enabled or not (with deferred volume all mixer handling is done + * from the IO thread). + * + * Sets the mixer settings to match the current source and port state (the port + * is given as an argument, because active_port may still point to the old + * port, if we're switching ports). */ +static void sync_mixer(struct userdata *u, pa_device_port *port) { + pa_alsa_setting *setting = NULL; + + pa_assert(u); + + if (!u->mixer_path) + return; + + /* If we use a synthesized mixer path, then the source has no ports. */ + if (port) { + pa_alsa_port_data *data; + + data = PA_DEVICE_PORT_DATA(u->source->active_port); + setting = data->setting; + } + + pa_alsa_path_select(u->mixer_path, setting, u->mixer_handle, u->source->muted); + + if (u->source->set_mute) + u->source->set_mute(u->source); + if (u->source->flags & PA_SOURCE_DEFERRED_VOLUME) { + if (u->source->write_volume) + u->source->write_volume(u->source); + } else { + if (u->source->set_volume) + u->source->set_volume(u->source); + } +} + /* Called from IO context */ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SOURCE(o)->userdata; @@ -1041,10 +1081,21 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off } case PA_SOURCE_MESSAGE_SET_STATE: { + pa_suspend_cause_t new_suspend_cause = ((pa_source_message_set_state *) data)->suspend_cause; pa_source_state_t new_state = ((pa_source_message_set_state *) data)->state; + /* When our session becomes active, we need to sync the mixer, + * because another user may have changed the mixer settings. + * + * If deferred volume is disabled, the syncing is done in the + * set_state() callback instead. */ + if ((u->source->flags & PA_SOURCE_DEFERRED_VOLUME) + && (u->source->suspend_cause & PA_SUSPEND_SESSION) + && !(new_suspend_cause & PA_SUSPEND_SESSION)) + sync_mixer(u, u->source->active_port); + /* It may be that only the suspend cause is changing, in which - * case there's nothing to do. */ + * case there's nothing more to do. */ if (new_state == u->source->thread_info.state) break; @@ -1086,6 +1137,13 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off break; } + + case SOURCE_MESSAGE_SYNC_MIXER: { + pa_device_port *port = data; + + sync_mixer(u, port); + return 0; + } } return pa_source_process_msg(o, code, data, offset, chunk); @@ -1099,6 +1157,16 @@ static int source_set_state_cb(pa_source *s, pa_source_state_t new_state, pa_sus pa_source_assert_ref(s); pa_assert_se(u = s->userdata); + /* When our session becomes active, we need to sync the mixer, because + * another user may have changed the mixer settings. + * + * If deferred volume is enabled, the syncing is done in the SET_STATE + * message handler instead. */ + if (!(u->source->flags & PA_SOURCE_DEFERRED_VOLUME) + && (u->source->suspend_cause & PA_SUSPEND_SESSION) + && !(new_suspend_cause & PA_SUSPEND_SESSION)) + sync_mixer(u, u->source->active_port); + old_state = pa_source_get_state(u->source); if (PA_SOURCE_IS_OPENED(old_state) && new_state == PA_SOURCE_SUSPENDED) @@ -1122,10 +1190,8 @@ static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { if (!PA_SOURCE_IS_LINKED(u->source->state)) return 0; - if (u->source->suspend_cause & PA_SUSPEND_SESSION) { - pa_source_set_mixer_dirty(u->source, true); + if (u->source->suspend_cause & PA_SUSPEND_SESSION) return 0; - } if (mask & SND_CTL_EVENT_MASK_VALUE) { pa_source_get_volume(u->source, true); @@ -1144,10 +1210,8 @@ static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { if (mask == SND_CTL_EVENT_MASK_REMOVE) return 0; - if (u->source->suspend_cause & PA_SUSPEND_SESSION) { - pa_source_set_mixer_dirty(u->source, true); + if (u->source->suspend_cause & PA_SUSPEND_SESSION) return 0; - } if (mask & SND_CTL_EVENT_MASK_VALUE) pa_source_update_volume_and_mute(u->source); @@ -1371,21 +1435,13 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) { pa_assert(u->mixer_handle); data = PA_DEVICE_PORT_DATA(p); - pa_assert_se(u->mixer_path = data->path); - pa_alsa_path_select(u->mixer_path, data->setting, u->mixer_handle, s->muted); - mixer_volume_init(u); - if (s->set_mute) - s->set_mute(s); - if (s->flags & PA_SOURCE_DEFERRED_VOLUME) { - if (s->write_volume) - s->write_volume(s); - } else { - if (s->set_volume) - s->set_volume(s); - } + if (s->flags & PA_SOURCE_DEFERRED_VOLUME) + pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_SYNC_MIXER, p, 0, NULL); + else + sync_mixer(u, p); return 0; } diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index a9c2b3a8e..d8ccc07c5 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -64,11 +64,6 @@ struct pa_sink_volume_change { PA_LLIST_FIELDS(pa_sink_volume_change); }; -struct sink_message_set_port { - pa_device_port *port; - int ret; -}; - static void sink_free(pa_object *s); static void pa_sink_volume_change_push(pa_sink *s); @@ -253,7 +248,6 @@ pa_sink* pa_sink_new( s->flags = flags; s->priority = 0; s->suspend_cause = data->suspend_cause; - pa_sink_set_mixer_dirty(s, false); s->name = pa_xstrdup(name); s->proplist = pa_proplist_copy(data->proplist); s->driver = pa_xstrdup(pa_path_get_filename(data->driver)); @@ -868,11 +862,6 @@ int pa_sink_update_status(pa_sink*s) { return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE, 0); } -/* Called from any context - must be threadsafe */ -void pa_sink_set_mixer_dirty(pa_sink *s, bool is_dirty) { - pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0); -} - /* Called from main context */ int pa_sink_suspend(pa_sink *s, bool suspend, pa_suspend_cause_t cause) { pa_suspend_cause_t merged_cause; @@ -887,27 +876,6 @@ int pa_sink_suspend(pa_sink *s, bool suspend, pa_suspend_cause_t cause) { else merged_cause = s->suspend_cause & ~cause; - if (!(merged_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) { - /* This might look racy but isn't: If somebody sets mixer_dirty exactly here, - it'll be handled just fine. */ - pa_sink_set_mixer_dirty(s, false); - pa_log_debug("Mixer is now accessible. Updating alsa mixer settings."); - if (s->active_port && s->set_port) { - if (s->flags & PA_SINK_DEFERRED_VOLUME) { - struct sink_message_set_port msg = { .port = s->active_port, .ret = 0 }; - pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0); - } - else - s->set_port(s, s->active_port); - } - else { - if (s->set_mute) - s->set_mute(s); - if (s->set_volume) - s->set_volume(s); - } - } - if (merged_cause) return sink_set_state(s, PA_SINK_SUSPENDED, merged_cause); else @@ -2933,15 +2901,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_set_max_request_within_thread(s, (size_t) offset); return 0; - case PA_SINK_MESSAGE_SET_PORT: - - pa_assert(userdata); - if (s->set_port) { - struct sink_message_set_port *msg_data = userdata; - msg_data->ret = s->set_port(s, msg_data->port); - } - return 0; - case PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE: /* This message is sent from IO-thread and handled in main thread. */ pa_assert_ctl_context(); @@ -3396,7 +3355,6 @@ size_t pa_sink_get_max_request(pa_sink *s) { /* Called from main context */ int pa_sink_set_port(pa_sink *s, const char *name, bool save) { pa_device_port *port; - int ret; pa_sink_assert_ref(s); pa_assert_ctl_context(); @@ -3417,15 +3375,7 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) { return 0; } - if (s->flags & PA_SINK_DEFERRED_VOLUME) { - struct sink_message_set_port msg = { .port = port, .ret = 0 }; - pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0); - ret = msg.ret; - } - else - ret = s->set_port(s, port); - - if (ret < 0) + if (s->set_port(s, port) < 0) return -PA_ERR_NOENTITY; pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 1f0f981c3..6d404c920 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -115,7 +115,6 @@ struct pa_sink { pa_hashmap *ports; pa_device_port *active_port; - pa_atomic_t mixer_dirty; /* The latency offset is inherited from the currently active port */ int64_t port_latency_offset; @@ -238,8 +237,8 @@ struct pa_sink { * thread context. */ pa_sink_cb_t update_requested_latency; /* may be NULL */ - /* Called whenever the port shall be changed. Called from IO - * thread if deferred volumes are enabled, and main thread otherwise. */ + /* Called whenever the port shall be changed. Called from the main + * thread. */ int (*set_port)(pa_sink *s, pa_device_port *port); /* may be NULL */ /* Called to get the list of formats supported by the sink, sorted @@ -360,7 +359,6 @@ typedef enum pa_sink_message { PA_SINK_MESSAGE_GET_MAX_REQUEST, PA_SINK_MESSAGE_SET_MAX_REWIND, PA_SINK_MESSAGE_SET_MAX_REQUEST, - PA_SINK_MESSAGE_SET_PORT, PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE, PA_SINK_MESSAGE_SET_PORT_LATENCY_OFFSET, PA_SINK_MESSAGE_MAX @@ -493,7 +491,6 @@ bool pa_sink_get_mute(pa_sink *sink, bool force_refresh); bool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p); int pa_sink_set_port(pa_sink *s, const char *name, bool save); -void pa_sink_set_mixer_dirty(pa_sink *s, bool is_dirty); unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */ unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */ diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 88e8632c0..cb512d088 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -56,11 +56,6 @@ struct pa_source_volume_change { PA_LLIST_FIELDS(pa_source_volume_change); }; -struct source_message_set_port { - pa_device_port *port; - int ret; -}; - static void source_free(pa_object *o); static void pa_source_volume_change_push(pa_source *s); @@ -240,7 +235,6 @@ pa_source* pa_source_new( s->flags = flags; s->priority = 0; s->suspend_cause = data->suspend_cause; - pa_source_set_mixer_dirty(s, false); s->name = pa_xstrdup(name); s->proplist = pa_proplist_copy(data->proplist); s->driver = pa_xstrdup(pa_path_get_filename(data->driver)); @@ -789,11 +783,6 @@ int pa_source_update_status(pa_source*s) { return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE, 0); } -/* Called from any context - must be threadsafe */ -void pa_source_set_mixer_dirty(pa_source *s, bool is_dirty) { - pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0); -} - /* Called from main context */ int pa_source_suspend(pa_source *s, bool suspend, pa_suspend_cause_t cause) { pa_suspend_cause_t merged_cause; @@ -811,27 +800,6 @@ int pa_source_suspend(pa_source *s, bool suspend, pa_suspend_cause_t cause) { else merged_cause = s->suspend_cause & ~cause; - if (!(merged_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) { - /* This might look racy but isn't: If somebody sets mixer_dirty exactly here, - it'll be handled just fine. */ - pa_source_set_mixer_dirty(s, false); - pa_log_debug("Mixer is now accessible. Updating alsa mixer settings."); - if (s->active_port && s->set_port) { - if (s->flags & PA_SOURCE_DEFERRED_VOLUME) { - struct source_message_set_port msg = { .port = s->active_port, .ret = 0 }; - pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0); - } - else - s->set_port(s, s->active_port); - } - else { - if (s->set_mute) - s->set_mute(s); - if (s->set_volume) - s->set_volume(s); - } - } - if (merged_cause) return source_set_state(s, PA_SOURCE_SUSPENDED, merged_cause); else @@ -2302,15 +2270,6 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ /* Implementors need to overwrite this implementation! */ return -1; - case PA_SOURCE_MESSAGE_SET_PORT: - - pa_assert(userdata); - if (s->set_port) { - struct source_message_set_port *msg_data = userdata; - msg_data->ret = s->set_port(s, msg_data->port); - } - return 0; - case PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE: /* This message is sent from IO-thread and handled in main thread. */ pa_assert_ctl_context(); @@ -2679,7 +2638,6 @@ size_t pa_source_get_max_rewind(pa_source *s) { /* Called from main context */ int pa_source_set_port(pa_source *s, const char *name, bool save) { pa_device_port *port; - int ret; pa_source_assert_ref(s); pa_assert_ctl_context(); @@ -2700,15 +2658,7 @@ int pa_source_set_port(pa_source *s, const char *name, bool save) { return 0; } - if (s->flags & PA_SOURCE_DEFERRED_VOLUME) { - struct source_message_set_port msg = { .port = port, .ret = 0 }; - pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0); - ret = msg.ret; - } - else - ret = s->set_port(s, port); - - if (ret < 0) + if (s->set_port(s, port) < 0) return -PA_ERR_NOENTITY; pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 035aa185e..65f168332 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -116,7 +116,6 @@ struct pa_source { pa_hashmap *ports; pa_device_port *active_port; - pa_atomic_t mixer_dirty; /* The latency offset is inherited from the currently active port */ int64_t port_latency_offset; @@ -202,8 +201,8 @@ struct pa_source { * thread context. */ pa_source_cb_t update_requested_latency; /* may be NULL */ - /* Called whenever the port shall be changed. Called from IO - * thread if deferred volumes are enabled, and main thread otherwise. */ + /* Called whenever the port shall be changed. Called from the main + * thread. */ int (*set_port)(pa_source *s, pa_device_port *port); /*ditto */ /* Called to get the list of formats supported by the source, sorted @@ -298,7 +297,6 @@ typedef enum pa_source_message { PA_SOURCE_MESSAGE_GET_FIXED_LATENCY, PA_SOURCE_MESSAGE_GET_MAX_REWIND, PA_SOURCE_MESSAGE_SET_MAX_REWIND, - PA_SOURCE_MESSAGE_SET_PORT, PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE, PA_SOURCE_MESSAGE_SET_PORT_LATENCY_OFFSET, PA_SOURCE_MESSAGE_MAX @@ -425,7 +423,6 @@ bool pa_source_get_mute(pa_source *source, bool force_refresh); bool pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p); int pa_source_set_port(pa_source *s, const char *name, bool save); -void pa_source_set_mixer_dirty(pa_source *s, bool is_dirty); int pa_source_reconfigure(pa_source *s, pa_sample_spec *spec, bool passthrough); -- 2.15.1