#include <pulse/util.h>
#include <pulse/internal.h>
+#include <pulsecore/core-format.h>
#include <pulsecore/mix.h>
+#include <pulsecore/stream-util.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/play-memblockq.h>
data->proplist = pa_proplist_new();
data->volume_writable = true;
- data->volume_factor_items = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- data->volume_factor_sink_items = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ data->volume_factor_items = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+ (pa_free_cb_t) volume_factor_entry_free);
+ data->volume_factor_sink_items = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
+ (pa_free_cb_t) volume_factor_entry_free);
return data;
}
pa_assert(formats);
if (data->req_formats)
- pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+ pa_idxset_free(data->req_formats, (pa_free_cb_t) pa_format_info_free);
data->req_formats = formats;
pa_format_info_free(data->format);
if (data->volume_factor_items)
- pa_hashmap_free(data->volume_factor_items, (pa_free_cb_t) volume_factor_entry_free);
+ pa_hashmap_free(data->volume_factor_items);
if (data->volume_factor_sink_items)
- pa_hashmap_free(data->volume_factor_sink_items, (pa_free_cb_t) volume_factor_entry_free);
+ pa_hashmap_free(data->volume_factor_sink_items);
pa_proplist_free(data->proplist);
}
pa_sink_input *i;
pa_resampler *resampler = NULL;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], fmt[PA_FORMAT_INFO_SNPRINT_MAX];
- pa_channel_map original_cm;
+ pa_channel_map volume_map;
int r;
char *pt;
char *memblockq_name;
- pa_sample_spec ss;
- pa_channel_map map;
pa_assert(_i);
pa_assert(core);
/* From this point on, we want to work only with formats, and get back
* to using the sample spec and channel map after all decisions w.r.t.
* routing are complete. */
- pa_idxset *tmp = pa_idxset_new(NULL, NULL);
- pa_format_info *f = pa_format_info_from_sample_spec(&data->sample_spec,
- data->channel_map_is_set ? &data->channel_map : NULL);
- pa_idxset_put(tmp, f, NULL);
- pa_sink_input_new_data_set_formats(data, tmp);
+ pa_format_info *f;
+ pa_idxset *formats;
+
+ f = pa_format_info_from_sample_spec2(&data->sample_spec, data->channel_map_is_set ? &data->channel_map : NULL,
+ !(data->flags & PA_SINK_INPUT_FIX_FORMAT),
+ !(data->flags & PA_SINK_INPUT_FIX_RATE),
+ !(data->flags & PA_SINK_INPUT_FIX_CHANNELS));
+ if (!f)
+ return -PA_ERR_INVALID;
+
+ formats = pa_idxset_new(NULL, NULL);
+ pa_idxset_put(formats, f, NULL);
+ pa_sink_input_new_data_set_formats(data, formats);
}
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0)
pa_return_val_if_fail(sink, -PA_ERR_NOENTITY);
pa_sink_input_new_data_set_sink(data, sink, false);
}
- /* Routing's done, we have a sink. Now let's fix the format and set up the
- * sample spec */
/* If something didn't pick a format for us, pick the top-most format since
* we assume this is sorted in priority order */
return -PA_ERR_NOTSUPPORTED;
}
- /* Now populate the sample spec and format according to the final
- * format that we've negotiated */
- pa_return_val_if_fail(pa_format_info_to_sample_spec(data->format, &ss, &map) == 0, -PA_ERR_INVALID);
- pa_sink_input_new_data_set_sample_spec(data, &ss);
- if (pa_format_info_is_pcm(data->format) && pa_channel_map_valid(&map))
- pa_sink_input_new_data_set_channel_map(data, &map);
-
pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE);
- pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID);
+ pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink
+ && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED),
+ -PA_ERR_INVALID);
+
+ /* Routing is done. We have a sink and a format. */
+
+ if (data->volume_is_set && pa_format_info_is_pcm(data->format)) {
+ /* If volume is set, we need to save the original data->channel_map,
+ * so that we can remap the volume from the original channel map to the
+ * final channel map of the stream in case data->channel_map gets
+ * modified in pa_format_info_to_sample_spec2(). */
+ r = pa_stream_get_volume_channel_map(&data->volume, data->channel_map_is_set ? &data->channel_map : NULL, data->format, &volume_map);
+ if (r < 0)
+ return r;
+ }
+
+ /* Now populate the sample spec and channel map according to the final
+ * format that we've negotiated */
+ r = pa_format_info_to_sample_spec2(data->format, &data->sample_spec, &data->channel_map, &data->sink->sample_spec,
+ &data->sink->channel_map);
+ if (r < 0)
+ return r;
r = check_passthrough_connection(pa_sink_input_new_data_is_passthrough(data), data->sink);
if (r != PA_OK)
return r;
- if (!data->sample_spec_is_set)
- data->sample_spec = data->sink->sample_spec;
-
- pa_return_val_if_fail(pa_sample_spec_valid(&data->sample_spec), -PA_ERR_INVALID);
-
- if (!data->channel_map_is_set) {
- if (pa_channel_map_compatible(&data->sink->channel_map, &data->sample_spec))
- data->channel_map = data->sink->channel_map;
- else
- pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
- }
-
- pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
-
/* Don't restore (or save) stream volume for passthrough streams and
* prevent attenuation/gain */
if (pa_sink_input_new_data_is_passthrough(data)) {
if (!data->volume_writable)
data->save_volume = false;
- pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID);
+ if (data->volume_is_set)
+ /* The original volume channel map may be different than the final
+ * stream channel map, so remapping may be needed. */
+ pa_cvolume_remap(&data->volume, &volume_map, &data->channel_map);
if (!data->muted_is_set)
data->muted = false;
- if (data->flags & PA_SINK_INPUT_FIX_FORMAT) {
- pa_return_val_if_fail(pa_format_info_is_pcm(data->format), -PA_ERR_INVALID);
- data->sample_spec.format = data->sink->sample_spec.format;
- pa_format_info_set_sample_format(data->format, data->sample_spec.format);
- }
-
- if (data->flags & PA_SINK_INPUT_FIX_RATE) {
- pa_return_val_if_fail(pa_format_info_is_pcm(data->format), -PA_ERR_INVALID);
- data->sample_spec.rate = data->sink->sample_spec.rate;
- pa_format_info_set_rate(data->format, data->sample_spec.rate);
- }
-
- original_cm = data->channel_map;
-
- if (data->flags & PA_SINK_INPUT_FIX_CHANNELS) {
- pa_return_val_if_fail(pa_format_info_is_pcm(data->format), -PA_ERR_INVALID);
- data->sample_spec.channels = data->sink->sample_spec.channels;
- data->channel_map = data->sink->channel_map;
- pa_format_info_set_channels(data->format, data->sample_spec.channels);
- pa_format_info_set_channel_map(data->format, &data->channel_map);
- }
-
- pa_assert(pa_sample_spec_valid(&data->sample_spec));
- pa_assert(pa_channel_map_valid(&data->channel_map));
-
if (!(data->flags & PA_SINK_INPUT_VARIABLE_RATE) &&
!pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec)) {
/* try to change sink rate. This is done before the FIXATE hook since
module-suspend-on-idle can resume a sink */
pa_log_info("Trying to change sample rate");
- if (pa_sink_update_rate(data->sink, data->sample_spec.rate, pa_sink_input_new_data_is_passthrough(data)) == true)
+ if (pa_sink_update_rate(data->sink, data->sample_spec.rate, pa_sink_input_new_data_is_passthrough(data)) >= 0)
pa_log_info("Rate changed to %u Hz", data->sink->sample_spec.rate);
}
return -PA_ERR_NOTSUPPORTED;
}
- /* Due to the fixing of the sample spec the volume might not match anymore */
- pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map);
-
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
i->volume_factor_sink_items = data->volume_factor_sink_items;
data->volume_factor_sink_items = NULL;
- volume_factor_from_hashmap(&i->volume_factor_sink, i->volume_factor_sink_items, i->sample_spec.channels);
+ volume_factor_from_hashmap(&i->volume_factor_sink, i->volume_factor_sink_items, i->sink->sample_spec.channels);
i->real_ratio = i->reference_ratio = data->volume;
pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels);
if (PA_SINK_INPUT_IS_LINKED(i->state))
pa_sink_input_unlink(i);
- pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
+ pa_log_info("Freeing input %u \"%s\"", i->index,
+ i->proplist ? pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)) : "");
/* Side note: this function must be able to destruct properly any
* kind of sink input in any state, even those which are
pa_idxset_free(i->direct_outputs, NULL);
if (i->thread_info.direct_outputs)
- pa_hashmap_free(i->thread_info.direct_outputs, NULL);
+ pa_hashmap_free(i->thread_info.direct_outputs);
if (i->volume_factor_items)
- pa_hashmap_free(i->volume_factor_items, (pa_free_cb_t) volume_factor_entry_free);
+ pa_hashmap_free(i->volume_factor_items);
if (i->volume_factor_sink_items)
- pa_hashmap_free(i->volume_factor_sink_items, (pa_free_cb_t) volume_factor_entry_free);
+ pa_hashmap_free(i->volume_factor_sink_items);
pa_xfree(i->driver);
pa_xfree(i);
i->process_rewind(i, amount);
called = true;
- /* Convert back to to sink domain */
+ /* Convert back to sink domain */
if (i->thread_info.resampler)
amount = pa_resampler_result(i->thread_info.resampler, amount);
pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- v = pa_hashmap_remove(i->volume_factor_items, key);
-
- if (!v)
+ if (pa_hashmap_remove_and_free(i->volume_factor_items, key) < 0)
return -1;
- volume_factor_entry_free(v);
-
switch (pa_hashmap_size(i->volume_factor_items)) {
case 0:
pa_cvolume_reset(&i->volume_factor, i->sample_spec.channels);
pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- if (!i->muted == !mute) {
- i->save_muted = i->save_muted || mute;
+ if (mute == i->muted) {
+ i->save_muted |= save;
return;
}
* their volume - this function does all that by using recursion. */
static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
pa_cvolume old_volume;
+ pa_cvolume new_volume;
pa_assert(i);
pa_assert(dest);
* (sinks that use volume sharing should always have
* soft_volume of 0 dB) */
- old_volume = i->origin_sink->reference_volume;
-
- i->origin_sink->reference_volume = root_sink->reference_volume;
- pa_cvolume_remap(&i->origin_sink->reference_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
+ new_volume = root_sink->reference_volume;
+ pa_cvolume_remap(&new_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
+ pa_sink_set_reference_volume_direct(i->origin_sink, &new_volume);
i->origin_sink->real_volume = root_sink->real_volume;
pa_cvolume_remap(&i->origin_sink->real_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
pa_assert(pa_cvolume_is_norm(&i->origin_sink->soft_volume));
- /* Notify others about the changed sink volume. If you wonder whether
- * i->origin_sink->set_volume() should be called somewhere, that's not
- * the case, because sinks that use volume sharing shouldn't have any
- * internal volume that set_volume() would update. If you wonder
- * whether the thread_info variables should be synced, yes, they
- * should, and it's done by the PA_SINK_MESSAGE_FINISH_MOVE message
- * handler. */
- if (!pa_cvolume_equal(&i->origin_sink->reference_volume, &old_volume))
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, i->origin_sink->index);
+ /* If you wonder whether i->origin_sink->set_volume() should be called
+ * somewhere, that's not the case, because sinks that use volume
+ * sharing shouldn't have any internal volume that set_volume() would
+ * update. If you wonder whether the thread_info variables should be
+ * synced, yes, they should, and it's done by the
+ * PA_SINK_MESSAGE_FINISH_MOVE message handler. */
/* Recursively update origin sink inputs. */
PA_IDXSET_FOREACH(origin_sink_input, i->origin_sink->inputs, idx)
SINK_INPUT_MOVE_FINISH hook */
pa_log_info("Trying to change sample rate");
- if (pa_sink_update_rate(dest, i->sample_spec.rate, pa_sink_input_is_passthrough(i)) == true)
+ if (pa_sink_update_rate(dest, i->sample_spec.rate, pa_sink_input_is_passthrough(i)) >= 0)
pa_log_info("Rate changed to %u Hz", dest->sample_spec.rate);
}
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));
+ (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+ (i->core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0));
if (!new_resampler) {
pa_log_warn("Unsupported resampling operation.");