X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/658a9153f094db9a30ac94428f4e46985e7096eb..358d92170a4df59a3e93059f5827f2301b9d4e2a:/src/pulsecore/sink-input.c diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 6e1b81f4..7aee5466 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -26,7 +26,6 @@ #include #include -#include #include #include @@ -50,15 +49,14 @@ PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject); static void sink_input_free(pa_object *o); static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v); -static int check_passthrough_connection(pa_format_info *format, pa_sink *dest) { - +static int check_passthrough_connection(pa_bool_t passthrough, pa_sink *dest) { if (pa_sink_is_passthrough(dest)) { pa_log_warn("Sink is already connected to PASSTHROUGH input"); return -PA_ERR_BUSY; } /* If current input(s) exist, check new input is not PASSTHROUGH */ - if (pa_idxset_size(dest->inputs) > 0 && !pa_format_info_is_pcm(format)) { + if (pa_idxset_size(dest->inputs) > 0 && passthrough) { pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT"); return -PA_ERR_BUSY; } @@ -91,6 +89,18 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const data->channel_map = *map; } +pa_bool_t pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data) { + pa_assert(data); + + if (PA_LIKELY(data->format) && PA_UNLIKELY(!pa_format_info_is_pcm(data->format))) + return TRUE; + + if (PA_UNLIKELY(data->flags & PA_SINK_INPUT_PASSTHROUGH)) + return TRUE; + + return FALSE; +} + void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { pa_assert(data); pa_assert(data->volume_writable); @@ -251,7 +261,8 @@ int pa_sink_input_new( * 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); + 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); } @@ -261,9 +272,11 @@ int pa_sink_input_new( pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID); - if (!data->sink) - pa_sink_input_new_data_set_sink(data, pa_namereg_get(core, NULL, PA_NAMEREG_SINK), FALSE); - + if (!data->sink) { + pa_sink *sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK); + 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 */ @@ -284,16 +297,14 @@ int pa_sink_input_new( } else { pa_return_val_if_fail(pa_format_info_to_sample_spec_fake(data->format, &ss), -PA_ERR_INVALID); pa_sink_input_new_data_set_sample_spec(data, &ss); - /* XXX: this is redundant - we can just check the encoding */ - data->flags |= PA_SINK_INPUT_PASSTHROUGH; } - pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY); 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); - r = check_passthrough_connection(data->format, data->sink); - pa_return_val_if_fail(r == PA_OK, 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; @@ -309,6 +320,12 @@ int pa_sink_input_new( 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 */ + if (pa_sink_input_new_data_is_passthrough(data)) { + data->volume_is_set = FALSE; + data->volume_factor_is_set = FALSE; + } + if (!data->volume_is_set) { pa_cvolume_reset(&data->volume, data->sample_spec.channels); data->volume_is_absolute = FALSE; @@ -373,7 +390,7 @@ int pa_sink_input_new( !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) { /* Note: for passthrough content we need to adjust the output rate to that of the current sink-input */ - if (!(data->flags & PA_SINK_INPUT_PASSTHROUGH)) /* no resampler for passthrough content */ + if (!pa_sink_input_new_data_is_passthrough(data)) /* no resampler for passthrough content */ if (!(resampler = pa_resampler_new( core->mempool, &data->sample_spec, &data->channel_map, @@ -599,6 +616,10 @@ void pa_sink_input_unlink(pa_sink_input *i) { 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); + + /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */ + if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) + pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH); } reset_callbacks(i); @@ -689,6 +710,10 @@ void pa_sink_input_put(pa_sink_input *i) { set_real_ratio(i, &i->volume); } + /* If we're entering passthrough mode, disable the monitor */ + if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) + pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH); + i->thread_info.soft_volume = i->soft_volume; i->thread_info.muted = i->muted; @@ -744,10 +769,6 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p /* pa_log_debug("peek"); */ - pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING || - i->thread_info.state == PA_SINK_INPUT_CORKED || - i->thread_info.state == PA_SINK_INPUT_DRAINED); - 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->core->mempool), &i->sample_spec); @@ -1170,12 +1191,25 @@ static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) { /* We don't copy the data to the thread_info data. That's left for someone else to do */ } +/* Called from main or I/O context */ +pa_bool_t pa_sink_input_is_passthrough(pa_sink_input *i) { + pa_sink_input_assert_ref(i); + + if (PA_UNLIKELY(!pa_format_info_is_pcm(i->format))) + return TRUE; + + if (PA_UNLIKELY(i->flags & PA_SINK_INPUT_PASSTHROUGH)) + return TRUE; + + return FALSE; +} + /* Called from main context */ pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_ctl_context(); - return !(i->flags & PA_SINK_INPUT_PASSTHROUGH); + return !pa_sink_input_is_passthrough(i); } /* Called from main context */ @@ -1334,7 +1368,7 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { return FALSE; } - if (check_passthrough_connection(i->format, dest) < 0) + if (check_passthrough_connection(pa_sink_input_is_passthrough(i), dest) < 0) return FALSE; if (i->may_move_to) @@ -1380,6 +1414,10 @@ int pa_sink_input_start_move(pa_sink_input *i) { pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); + /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */ + if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) + pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH); + pa_sink_update_status(i->sink); pa_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map); i->sink = NULL; @@ -1552,8 +1590,14 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { if (!pa_sink_input_may_move_to(i, dest)) return -PA_ERR_NOTSUPPORTED; - if (!pa_format_info_is_pcm(i->format) && !pa_sink_check_format(dest, i->format)) { - /* FIXME: Fire a message here so the client can renegotiate */ + if (pa_sink_input_is_passthrough(i) && !pa_sink_check_format(dest, i->format)) { + pa_proplist *p = pa_proplist_new(); + pa_log_debug("New sink doesn't support stream format, sending format-changed and killing"); + /* Tell the client what device we want to be on if it is going to + * reconnect */ + pa_proplist_sets(p, "device", dest->name); + pa_sink_input_send_event(i, PA_STREAM_EVENT_FORMAT_LOST, p); + pa_proplist_free(p); return -PA_ERR_NOTSUPPORTED; } @@ -1568,7 +1612,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) || !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) { - /* Okey, we need a new resampler for the new sink */ + /* Okay, we need a new resampler for the new sink */ if (!(new_resampler = pa_resampler_new( i->core->mempool, @@ -1614,18 +1658,23 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { 1, 0, &i->sink->silence); + i->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID; } + pa_sink_update_status(dest); update_volume_due_to_moving(i, dest); pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); + /* If we're entering passthrough mode, disable the monitor */ + if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) + pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH); + pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name); /* Notify everyone */ 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); return 0; @@ -1703,8 +1752,6 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state if (i->state_change) i->state_change(i, state); - i->thread_info.state = state; - if (corking) { pa_log_debug("Requesting rewind due to corking"); @@ -1713,17 +1760,25 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state * so that the unplayed already mixed data is not lost */ pa_sink_input_request_rewind(i, 0, TRUE, TRUE, FALSE); + /* Set the corked state *after* requesting rewind */ + i->thread_info.state = state; + } else if (uncorking) { + pa_log_debug("Requesting rewind due to uncorking"); + i->thread_info.underrun_for = (uint64_t) -1; i->thread_info.playing_for = 0; - pa_log_debug("Requesting rewind due to uncorking"); + /* Set the uncorked state *before* requesting rewind */ + i->thread_info.state = 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, TRUE); - } + } else + /* We may not be corking or uncorking, but we still need to set the state. */ + i->thread_info.state = state; } /* Called from thread context, except when it is not. */