From 8ff0197a4391a43ea932f7f4218e14d2e259c087 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Mar 2012 21:51:17 +0200 Subject: [PATCH] output/osx: use the fifo_buffer library instead of rolling own The existing buffer implementation has a major flaw: it is unable to re-fill the buffer until it has been consumed completely, leading to many occasions where the render callback needs to generate silence, just because the play() implementation was unable to append more data. The fifo_buffer library handles that well. --- NEWS | 2 + src/output/osx_plugin.c | 93 ++++++++++++++++------------------------- 2 files changed, 39 insertions(+), 56 deletions(-) diff --git a/NEWS b/NEWS index 317679bd3..ddb612c3b 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,8 @@ ver 0.16.8 (2012/??/??) * decoder: - vorbis (and others): fix seeking at startup - ffmpeg: read the "year" tag +* output: + - osx: fix stuttering due to buffering bug * fix endless loop in text file reader diff --git a/src/output/osx_plugin.c b/src/output/osx_plugin.c index 5284afc29..501dcec10 100644 --- a/src/output/osx_plugin.c +++ b/src/output/osx_plugin.c @@ -19,6 +19,7 @@ #include "config.h" #include "output_api.h" +#include "fifo_buffer.h" #include #include @@ -31,10 +32,8 @@ struct osx_output { AudioUnit au; GMutex *mutex; GCond *condition; - char *buffer; - size_t buffer_size; - size_t pos; - size_t len; + + struct fifo_buffer *buffer; }; /** @@ -64,11 +63,6 @@ osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, oo->mutex = g_mutex_new(); oo->condition = g_cond_new(); - oo->pos = 0; - oo->len = 0; - oo->buffer = NULL; - oo->buffer_size = 0; - return oo; } @@ -76,7 +70,6 @@ static void osx_output_finish(void *data) { struct osx_output *od = data; - g_free(od->buffer); g_mutex_free(od->mutex); g_cond_free(od->condition); g_free(od); @@ -87,7 +80,7 @@ static void osx_output_cancel(void *data) struct osx_output *od = data; g_mutex_lock(od->mutex); - od->len = 0; + fifo_buffer_clear(od->buffer); g_mutex_unlock(od->mutex); } @@ -98,6 +91,8 @@ static void osx_output_close(void *data) AudioOutputUnitStop(od->au); AudioUnitUninitialize(od->au); CloseComponent(od->au); + + fifo_buffer_free(od->buffer); } static OSStatus @@ -111,37 +106,29 @@ osx_render(void *vdata, struct osx_output *od = (struct osx_output *) vdata; AudioBuffer *buffer = &buffer_list->mBuffers[0]; size_t buffer_size = buffer->mDataByteSize; - size_t bytes_to_copy; - size_t trailer_length; - size_t dest_pos = 0; + + assert(od->buffer != NULL); g_mutex_lock(od->mutex); - bytes_to_copy = MIN(od->len, buffer_size); - od->len -= bytes_to_copy; + size_t nbytes; + const void *src = fifo_buffer_read(od->buffer, &nbytes); - trailer_length = od->buffer_size - od->pos; - if (bytes_to_copy > trailer_length) { - memcpy((unsigned char*)buffer->mData + dest_pos, - od->buffer + od->pos, trailer_length); - od->pos = 0; - dest_pos += trailer_length; - bytes_to_copy -= trailer_length; - } + if (src != NULL) { + if (nbytes > buffer_size) + nbytes = buffer_size; - memcpy((unsigned char*)buffer->mData + dest_pos, - od->buffer + od->pos, bytes_to_copy); - od->pos += bytes_to_copy; - - if (od->pos >= od->buffer_size) - od->pos = 0; + memcpy(buffer->mData, src, nbytes); + fifo_buffer_consume(od->buffer, nbytes); + } else + nbytes = 0; g_cond_signal(od->condition); g_mutex_unlock(od->mutex); - if (bytes_to_copy < buffer_size) - memset((unsigned char*)buffer->mData + bytes_to_copy, 0, - buffer_size - bytes_to_copy); + if (nbytes < buffer_size) + memset((unsigned char*)buffer->mData + nbytes, 0, + buffer_size - nbytes); return 0; } @@ -244,15 +231,12 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error) } /* create a buffer of 1s */ - od->buffer_size = (audio_format->sample_rate) * - audio_format_frame_size(audio_format); - od->buffer = g_realloc(od->buffer, od->buffer_size); - - od->pos = 0; - od->len = 0; + od->buffer = fifo_buffer_new(audio_format->sample_rate * + audio_format_frame_size(audio_format)); status = AudioOutputUnitStart(od->au); if (status != 0) { + fifo_buffer_free(od->buffer); g_set_error(error, osx_output_quark(), 0, "unable to start audio output: %s", GetMacOSStatusCommentString(status)); @@ -267,33 +251,30 @@ osx_output_play(void *data, const void *chunk, size_t size, G_GNUC_UNUSED GError **error) { struct osx_output *od = data; - size_t start, nbytes; g_mutex_lock(od->mutex); - while (od->len >= od->buffer_size) + void *dest; + size_t max_length; + + while (true) { + dest = fifo_buffer_write(od->buffer, &max_length); + if (dest != NULL) + break; + /* wait for some free space in the buffer */ g_cond_wait(od->condition, od->mutex); + } - start = od->pos + od->len; - if (start >= od->buffer_size) - start -= od->buffer_size; + if (size > max_length) + size = max_length; - nbytes = start < od->pos - ? od->pos - start - : od->buffer_size - start; - - assert(nbytes > 0); - - if (nbytes > size) - nbytes = size; - - memcpy(od->buffer + start, chunk, nbytes); - od->len += nbytes; + memcpy(dest, chunk, size); + fifo_buffer_append(od->buffer, size); g_mutex_unlock(od->mutex); - return nbytes; + return size; } const struct audio_output_plugin osxPlugin = {