mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
fix a bad memory access pulsecore/client.c
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2527 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
7bae1baa3e
commit
b27cc1d426
14 changed files with 220 additions and 33 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ typedef struct pa_source pa_source;
|
|||
#include <pulsecore/asyncmsgq.h>
|
||||
#include <pulsecore/msgobject.h>
|
||||
#include <pulsecore/rtpoll.h>
|
||||
#include <pulsecore/source-output.h>
|
||||
|
||||
#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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue