diff options
Diffstat (limited to 'src/pulsecore/sink-input.c')
-rw-r--r-- | src/pulsecore/sink-input.c | 391 |
1 files changed, 276 insertions, 115 deletions
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index d25cd797..d4d11194 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -30,6 +30,7 @@ #include <pulse/utf8.h> #include <pulse/xmalloc.h> +#include <pulse/util.h> #include <pulsecore/sample-util.h> #include <pulsecore/core-subscribe.h> @@ -71,11 +72,18 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const data->channel_map = *map; } -void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { +void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { pa_assert(data); - if ((data->volume_is_set = !!volume)) - data->volume = data->virtual_volume = *volume; + if ((data->soft_volume_is_set = !!volume)) + data->soft_volume = *volume; +} + +void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { + pa_assert(data); + + if ((data->virtual_volume_is_set = !!volume)) + data->virtual_volume = *volume; } void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) { @@ -130,8 +138,10 @@ pa_sink_input* pa_sink_input_new( pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); - if (!data->sink) - data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, TRUE); + if (!data->sink) { + data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK); + data->save_sink = FALSE; + } pa_return_null_if_fail(data->sink); pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED); @@ -152,17 +162,36 @@ pa_sink_input* pa_sink_input_new( pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec)); - if (!data->volume_is_set) { - pa_cvolume_reset(&data->volume, data->sample_spec.channels); - pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); - } + if (!data->virtual_volume_is_set) { + + if (data->sink->flags & PA_SINK_FLAT_VOLUME) { + data->virtual_volume = data->sink->virtual_volume; + pa_cvolume_remap(&data->virtual_volume, &data->sink->channel_map, &data->channel_map); + } else + pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); + + data->save_volume = FALSE; - pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); - pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec)); + } else if (!data->virtual_volume_is_absolute) { + + /* When the 'absolute' bool is set then we'll treat the volume + * as relative to the sink volume even in flat volume mode */ + if (data->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume t = data->sink->virtual_volume; + pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map); + pa_sw_cvolume_multiply(&data->virtual_volume, &data->virtual_volume, &t); + } + } pa_return_null_if_fail(pa_cvolume_valid(&data->virtual_volume)); pa_return_null_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec)); + if (!data->soft_volume_is_set) + data->soft_volume = data->virtual_volume; + + pa_return_null_if_fail(pa_cvolume_valid(&data->soft_volume)); + pa_return_null_if_fail(pa_cvolume_compatible(&data->soft_volume, &data->sample_spec)); + if (!data->muted_is_set) data->muted = FALSE; @@ -183,13 +212,17 @@ pa_sink_input* pa_sink_input_new( pa_assert(pa_channel_map_valid(&data->channel_map)); /* Due to the fixing of the sample spec the volume might not match anymore */ - pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map); + pa_cvolume_remap(&data->soft_volume, &original_cm, &data->channel_map); + pa_cvolume_remap(&data->virtual_volume, &original_cm, &data->channel_map); if (data->resample_method == PA_RESAMPLER_INVALID) data->resample_method = core->resample_method; pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX); + if (data->client) + pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0) return NULL; @@ -214,8 +247,6 @@ pa_sink_input* pa_sink_input_new( pa_log_warn("Unsupported resampling operation."); return NULL; } - - data->resample_method = pa_resampler_get_method(resampler); } i = pa_msgobject_new(pa_sink_input); @@ -226,17 +257,21 @@ pa_sink_input* pa_sink_input_new( i->state = PA_SINK_INPUT_INIT; i->flags = flags; i->proplist = pa_proplist_copy(data->proplist); - i->driver = pa_xstrdup(data->driver); + i->driver = pa_xstrdup(pa_path_get_filename(data->driver)); i->module = data->module; i->sink = data->sink; i->client = data->client; - i->resample_method = data->resample_method; + i->requested_resample_method = data->resample_method; + i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; i->sample_spec = data->sample_spec; i->channel_map = data->channel_map; i->virtual_volume = data->virtual_volume; - i->volume = data->volume; + i->soft_volume = data->soft_volume; + i->save_volume = data->save_volume; + i->save_sink = data->save_sink; + i->save_muted = data->save_muted; i->muted = data->muted; @@ -260,11 +295,12 @@ pa_sink_input* pa_sink_input_new( pa_atomic_store(&i->thread_info.drained, 1); i->thread_info.sample_spec = i->sample_spec; i->thread_info.resampler = resampler; - i->thread_info.volume = i->volume; + i->thread_info.soft_volume = i->soft_volume; i->thread_info.muted = i->muted; i->thread_info.requested_sink_latency = (pa_usec_t) -1; i->thread_info.rewrite_nbytes = 0; i->thread_info.rewrite_flush = FALSE; + i->thread_info.dont_rewind_render = FALSE; i->thread_info.underrun_for = (uint64_t) -1; i->thread_info.playing_for = 0; i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); @@ -282,6 +318,9 @@ pa_sink_input* pa_sink_input_new( pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0); pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0); + if (i->client) + pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0); + pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), @@ -298,6 +337,9 @@ pa_sink_input* pa_sink_input_new( static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) { pa_assert(i); + if (!i->sink) + return; + if (i->state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) pa_assert_se(i->sink->n_corked -- >= 1); else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED) @@ -330,13 +372,13 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) { } if (state != PA_SINK_INPUT_UNLINKED) { - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i); for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev) - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); for (ssync = i->sync_next; ssync; ssync = ssync->sync_next) - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); } pa_sink_update_status(i->sink); @@ -358,7 +400,7 @@ void pa_sink_input_unlink(pa_sink_input *i) { linked = PA_SINK_INPUT_IS_LINKED(i->state); if (linked) - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i); if (i->sync_prev) i->sync_prev->sync_next = i->sync_next; @@ -367,9 +409,14 @@ void pa_sink_input_unlink(pa_sink_input *i) { i->sync_prev = i->sync_next = NULL; - pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL); - if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL)) - pa_sink_input_unref(i); + pa_idxset_remove_by_data(i->core->sink_inputs, i, NULL); + + if (i->sink) + if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL)) + pa_sink_input_unref(i); + + if (i->client) + pa_idxset_remove_by_data(i->client->sink_inputs, i, NULL); while ((o = pa_idxset_first(i->direct_outputs, NULL))) { pa_assert(o != p); @@ -380,20 +427,30 @@ void pa_sink_input_unlink(pa_sink_input *i) { update_n_corked(i, PA_SINK_INPUT_UNLINKED); i->state = PA_SINK_INPUT_UNLINKED; - if (linked) + if (linked && i->sink) { + /* We might need to update the sink's volume if we are in flat volume mode. */ + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume new_volume; + pa_sink_update_flat_volume(i->sink, &new_volume); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); + } + if (i->sink->asyncmsgq) pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0); + } reset_callbacks(i); if (linked) { - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index); - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i); } - pa_sink_update_status(i->sink); + if (i->sink) { + pa_sink_update_status(i->sink); + i->sink = NULL; + } - i->sink = NULL; pa_sink_input_unref(i); } @@ -442,7 +499,7 @@ void pa_sink_input_put(pa_sink_input *i) { pa_assert(i->process_rewind); pa_assert(i->kill); - i->thread_info.volume = i->volume; + i->thread_info.soft_volume = i->soft_volume; i->thread_info.muted = i->muted; state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; @@ -450,10 +507,17 @@ void pa_sink_input_put(pa_sink_input *i) { update_n_corked(i, state); i->state = state; + /* We might need to update the sink's volume if we are in flat volume mode. */ + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume new_volume; + pa_sink_update_flat_volume(i->sink, &new_volume); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); + } + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0); - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i); pa_sink_update_status(i->sink); } @@ -508,9 +572,9 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa block_size_max_sink_input = i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : - pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sample_spec); + pa_frame_align(pa_mempool_block_size_max(i->core->mempool), &i->sample_spec); - block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sink->sample_spec); + block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->core->mempool), &i->sink->sample_spec); /* Default buffer size */ if (slength <= 0) @@ -535,7 +599,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa * it after and leave it for the sink code */ do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map); - volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted; + volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted; while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) { pa_memchunk tchunk; @@ -581,7 +645,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa if (i->thread_info.muted) pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec); else - pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.volume); + pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.soft_volume); } if (!i->thread_info.resampler) @@ -627,7 +691,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa /* We've both the same channel map, so let's have the sink do the adjustment for us*/ pa_cvolume_mute(volume, i->sink->sample_spec.channels); else - *volume = i->thread_info.volume; + *volume = i->thread_info.soft_volume; return 0; } @@ -658,7 +722,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); - if (nbytes > 0) { + if (nbytes > 0 && !i->thread_info.dont_rewind_render) { pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes); pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); } @@ -714,6 +778,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam i->thread_info.rewrite_nbytes = 0; i->thread_info.rewrite_flush = FALSE; + i->thread_info.dont_rewind_render = FALSE; } /* Called from thread context */ @@ -797,35 +862,43 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) { } /* Called from main context */ -void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { - pa_sink_input_set_volume_data data; - +void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); pa_assert(volume); pa_assert(pa_cvolume_valid(volume)); pa_assert(pa_cvolume_compatible(volume, &i->sample_spec)); - data.sink_input = i; - data.virtual_volume = *volume; - data.volume = *volume; + if (pa_cvolume_equal(volume, &i->virtual_volume)) + return; - /* If you change something here, consider looking into - * module-flat-volume.c as well since it uses very similar - * code. */ + i->virtual_volume = *volume; + i->save_volume = save; - if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], &data) < 0) - return; + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume new_volume; - if (!pa_cvolume_equal(&i->volume, &data.volume)) { - i->volume = data.volume; - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &data.volume, 0, NULL) == 0); - } + /* We are in flat volume mode, so let's update all sink input + * volumes and update the flat volume of the sink */ - if (!pa_cvolume_equal(&i->virtual_volume, &data.virtual_volume)) { - i->virtual_volume = data.virtual_volume; - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + pa_sink_update_flat_volume(i->sink, &new_volume); + pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE); + + } else { + + /* OK, we are in normal volume mode. The volume only affects + * ourselves */ + + i->soft_volume = *volume; + + /* Hooks have the ability to play games with i->soft_volume */ + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i); + + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0); } + + /* The virtual volume changed, let's tell people so */ + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } /* Called from main context */ @@ -837,7 +910,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { } /* Called from main context */ -void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { +void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) { pa_assert(i); pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); @@ -846,9 +919,10 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { return; i->muted = mute; + i->save_muted = save; - pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL); - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } /* Called from main context */ @@ -859,6 +933,21 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) { return i->muted; } +/* Called from main thread */ +pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) { + + pa_sink_input_assert_ref(i); + + pa_proplist_update(i->proplist, mode, p); + + if (PA_SINK_IS_LINKED(i->state)) { + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + } + + return TRUE; +} + /* Called from main context */ void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) { pa_sink_input_assert_ref(i); @@ -880,7 +969,7 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL); - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); return 0; } @@ -903,8 +992,8 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) { pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME); if (PA_SINK_INPUT_IS_LINKED(i->state)) { - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } } @@ -912,17 +1001,13 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) { pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { pa_sink_input_assert_ref(i); - return i->resample_method; + return i->actual_resample_method; } /* Called from main context */ -pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { +pa_bool_t pa_sink_input_may_move(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - pa_sink_assert_ref(dest); - - if (dest == i->sink) - return TRUE; if (i->flags & PA_SINK_INPUT_DONT_MOVE) return FALSE; @@ -932,6 +1017,21 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { return FALSE; } + return TRUE; +} + +/* Called from main context */ +pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { + pa_sink_input_assert_ref(i); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_sink_assert_ref(dest); + + if (dest == i->sink) + return TRUE; + + if (!pa_sink_input_may_move(i)) + return FALSE; + if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) { pa_log_warn("Failed to move sink input: too many inputs per sink."); return FALSE; @@ -945,34 +1045,72 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { } /* Called from main context */ -int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { - pa_resampler *new_resampler; - pa_sink *origin; - pa_sink_input_move_hook_data hook_data; +int pa_sink_input_start_move(pa_sink_input *i) { pa_source_output *o, *p = NULL; + pa_sink *origin; pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - pa_sink_assert_ref(dest); + pa_assert(i->sink); - origin = i->sink; - - if (dest == origin) - return 0; + if (!pa_sink_input_may_move(i)) + return -1; - if (!pa_sink_input_may_move_to(i, dest)) + if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i) < 0) return -1; + origin = i->sink; + /* Kill directly connected outputs */ while ((o = pa_idxset_first(i->direct_outputs, NULL))) { pa_assert(o != p); pa_source_output_kill(o); p = o; } + pa_assert(pa_idxset_isempty(i->direct_outputs)); + + pa_idxset_remove_by_data(i->sink->inputs, i, NULL); + + if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) + pa_assert_se(i->sink->n_corked-- >= 1); + + /* We might need to update the sink's volume if we are in flat volume mode. */ + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume new_volume; + pa_sink_update_flat_volume(i->sink, &new_volume); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); + } + + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); + + pa_sink_update_status(i->sink); + i->sink = NULL; + + return 0; +} + +/* Called from main context */ +int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { + pa_resampler *new_resampler; + + pa_sink_input_assert_ref(i); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_assert(!i->sink); + pa_sink_assert_ref(dest); + + if (!pa_sink_input_may_move_to(i, dest)) + return -1; + + i->sink = dest; + i->save_sink = save; + pa_idxset_put(dest->inputs, i, NULL); + + if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) + i->sink->n_corked++; if (i->thread_info.resampler && - pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) && - pa_channel_map_equal(&origin->channel_map, &dest->channel_map)) + pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) && + pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &dest->channel_map)) /* Try to reuse the old resampler if possible */ new_resampler = i->thread_info.resampler; @@ -984,10 +1122,10 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { /* Okey, we need a new resampler for the new sink */ if (!(new_resampler = pa_resampler_new( - dest->core->mempool, + i->core->mempool, &i->sample_spec, &i->channel_map, &dest->sample_spec, &dest->channel_map, - i->resample_method, + i->requested_resample_method, ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) { @@ -997,21 +1135,6 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { } else new_resampler = NULL; - hook_data.sink_input = i; - hook_data.destination = dest; - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data); - - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); - - pa_idxset_remove_by_data(origin->inputs, i, NULL); - pa_idxset_put(dest->inputs, i, NULL); - i->sink = dest; - - if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) { - pa_assert_se(origin->n_corked-- >= 1); - dest->n_corked++; - } - /* Replace resampler and render queue */ if (new_resampler != i->thread_info.resampler) { @@ -1032,20 +1155,47 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { &i->sink->silence); } - pa_sink_update_status(origin); pa_sink_update_status(dest); + /* We might need to update the sink's volume if we are in flat volume mode. */ + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume new_volume; + pa_sink_update_flat_volume(i->sink, &new_volume); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); + } + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); + pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name); + + /* Notify everyone */ if (i->moved) i->moved(i); - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); - pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name); + return 0; +} - /* Notify everyone */ - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +/* Called from main context */ +int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { + pa_sink_input_assert_ref(i); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_assert(i->sink); + pa_sink_assert_ref(dest); + + if (dest == i->sink) + return 0; + + if (!pa_sink_input_may_move_to(i, dest)) + return -1; + + if (pa_sink_input_start_move(i) < 0) + return -1; + + if (pa_sink_input_finish_move(i, dest, save) < 0) + return -1; return 0; } @@ -1076,7 +1226,7 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state /* This will tell the implementing sink input driver to rewind * so that the unplayed already mixed data is not lost */ - pa_sink_input_request_rewind(i, 0, TRUE, TRUE); + pa_sink_input_request_rewind(i, 0, TRUE, TRUE, FALSE); } else if (uncorking) { @@ -1087,7 +1237,7 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state /* OK, we're being uncorked. Make sure we're not rewound when * the hw buffer is remixed and request a remix. */ - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } } @@ -1098,14 +1248,18 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t switch (code) { - case PA_SINK_INPUT_MESSAGE_SET_VOLUME: - i->thread_info.volume = *((pa_cvolume*) userdata); - pa_sink_input_request_rewind(i, 0, TRUE, FALSE); + case PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME: + if (!pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) { + i->thread_info.soft_volume = i->soft_volume; + pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); + } return 0; - case PA_SINK_INPUT_MESSAGE_SET_MUTE: - i->thread_info.muted = PA_PTR_TO_UINT(userdata); - pa_sink_input_request_rewind(i, 0, TRUE, FALSE); + case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE: + if (i->thread_info.muted != i->muted) { + i->thread_info.muted = i->muted; + pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); + } return 0; case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { @@ -1180,7 +1334,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { } /* Called from IO context */ -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) { +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush, pa_bool_t dont_rewind_render) { size_t lbq; /* If 'rewrite' is TRUE the sink is rewound as far as requested @@ -1191,7 +1345,9 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam * If 'rewrite' is FALSE the sink is rewound as far as requested * and possible and the already rendered data is dropped so that * in the next iteration we read new data from the - * implementor. This implies 'flush' is TRUE. */ + * implementor. This implies 'flush' is TRUE. If + * dont_rewind_render is TRUE then the render memblockq is not + * rewound. */ pa_sink_input_assert_ref(i); @@ -1204,6 +1360,7 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam return; pa_assert(rewrite || flush); + pa_assert(!dont_rewind_render || !rewrite); /* Calculate how much we can rewind locally without having to * touch the sink */ @@ -1238,6 +1395,10 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam i->thread_info.rewrite_flush || (flush && i->thread_info.rewrite_nbytes != 0); + i->thread_info.dont_rewind_render = + i->thread_info.dont_rewind_render || + dont_rewind_render; + if (nbytes != (size_t) -1) { /* Transform to sink domain */ @@ -1258,8 +1419,8 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) { pa_assert(ret); pa_silence_memchunk_get( - &i->sink->core->silence_cache, - i->sink->core->mempool, + &i->core->silence_cache, + i->core->mempool, ret, &i->sample_spec, i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0); |