From 2ce63f32adf9c47d6ed9604f3cd090229409df05 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Thu, 5 Nov 2009 22:42:52 +0100 Subject: [PATCH] wng: Avoid buffer underruns due to filter chain output buffer constraints. Using ogg vorbis streams together with the oss writer hits the following nasty bug: In case the filter chain can provide more data than what fits into its output buffer, it converts the maximal amount possible to completely fill its output buffer. However, the time to play this data might be less than than the time until the next data packet arrives from the upper layers, especially when using ogg vorbis streams and FEC. Since the filter chain task has no pre_select function, the convert function(s) of its filter nodes only get the chance to convert more data until the next select call returns, which might already be too late. This patch fixes the bug by teaching the pre_select function of the writer node group to remember whether something was written during the previous call to wng_post_select(). In this case we force a minimal timeout for select, i.e. the next call to select() will return immediately and the convert functions of the filter node are called again, hopefully converting more data for the writer node group. --- write.h | 2 ++ write_common.c | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/write.h b/write.h index 1f316fc0..8816be73 100644 --- a/write.h +++ b/write.h @@ -107,6 +107,8 @@ struct writer_node_group { struct task task; /** Whether the group is open, i.e. wng_open() was called. */ int open; + /** Max number of bytes written in the previous post_select() call. */ + int last_written; }; /** Loop over each writer node in a writer group. */ diff --git a/write_common.c b/write_common.c index 476df8f0..2dca309c 100644 --- a/write_common.c +++ b/write_common.c @@ -20,7 +20,7 @@ const char *writer_names[] ={WRITER_NAMES}; /** the array of supported writers */ struct writer writers[NUM_SUPPORTED_WRITERS] = {WRITER_ARRAY}; -static void wng_pre_select(__a_unused struct sched *s, struct task *t) +static void wng_pre_select(struct sched *s, struct task *t) { struct writer_node_group *g = container_of(t, struct writer_node_group, task); int i; @@ -34,13 +34,25 @@ static void wng_pre_select(__a_unused struct sched *s, struct task *t) if (t->error < 0) return; } + /* + * Force a minimal delay if something was written during the previous + * call to wng_post_select(). This is necessary because the filter + * chain might still have data for us which it couldn't convert during + * the previous run due to its buffer size constraints. In this case we + * do not want to wait until the next input data arrives as this could + * lead to buffer underruns. + */ + if (g->last_written == 0) + return; + s->timeout.tv_sec = 0; + s->timeout.tv_usec = 1; } static void wng_post_select(struct sched *s, struct task *t) { struct writer_node_group *g = container_of(t, struct writer_node_group, task); int i; - size_t min_written = 0; + size_t min_written = 0, max_written = 0; FOR_EACH_WRITER_NODE(i, g) { struct writer_node *wn = &g->writer_nodes[i]; @@ -52,7 +64,9 @@ static void wng_post_select(struct sched *s, struct task *t) min_written = wn->written; else min_written = PARA_MIN(min_written, wn->written); + max_written = PARA_MAX(max_written, wn->written); } + g->last_written = max_written; //PARA_INFO_LOG("loaded: %zd, min_written: %zd bytes\n", *g->loaded, min_written); if (min_written) { *g->loaded -= min_written; -- 2.39.2