From 4f500149af0d4e50938b5c93b6c16a5de4e43685 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Mar 2012 19:51:19 +0100 Subject: text_input_stream: detect end-of-file Fixes endless loop when the last line of a text file was not terminated (bug 3470). --- NEWS | 1 + 1 file changed, 1 insertion(+) (limited to 'NEWS') diff --git a/NEWS b/NEWS index aa6ef951..317679bd 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ ver 0.16.8 (2012/??/??) * decoder: - vorbis (and others): fix seeking at startup - ffmpeg: read the "year" tag +* fix endless loop in text file reader ver 0.16.7 (2012/02/04) -- cgit v1.2.3 From 8ff0197a4391a43ea932f7f4218e14d2e259c087 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 28 Mar 2012 21:51:17 +0200 Subject: 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(-) (limited to 'NEWS') diff --git a/NEWS b/NEWS index 317679bd..ddb612c3 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 5284afc2..501dcec1 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; - g_mutex_lock(od->mutex); + assert(od->buffer != NULL); - bytes_to_copy = MIN(od->len, buffer_size); - od->len -= bytes_to_copy; + g_mutex_lock(od->mutex); - 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; - } + size_t nbytes; + const void *src = fifo_buffer_read(od->buffer, &nbytes); - memcpy((unsigned char*)buffer->mData + dest_pos, - od->buffer + od->pos, bytes_to_copy); - od->pos += bytes_to_copy; + if (src != NULL) { + if (nbytes > buffer_size) + nbytes = buffer_size; - 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) - /* 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; + void *dest; + size_t max_length; - nbytes = start < od->pos - ? od->pos - start - : od->buffer_size - start; + while (true) { + dest = fifo_buffer_write(od->buffer, &max_length); + if (dest != NULL) + break; - assert(nbytes > 0); + /* wait for some free space in the buffer */ + g_cond_wait(od->condition, od->mutex); + } - if (nbytes > size) - nbytes = size; + if (size > max_length) + size = max_length; - 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 = { -- cgit v1.2.3 From 83174de420eb2a63d77a7953081b3fb360ecbc31 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 4 Apr 2012 08:56:45 +0200 Subject: update: properly skip symlinks in path that is to be updated. --- NEWS | 1 + src/update_walk.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'NEWS') diff --git a/NEWS b/NEWS index ddb612c3..a8702203 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ ver 0.16.8 (2012/??/??) * output: - osx: fix stuttering due to buffering bug * fix endless loop in text file reader +* update: skip symlinks in path that is to be updated ver 0.16.7 (2012/02/04) diff --git a/src/update_walk.c b/src/update_walk.c index 03fa7845..5d2f778f 100644 --- a/src/update_walk.c +++ b/src/update_walk.c @@ -848,6 +848,9 @@ directory_make_child_checked(struct directory *parent, const char *path) return NULL; } + if (skip_symlink(parent, path)) + return NULL; + /* if we're adding directory paths, make sure to delete filenames with potentially the same name */ conflicting = songvec_find(&parent->songs, base); @@ -896,7 +899,8 @@ updatePath(const char *path) name = g_path_get_basename(path); - if (stat_directory_child(parent, name, &st) == 0) + if (!skip_symlink(parent, name) && + stat_directory_child(parent, name, &st) == 0) updateInDirectory(parent, name, &st); else delete_name_in(parent, name); -- cgit v1.2.3 From e7a18625177e2831d96baf2f7fc16f23bc99975d Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 4 Apr 2012 21:38:29 +0200 Subject: output/jack: workaround for libjack1 crash bug --- NEWS | 1 + src/output/jack_output_plugin.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) (limited to 'NEWS') diff --git a/NEWS b/NEWS index a8702203..a7bd652b 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ ver 0.16.8 (2012/??/??) - vorbis (and others): fix seeking at startup - ffmpeg: read the "year" tag * output: + - jack: workaround for libjack1 crash bug - osx: fix stuttering due to buffering bug * fix endless loop in text file reader * update: skip symlinks in path that is to be updated diff --git a/src/output/jack_output_plugin.c b/src/output/jack_output_plugin.c index 2767d4eb..bc13c2f8 100644 --- a/src/output/jack_output_plugin.c +++ b/src/output/jack_output_plugin.c @@ -143,6 +143,13 @@ mpd_jack_process(jack_nframes_t nframes, void *arg) for (unsigned i = 0; i < jd->audio_format.channels; ++i) { out = jack_port_get_buffer(jd->ports[i], nframes); + if (out == NULL) + /* workaround for libjack1 bug: if the server + connection fails, the process callback is + invoked anyway, but unable to get a + buffer */ + continue; + jack_ringbuffer_read(jd->ringbuffer[i], (char *)out, available * jack_sample_size); @@ -156,6 +163,12 @@ mpd_jack_process(jack_nframes_t nframes, void *arg) for (unsigned i = jd->audio_format.channels; i < jd->num_source_ports; ++i) { out = jack_port_get_buffer(jd->ports[i], nframes); + if (out == NULL) + /* workaround for libjack1 bug: if the server + connection fails, the process callback is + invoked anyway, but unable to get a + buffer */ + continue; for (jack_nframes_t f = 0; f < nframes; ++f) out[f] = 0.0; -- cgit v1.2.3 From a9edf85a692e6afbe6c5efc9f2784e2c5d191eab Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 4 Apr 2012 21:40:56 +0200 Subject: output/jack: check for connection failure before starting playback --- NEWS | 1 + src/output/jack_output_plugin.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'NEWS') diff --git a/NEWS b/NEWS index a7bd652b..8eb3ed3b 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ ver 0.16.8 (2012/??/??) - vorbis (and others): fix seeking at startup - ffmpeg: read the "year" tag * output: + - jack: check for connection failure before starting playback - jack: workaround for libjack1 crash bug - osx: fix stuttering due to buffering bug * fix endless loop in text file reader diff --git a/src/output/jack_output_plugin.c b/src/output/jack_output_plugin.c index bc13c2f8..c67fcd38 100644 --- a/src/output/jack_output_plugin.c +++ b/src/output/jack_output_plugin.c @@ -576,6 +576,9 @@ mpd_jack_open(void *data, struct audio_format *audio_format, GError **error_r) jd->pause = false; + if (jd->client != NULL && jd->shutdown) + mpd_jack_disconnect(jd); + if (jd->client == NULL && !mpd_jack_connect(jd, error_r)) return false; -- cgit v1.2.3 From 98a468a1013401298205390db43b63a6ff3a3478 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 4 Apr 2012 23:10:38 +0200 Subject: encoder/vorbis: generate end-of-stream packet before tag Don't reset the ogg_stream_state object, because this discards the end-of-stream packet that was just added. --- NEWS | 2 ++ src/encoder/vorbis_encoder.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'NEWS') diff --git a/NEWS b/NEWS index 8eb3ed3b..caa3cd07 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 +* encoder: + - vorbis: generate end-of-stream packet before tag * output: - jack: check for connection failure before starting playback - jack: workaround for libjack1 crash bug diff --git a/src/encoder/vorbis_encoder.c b/src/encoder/vorbis_encoder.c index 38a998bd..519d7cbf 100644 --- a/src/encoder/vorbis_encoder.c +++ b/src/encoder/vorbis_encoder.c @@ -285,8 +285,6 @@ vorbis_encoder_pre_tag(struct encoder *_encoder, G_GNUC_UNUSED GError **error) vorbis_analysis_init(&encoder->vd, &encoder->vi); vorbis_block_init(&encoder->vd, &encoder->vb); - ogg_stream_reset(&encoder->os); - encoder->flush = true; return true; } -- cgit v1.2.3 From 5acee73fc85e44179120a5818247fc0760038cff Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 5 Apr 2012 00:03:38 +0200 Subject: encoder/vorbis: generate end-of-stream packet when playback ends Add the encoder_plugin method end(). This is important for the recorder plugin. --- NEWS | 1 + src/encoder/flac_encoder.c | 1 + src/encoder/twolame_encoder.c | 1 + src/encoder/vorbis_encoder.c | 1 + src/encoder_plugin.h | 39 +++++++++++++++++++++++++++++++++++-- src/output/recorder_output_plugin.c | 2 +- src/output/shout_plugin.c | 2 +- test/run_encoder.c | 2 +- test/test_vorbis_encoder.c | 2 +- 9 files changed, 45 insertions(+), 6 deletions(-) (limited to 'NEWS') diff --git a/NEWS b/NEWS index caa3cd07..5a215dad 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ ver 0.16.8 (2012/??/??) - ffmpeg: read the "year" tag * encoder: - vorbis: generate end-of-stream packet before tag + - vorbis: generate end-of-stream packet when playback ends * output: - jack: check for connection failure before starting playback - jack: workaround for libjack1 crash bug diff --git a/src/encoder/flac_encoder.c b/src/encoder/flac_encoder.c index 6389513e..e2c455e3 100644 --- a/src/encoder/flac_encoder.c +++ b/src/encoder/flac_encoder.c @@ -354,6 +354,7 @@ const struct encoder_plugin flac_encoder_plugin = { .finish = flac_encoder_finish, .open = flac_encoder_open, .close = flac_encoder_close, + .end = flac_encoder_flush, .flush = flac_encoder_flush, .write = flac_encoder_write, .read = flac_encoder_read, diff --git a/src/encoder/twolame_encoder.c b/src/encoder/twolame_encoder.c index d20af551..073c3128 100644 --- a/src/encoder/twolame_encoder.c +++ b/src/encoder/twolame_encoder.c @@ -300,6 +300,7 @@ const struct encoder_plugin twolame_encoder_plugin = { .finish = twolame_encoder_finish, .open = twolame_encoder_open, .close = twolame_encoder_close, + .end = twolame_encoder_flush, .flush = twolame_encoder_flush, .write = twolame_encoder_write, .read = twolame_encoder_read, diff --git a/src/encoder/vorbis_encoder.c b/src/encoder/vorbis_encoder.c index 519d7cbf..9f09b2ac 100644 --- a/src/encoder/vorbis_encoder.c +++ b/src/encoder/vorbis_encoder.c @@ -405,6 +405,7 @@ const struct encoder_plugin vorbis_encoder_plugin = { .finish = vorbis_encoder_finish, .open = vorbis_encoder_open, .close = vorbis_encoder_close, + .end = vorbis_encoder_pre_tag, .flush = vorbis_encoder_flush, .pre_tag = vorbis_encoder_pre_tag, .tag = vorbis_encoder_tag, diff --git a/src/encoder_plugin.h b/src/encoder_plugin.h index af3f76a4..70eee51a 100644 --- a/src/encoder_plugin.h +++ b/src/encoder_plugin.h @@ -35,7 +35,7 @@ struct encoder { const struct encoder_plugin *plugin; #ifndef NDEBUG - bool open, pre_tag, tag; + bool open, pre_tag, tag, end; #endif }; @@ -53,6 +53,8 @@ struct encoder_plugin { void (*close)(struct encoder *encoder); + bool (*end)(struct encoder *encoder, GError **error); + bool (*flush)(struct encoder *encoder, GError **error); bool (*pre_tag)(struct encoder *encoder, GError **error); @@ -132,7 +134,7 @@ encoder_open(struct encoder *encoder, struct audio_format *audio_format, bool success = encoder->plugin->open(encoder, audio_format, error); #ifndef NDEBUG encoder->open = success; - encoder->pre_tag = encoder->tag = false; + encoder->pre_tag = encoder->tag = encoder->end = false; #endif return success; } @@ -156,6 +158,35 @@ encoder_close(struct encoder *encoder) #endif } +/** + * Ends the stream: flushes the encoder object, generate an + * end-of-stream marker (if applicable), make everything which might + * currently be buffered available by encoder_read(). + * + * After this function has been called, the encoder may not be usable + * for more data, and only encoder_read() and encoder_close() can be + * called. + * + * @param encoder the encoder + * @param error location to store the error occuring, or NULL to ignore errors. + * @return true on success + */ +static inline bool +encoder_end(struct encoder *encoder, GError **error) +{ + assert(encoder->open); + assert(!encoder->end); + +#ifndef NDEBUG + encoder->end = true; +#endif + + /* this method is optional */ + return encoder->plugin->end != NULL + ? encoder->plugin->end(encoder, error) + : true; +} + /** * Flushes an encoder object, make everything which might currently be * buffered available by encoder_read(). @@ -170,6 +201,7 @@ encoder_flush(struct encoder *encoder, GError **error) assert(encoder->open); assert(!encoder->pre_tag); assert(!encoder->tag); + assert(!encoder->end); /* this method is optional */ return encoder->plugin->flush != NULL @@ -193,6 +225,7 @@ encoder_pre_tag(struct encoder *encoder, GError **error) assert(encoder->open); assert(!encoder->pre_tag); assert(!encoder->tag); + assert(!encoder->end); /* this method is optional */ bool success = encoder->plugin->pre_tag != NULL @@ -222,6 +255,7 @@ encoder_tag(struct encoder *encoder, const struct tag *tag, GError **error) assert(encoder->open); assert(!encoder->pre_tag); assert(encoder->tag); + assert(!encoder->end); #ifndef NDEBUG encoder->tag = false; @@ -249,6 +283,7 @@ encoder_write(struct encoder *encoder, const void *data, size_t length, assert(encoder->open); assert(!encoder->pre_tag); assert(!encoder->tag); + assert(!encoder->end); return encoder->plugin->write(encoder, data, length, error); } diff --git a/src/output/recorder_output_plugin.c b/src/output/recorder_output_plugin.c index 10d64106..2f088a10 100644 --- a/src/output/recorder_output_plugin.c +++ b/src/output/recorder_output_plugin.c @@ -191,7 +191,7 @@ recorder_output_close(void *data) /* flush the encoder and write the rest to the file */ - if (encoder_flush(recorder->encoder, NULL)) + if (encoder_end(recorder->encoder, NULL)) recorder_output_encoder_to_file(recorder, NULL); /* now really close everything */ diff --git a/src/output/shout_plugin.c b/src/output/shout_plugin.c index 35efd9fc..27ef3b99 100644 --- a/src/output/shout_plugin.c +++ b/src/output/shout_plugin.c @@ -358,7 +358,7 @@ static void close_shout_conn(struct shout_data * sd) sd->buf.len = 0; if (sd->encoder != NULL) { - if (encoder_flush(sd->encoder, NULL)) + if (encoder_end(sd->encoder, NULL)) write_page(sd, NULL); encoder_close(sd->encoder); diff --git a/test/run_encoder.c b/test/run_encoder.c index 16e63302..4c05a06c 100644 --- a/test/run_encoder.c +++ b/test/run_encoder.c @@ -121,7 +121,7 @@ int main(int argc, char **argv) encoder_to_stdout(encoder); } - ret = encoder_flush(encoder, &error); + ret = encoder_end(encoder, &error); if (!ret) { g_printerr("encoder_flush() failed: %s\n", error->message); diff --git a/test/test_vorbis_encoder.c b/test/test_vorbis_encoder.c index 370a8a59..969ab768 100644 --- a/test/test_vorbis_encoder.c +++ b/test/test_vorbis_encoder.c @@ -99,7 +99,7 @@ main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv) /* finish */ - success = encoder_flush(encoder, NULL); + success = encoder_end(encoder, NULL); assert(success); encoder_to_stdout(encoder); -- cgit v1.2.3