remove underrun condition in pa_sinks. Instead return silence in pa_sink_render() when necessary. This is required to guarantee that the time functions in connected sink inputs stays linear

git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/lennart@1490 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Lennart Poettering 2007-06-23 20:04:47 +00:00
parent 780f736547
commit 013a55a692
2 changed files with 81 additions and 55 deletions

View file

@ -116,6 +116,7 @@ pa_sink* pa_sink_new(
s->userdata = NULL; s->userdata = NULL;
s->asyncmsgq = NULL; s->asyncmsgq = NULL;
s->silence = NULL;
r = pa_idxset_put(core->sinks, s, &s->index); r = pa_idxset_put(core->sinks, s, &s->index);
pa_assert(s->index != PA_IDXSET_INVALID && r >= 0); pa_assert(s->index != PA_IDXSET_INVALID && r >= 0);
@ -222,6 +223,9 @@ static void sink_free(pa_object *o) {
pa_hashmap_free(s->thread_info.inputs, NULL, NULL); pa_hashmap_free(s->thread_info.inputs, NULL, NULL);
if (s->silence)
pa_memblock_unref(s->silence);
pa_xfree(s->name); pa_xfree(s->name);
pa_xfree(s->description); pa_xfree(s->description);
pa_xfree(s->driver); pa_xfree(s->driver);
@ -270,53 +274,85 @@ static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) {
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(info); pa_assert(info);
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
/* Increase ref counter, to make sure that this input doesn't pa_sink_input_assert_ref(i);
* vanish while we still need it */
pa_sink_input_ref(i);
if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0) { if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0)
pa_sink_input_unref(i);
continue; continue;
}
info->userdata = i; info->userdata = pa_sink_input_ref(i);
pa_assert(info->chunk.memblock); pa_assert(info->chunk.memblock);
pa_assert(info->chunk.length); pa_assert(info->chunk.length > 0);
info++; info++;
maxinfo--;
n++; n++;
maxinfo--;
} }
return n; return n;
} }
static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) { static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length) {
pa_sink_input *i;
void *state = NULL;
unsigned p = 0;
unsigned n_unreffed = 0;
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(info);
for (; maxinfo > 0; maxinfo--, info++) { /* We optimize for the case where the order of the inputs has not changed */
pa_sink_input *i = info->userdata;
pa_assert(i); while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
pa_assert(info->chunk.memblock); unsigned j;
pa_mix_info* m;
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 ++) {
if (info[p].userdata == i) {
m = info + p;
break;
}
if (++p > n)
p = 0;
}
/* Drop read data */ /* Drop read data */
pa_sink_input_drop(i, &info->chunk, length); pa_sink_input_drop(i, m ? &m->chunk : NULL, length);
pa_memblock_unref(info->chunk.memblock);
/* Decrease ref counter */ if (m) {
pa_sink_input_unref(i); pa_sink_input_unref(m->userdata);
info->userdata = NULL; m->userdata = NULL;
if (m->chunk.memblock)
pa_memblock_unref(m->chunk.memblock);
pa_memchunk_reset(&m->chunk);
n_unreffed += 1;
}
}
/* Now drop references to entries that are included in the
* pa_mix_info array but don't exist anymore */
if (n_unreffed < n) {
for (; n > 0; info++, n--) {
if (info->userdata)
pa_sink_input_unref(info->userdata);
if (info->chunk.memblock)
pa_memblock_unref(info->chunk.memblock);
}
} }
} }
int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_mix_info info[MAX_MIX_CHANNELS]; pa_mix_info info[MAX_MIX_CHANNELS];
unsigned n; unsigned n;
int r = -1;
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(length); pa_assert(length);
@ -326,10 +362,19 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
n = fill_mix_info(s, info, MAX_MIX_CHANNELS); n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
if (n <= 0) if (n == 0) {
goto finish;
if (n == 1) { if (!s->silence || pa_memblock_get_length(s->silence) < length) {
if (s->silence)
pa_memblock_unref(s->silence);
s->silence = pa_silence_memblock_new(s->core->mempool, &s->sample_spec, length);
}
result->memblock = pa_memblock_ref(s->silence);
result->length = length;
result->index = 0;
} else if (n == 1) {
pa_cvolume volume; pa_cvolume volume;
*result = info[0].chunk; *result = info[0].chunk;
@ -363,18 +408,12 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
if (s->monitor_source) if (s->monitor_source)
pa_source_post(s->monitor_source, result); pa_source_post(s->monitor_source, result);
r = 0;
finish:
pa_sink_unref(s); pa_sink_unref(s);
return r;
} }
int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
pa_mix_info info[MAX_MIX_CHANNELS]; pa_mix_info info[MAX_MIX_CHANNELS];
unsigned n; unsigned n;
int r = -1;
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(target); pa_assert(target);
@ -385,11 +424,9 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
n = fill_mix_info(s, info, MAX_MIX_CHANNELS); n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
if (n <= 0) if (n == 0) {
goto finish; pa_silence_memchunk(target, &s->sample_spec);
} else if (n == 1) {
if (n == 1) {
if (target->length > info[0].chunk.length) if (target->length > info[0].chunk.length)
target->length = info[0].chunk.length; target->length = info[0].chunk.length;
@ -435,12 +472,7 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
if (s->monitor_source) if (s->monitor_source)
pa_source_post(s->monitor_source, target); pa_source_post(s->monitor_source, target);
r = 0;
finish:
pa_sink_unref(s); pa_sink_unref(s);
return r;
} }
void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
@ -461,20 +493,12 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
chunk.index += d; chunk.index += d;
chunk.length -= d; chunk.length -= d;
if (pa_sink_render_into(s, &chunk) < 0) pa_sink_render_into(s, &chunk);
break;
d += chunk.length; d += chunk.length;
l -= chunk.length; l -= chunk.length;
} }
if (l > 0) {
chunk = *target;
chunk.index += d;
chunk.length -= d;
pa_silence_memchunk(&chunk, &s->sample_spec);
}
pa_sink_unref(s); pa_sink_unref(s);
} }

View file

@ -92,6 +92,8 @@ struct pa_sink {
int soft_muted; int soft_muted;
} thread_info; } thread_info;
pa_memblock *silence;
void *userdata; void *userdata;
}; };
@ -149,9 +151,9 @@ unsigned pa_sink_used_by(pa_sink *s);
/* To be used exclusively by the sink driver thread */ /* To be used exclusively by the sink driver thread */
int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
int pa_sink_render_into(pa_sink*s, pa_memchunk *target); void pa_sink_render_into(pa_sink*s, pa_memchunk *target);
void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target);
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk); int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk);