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->asyncmsgq = NULL;
s->silence = NULL;
r = pa_idxset_put(core->sinks, s, &s->index);
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);
if (s->silence)
pa_memblock_unref(s->silence);
pa_xfree(s->name);
pa_xfree(s->description);
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_assert(info);
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
/* Increase ref counter, to make sure that this input doesn't
* vanish while we still need it */
pa_sink_input_ref(i);
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
pa_sink_input_assert_ref(i);
if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0) {
pa_sink_input_unref(i);
if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0)
continue;
}
info->userdata = i;
info->userdata = pa_sink_input_ref(i);
pa_assert(info->chunk.memblock);
pa_assert(info->chunk.length);
pa_assert(info->chunk.length > 0);
info++;
maxinfo--;
n++;
maxinfo--;
}
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_assert(info);
for (; maxinfo > 0; maxinfo--, info++) {
pa_sink_input *i = info->userdata;
/* We optimize for the case where the order of the inputs has not changed */
pa_assert(i);
pa_assert(info->chunk.memblock);
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
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 */
pa_sink_input_drop(i, &info->chunk, length);
pa_memblock_unref(info->chunk.memblock);
pa_sink_input_drop(i, m ? &m->chunk : NULL, length);
/* Decrease ref counter */
pa_sink_input_unref(i);
info->userdata = NULL;
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);
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];
unsigned n;
int r = -1;
pa_sink_assert_ref(s);
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);
if (n <= 0)
goto finish;
if (n == 0) {
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;
*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)
pa_source_post(s->monitor_source, result);
r = 0;
finish:
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];
unsigned n;
int r = -1;
pa_sink_assert_ref(s);
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);
if (n <= 0)
goto finish;
if (n == 1) {
if (n == 0) {
pa_silence_memchunk(target, &s->sample_spec);
} else if (n == 1) {
if (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)
pa_source_post(s->monitor_source, target);
r = 0;
finish:
pa_sink_unref(s);
return r;
}
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.length -= d;
if (pa_sink_render_into(s, &chunk) < 0)
break;
pa_sink_render_into(s, &chunk);
d += 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);
}

View file

@ -92,6 +92,8 @@ struct pa_sink {
int soft_muted;
} thread_info;
pa_memblock *silence;
void *userdata;
};
@ -149,9 +151,9 @@ unsigned pa_sink_used_by(pa_sink *s);
/* 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);
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);
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk);