From b27cc1d426b7fda9cc21a0854a9cd6f494e2f7fa Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 13 Jun 2008 21:56:19 +0000 Subject: fix a bad memory access pulsecore/client.c git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2527 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/map-file | 2 ++ src/pulse/internal.h | 2 ++ src/pulse/stream.c | 29 +++++++++++++++++ src/pulse/stream.h | 16 ++++++++-- src/pulsecore/cli-text.c | 2 ++ src/pulsecore/client.c | 2 +- src/pulsecore/protocol-native.c | 25 ++++++++++++--- src/pulsecore/sink-input.c | 24 ++++++++++++++ src/pulsecore/sink-input.h | 7 +++++ src/pulsecore/sink.c | 69 ++++++++++++++++++++++++++++++----------- src/pulsecore/source-output.c | 19 ++++++++++-- src/pulsecore/source-output.h | 6 ++++ src/pulsecore/source.c | 46 +++++++++++++++++++++++++-- src/pulsecore/source.h | 4 ++- 14 files changed, 220 insertions(+), 33 deletions(-) diff --git a/src/map-file b/src/map-file index d9189743..c3fb0882 100644 --- a/src/map-file +++ b/src/map-file @@ -180,6 +180,7 @@ pa_stream_get_device_index; pa_stream_get_device_name; pa_stream_get_index; pa_stream_get_latency; +pa_stream_get_monitor_stream; pa_stream_get_sample_spec; pa_stream_get_state; pa_stream_get_time; @@ -197,6 +198,7 @@ pa_stream_ref; pa_stream_set_buffer_attr; pa_stream_set_latency_update_callback; pa_stream_set_moved_callback; +pa_stream_set_monitor_stream; pa_stream_set_name; pa_stream_set_overflow_callback; pa_stream_set_read_callback; diff --git a/src/pulse/internal.h b/src/pulse/internal.h index d346e945..9b288562 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -106,6 +106,8 @@ struct pa_stream { pa_context *context; pa_mainloop_api *mainloop; + uint32_t direct_on_input; + pa_stream_direction_t direction; pa_stream_state_t state; pa_stream_flags_t flags; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 7d49cfd5..3bb359ac 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -107,6 +107,8 @@ pa_stream *pa_stream_new_with_proplist( s->sample_spec = *ss; s->channel_map = *map; + s->direct_on_input = PA_INVALID_INDEX; + s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new(); if (name) pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name); @@ -838,6 +840,7 @@ static int create_stream( pa_assert(direction == PA_STREAM_PLAYBACK || direction == PA_STREAM_RECORD); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direct_on_input == PA_INVALID_INDEX || direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED| PA_STREAM_INTERPOLATE_TIMING| PA_STREAM_NOT_MONOTONOUS| @@ -954,6 +957,9 @@ static int create_stream( PA_TAG_BOOLEAN, flags & PA_STREAM_ADJUST_LATENCY, PA_TAG_PROPLIST, s->proplist, PA_TAG_INVALID); + + if (s->direction == PA_STREAM_RECORD) + pa_tagstruct_putu32(t, s->direct_on_input); } pa_pstream_send_tagstruct(s->context->pstream, t); @@ -2227,3 +2233,26 @@ pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], return o; } + +int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED); + + s->direct_on_input = sink_input_idx; + + return 0; +} + +uint32_t pa_stream_get_monitor_stream(pa_stream *s) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direct_on_input != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX); + + return s->direct_on_input; +} diff --git a/src/pulse/stream.h b/src/pulse/stream.h index ebb45f2b..856b85ae 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -527,14 +527,14 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s); * server is at least PulseAudio 0.9.8. \since 0.9.8 */ pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata); -/* Change the stream sampling rate during playback. You need to pass +/** Change the stream sampling rate during playback. You need to pass * PA_STREAM_VARIABLE_RATE in the flags parameter of * pa_stream_connect() if you plan to use this function. Only valid * after the stream has been connected successfully and if the server * is at least PulseAudio 0.9.8. \since 0.9.8 */ pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata); -/* Update the property list of the sink input/source output of this +/** Update the property list of the sink input/source output of this * stream, adding new entries. Please note that it is highly * recommended to set as much properties initially via * pa_stream_new_with_proplist() as possible instead a posteriori with @@ -542,10 +542,20 @@ pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_strea * this stream to the right device. \since 0.9.11 */ pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata); -/* Update the property list of the sink input/source output of this +/** Update the property list of the sink input/source output of this * stream, remove entries. \since 0.9.11 */ pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata); +/** For record streams connected to a monitor source: monitor only a + * very specific sink input of the sink. Thus function needs to be + * called before pa_stream_connect_record() is called. \since + * 0.9.11 */ +int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx); + +/** Return what has been set with pa_stream_set_monitor_stream() + * ebfore. \since 0.9.11 */ +uint32_t pa_stream_get_monitor_stream(pa_stream *s); + PA_C_DECL_END #endif diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 029a7089..e81390c8 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -296,6 +296,8 @@ char *pa_source_output_list_to_string(pa_core *c) { pa_strbuf_printf(s, "\towner module: %u\n", o->module->index); if (o->client) pa_strbuf_printf(s, "\tclient: %u <%s>\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME))); + if (o->direct_on_input) + pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index); t = pa_proplist_to_string(o->proplist); pa_strbuf_printf(s, "\tproperties:\n%s", t); diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c index fb4f8c87..7788e5d2 100644 --- a/src/pulsecore/client.c +++ b/src/pulsecore/client.c @@ -80,7 +80,7 @@ void pa_client_free(pa_client *c) { pa_xfree(c->driver); pa_xfree(c); - pa_core_check_quit(c->core); + pa_core_check_quit(core); } void pa_client_kill(pa_client *c) { diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index fc1c8b1b..a05c0f7e 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -499,7 +499,8 @@ static void fix_record_buffer_attr_pre(record_stream *s, pa_bool_t adjust_latenc fragsize_usec = s->source_latency; *fragsize = pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); - } + } else + s->source_latency = 0; } static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, uint32_t *fragsize) { @@ -533,7 +534,8 @@ static record_stream* record_stream_new( uint32_t *fragsize, pa_source_output_flags_t flags, pa_proplist *p, - pa_bool_t adjust_latency) { + pa_bool_t adjust_latency, + pa_sink_input *direct_on_input) { record_stream *s; pa_source_output *source_output; @@ -553,6 +555,7 @@ static record_stream* record_stream_new( data.module = c->protocol->module; data.client = c->client; data.source = source; + data.direct_on_input = direct_on_input; pa_source_output_new_data_set_sample_spec(&data, ss); pa_source_output_new_data_set_channel_map(&data, map); if (peak_detect) @@ -1757,7 +1760,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ record_stream *s; uint32_t maxlength, fragment_size; uint32_t source_index; - const char *name, *source_name; + const char *name = NULL, *source_name; pa_sample_spec ss; pa_channel_map map; pa_tagstruct *reply; @@ -1775,6 +1778,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ peak_detect = FALSE; pa_source_output_flags_t flags = 0; pa_proplist *p; + uint32_t direct_on_input_idx = PA_INVALID_INDEX; + pa_sink_input *direct_on_input = NULL; connection_assert_ref(c); pa_assert(t); @@ -1823,7 +1828,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 || pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || - pa_tagstruct_get_proplist(t, p) < 0) { + pa_tagstruct_get_proplist(t, p) < 0 || + pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) { protocol_error(c); pa_proplist_free(p); return; @@ -1853,6 +1859,15 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ } } + if (direct_on_input_idx != PA_INVALID_INDEX) { + + if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + pa_proplist_free(p); + return; + } + } + flags = (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) | (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) | @@ -1863,7 +1878,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0); - s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency); + s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index d51ff810..4defb0f6 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -220,6 +220,8 @@ pa_sink_input* pa_sink_input_new( i->sink = data->sink; i->client = data->client; + i->direct_outputs = pa_idxset_new(NULL, NULL); + i->resample_method = data->resample_method; i->sample_spec = data->sample_spec; i->channel_map = data->channel_map; @@ -252,6 +254,7 @@ pa_sink_input* pa_sink_input_new( i->thread_info.rewrite_flush = 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); i->thread_info.render_memblockq = pa_memblockq_new( 0, @@ -322,6 +325,7 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) { void pa_sink_input_unlink(pa_sink_input *i) { pa_bool_t linked; + pa_source_output *o, *p = NULL; pa_assert(i); /* See pa_sink_unlink() for a couple of comments how this function @@ -345,6 +349,12 @@ void pa_sink_input_unlink(pa_sink_input *i) { if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL)) pa_sink_input_unref(i); + while ((o = pa_idxset_first(i->direct_outputs, NULL))) { + pa_assert(o != p); + pa_source_output_kill(o); + p = o; + } + update_n_corked(i, PA_SINK_INPUT_UNLINKED); i->state = PA_SINK_INPUT_UNLINKED; @@ -385,6 +395,12 @@ static void sink_input_free(pa_object *o) { if (i->proplist) pa_proplist_free(i->proplist); + if (i->direct_outputs) + pa_idxset_free(i->direct_outputs, NULL, NULL); + + if (i->thread_info.direct_outputs) + pa_hashmap_free(i->thread_info.direct_outputs, NULL, NULL); + pa_xfree(i->driver); pa_xfree(i); } @@ -839,6 +855,7 @@ 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; + pa_source_output *o, *p = NULL; pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); @@ -862,6 +879,13 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { return -1; } + /* Kill directly connected outputs */ + while ((o = pa_idxset_first(i->direct_outputs, NULL))) { + pa_assert(o != p); + pa_source_output_kill(o); + p = o; + } + 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)) diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 5f146122..1716863a 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -80,6 +80,11 @@ struct pa_sink_input { pa_sink *sink; + /* A sink input may be connected to multiple source outputs + * directly, so that they don't get mixed data of the entire + * source. */ + pa_idxset *direct_outputs; + pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -166,6 +171,8 @@ struct pa_sink_input { /* The requested latency for the sink */ pa_usec_t requested_sink_latency; + + pa_hashmap *direct_outputs; } thread_info; void *userdata; diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 27b7b0f8..68b41f26 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -498,24 +498,25 @@ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, uns return n; } -static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length) { +static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) { pa_sink_input *i; void *state = NULL; unsigned p = 0; unsigned n_unreffed = 0; pa_sink_assert_ref(s); + pa_assert(result); + pa_assert(result->memblock); + pa_assert(result->length > 0); /* We optimize for the case where the order of the inputs has not changed */ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { unsigned j; - pa_mix_info* m; + pa_mix_info* m = NULL; pa_sink_input_assert_ref(i); - m = NULL; - /* Let's try to find the matching entry info the pa_mix_info array */ for (j = 0; j < n; j ++) { @@ -530,14 +531,47 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length } /* Drop read data */ - pa_sink_input_drop(i, length); + pa_sink_input_drop(i, result->length); + + if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) { + + if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) { + void *ostate = NULL; + pa_source_output *o; + pa_memchunk c; + + if (m && m->chunk.memblock) { + c = m->chunk; + pa_memblock_ref(c.memblock); + pa_assert(result->length <= c.length); + c.length = result->length; + + pa_memchunk_make_writable(&c, 0); + pa_volume_memchunk(&c, &s->sample_spec, &m->volume); + } else { + c = s->silence; + pa_memblock_ref(c.memblock); + pa_assert(result->length <= c.length); + c.length = result->length; + } + + while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) { + pa_source_output_assert_ref(o); + pa_assert(o->direct_on_input == i); + pa_source_post_direct(s->monitor_source, o, &c); + } + + pa_memblock_unref(c.memblock); + } + } if (m) { - pa_sink_input_unref(m->userdata); - m->userdata = NULL; if (m->chunk.memblock) pa_memblock_unref(m->chunk.memblock); - pa_memchunk_reset(&m->chunk); + pa_memchunk_reset(&m->chunk); + + pa_sink_input_unref(m->userdata); + m->userdata = NULL; n_unreffed += 1; } @@ -554,6 +588,9 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length pa_memblock_unref(info->chunk.memblock); } } + + if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) + pa_source_post(s->monitor_source, result); } void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { @@ -624,10 +661,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { } if (s->thread_info.state == PA_SINK_RUNNING) - inputs_drop(s, info, n, result->length); - - if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) - pa_source_post(s->monitor_source, result); + inputs_drop(s, info, n, result); pa_sink_unref(s); } @@ -653,6 +687,8 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { if (length > block_size_max) length = pa_frame_align(block_size_max, &s->sample_spec); + pa_assert(length > 0); + n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0; if (n == 0) { @@ -676,8 +712,8 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { vchunk = info[0].chunk; pa_memblock_ref(vchunk.memblock); - if (vchunk.length > target->length) - vchunk.length = target->length; + if (vchunk.length > length) + vchunk.length = length; if (!pa_cvolume_is_norm(&volume)) { pa_memchunk_make_writable(&vchunk, 0); @@ -703,10 +739,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { } if (s->thread_info.state == PA_SINK_RUNNING) - inputs_drop(s, info, n, target->length); - - if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) - pa_source_post(s->monitor_source, target); + inputs_drop(s, info, n, target); pa_sink_unref(s); } diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 2ed4bb56..8f7aa49b 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -114,6 +114,8 @@ pa_source_output* pa_source_output_new( pa_return_null_if_fail(data->source); pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED); + pa_return_null_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of); + if (!data->sample_spec_is_set) data->sample_spec = data->source->sample_spec; @@ -192,6 +194,8 @@ pa_source_output* pa_source_output_new( o->sample_spec = data->sample_spec; o->channel_map = data->channel_map; + o->direct_on_input = data->direct_on_input; + reset_callbacks(o); o->userdata = NULL; @@ -200,6 +204,7 @@ pa_source_output* pa_source_output_new( o->thread_info.sample_spec = o->sample_spec; o->thread_info.resampler = resampler; o->thread_info.requested_source_latency = (pa_usec_t) -1; + o->thread_info.direct_on_input = o->direct_on_input; o->thread_info.delay_memblockq = pa_memblockq_new( 0, @@ -214,6 +219,9 @@ pa_source_output* pa_source_output_new( pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0); pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0); + if (o->direct_on_input) + pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0); + pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)), @@ -269,6 +277,8 @@ void pa_source_output_unlink(pa_source_output*o) { if (linked) pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o); + if (o->direct_on_input) + pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL); pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL); if (pa_idxset_remove_by_data(o->source->outputs, o, NULL)) pa_source_output_unref(o); @@ -368,10 +378,10 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { pa_assert(chunk); pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec)); - if (!o->push || o->state == PA_SOURCE_OUTPUT_CORKED) + if (!o->push || o->thread_info.state == PA_SOURCE_OUTPUT_CORKED) return; - pa_assert(o->state == PA_SOURCE_OUTPUT_RUNNING); + pa_assert(o->thread_info.state == PA_SOURCE_OUTPUT_RUNNING); if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) { pa_log_debug("Delay queue overflow!"); @@ -422,7 +432,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); if (nbytes <= 0) @@ -583,6 +593,9 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) return -1; + if (o->direct_on_input) + return -1; + if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { pa_log_warn("Failed to move source output: too many outputs per source."); return -1; diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 2dadb5c4..ae98b0e3 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -73,6 +73,9 @@ struct pa_source_output { pa_source *source; + /* A source output can monitor just a single input of a sink, in which case we find it here */ + pa_sink_input *direct_on_input; /* may be NULL */ + pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -135,6 +138,8 @@ struct pa_source_output { /* The requested latency for the source */ pa_usec_t requested_source_latency; + + pa_sink_input *direct_on_input; /* may be NULL */ } thread_info; void *userdata; @@ -154,6 +159,7 @@ enum { typedef struct pa_source_output_new_data { pa_proplist *proplist; + pa_sink_input *direct_on_input; const char *driver; pa_module *module; diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 3028b5b9..19999e70 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -417,7 +417,9 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) { pa_source_output_assert_ref(o); - pa_source_output_push(o, &vchunk); + + if (!o->thread_info.direct_on_input) + pa_source_output_push(o, &vchunk); } pa_memblock_unref(vchunk.memblock); @@ -425,11 +427,41 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) { pa_source_output_assert_ref(o); - pa_source_output_push(o, chunk); + + if (!o->thread_info.direct_on_input) + pa_source_output_push(o, chunk); } } } +void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) { + pa_source_assert_ref(s); + pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state)); + pa_source_output_assert_ref(o); + pa_assert(o->thread_info.direct_on_input); + pa_assert(chunk); + + if (s->thread_info.state != PA_SOURCE_RUNNING) + return; + + if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) { + pa_memchunk vchunk = *chunk; + + pa_memblock_ref(vchunk.memblock); + pa_memchunk_make_writable(&vchunk, 0); + + if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume)) + pa_silence_memchunk(&vchunk, &s->sample_spec); + else + pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume); + + pa_source_output_push(o, &vchunk); + + pa_memblock_unref(vchunk.memblock); + } else + pa_source_output_push(o, chunk); +} + pa_usec_t pa_source_get_latency(pa_source *s) { pa_usec_t usec; @@ -577,6 +609,11 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o)); + if (o->direct_on_input) { + o->thread_info.direct_on_input = o->direct_on_input; + pa_hashmap_put(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index), o); + } + pa_assert(!o->thread_info.attached); o->thread_info.attached = TRUE; @@ -606,6 +643,11 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ pa_assert(o->thread_info.attached); o->thread_info.attached = FALSE; + if (o->thread_info.direct_on_input) { + pa_hashmap_remove(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index)); + o->thread_info.direct_on_input = NULL; + } + if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index))) pa_source_output_unref(o); diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index d12659d7..8509057f 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -42,6 +42,7 @@ typedef struct pa_source pa_source; #include #include #include +#include #define PA_MAX_OUTPUTS_PER_SOURCE 32 @@ -205,7 +206,8 @@ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that ar /* To be called exclusively by the source driver, from IO context */ -void pa_source_post(pa_source*s, const pa_memchunk *b); +void pa_source_post(pa_source*s, const pa_memchunk *chunk); +void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk); void pa_source_process_rewind(pa_source *s, size_t nbytes); int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa_memchunk *chunk); -- cgit