summaryrefslogtreecommitdiffstats
path: root/src/pulsecore/sink-input.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore/sink-input.c')
-rw-r--r--src/pulsecore/sink-input.c391
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);