diff options
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | src/decoder/wavpack_decoder_plugin.c | 47 | ||||
-rw-r--r-- | src/encoder/vorbis_encoder.c | 11 | ||||
-rw-r--r-- | src/encoder_plugin.h | 24 | ||||
-rw-r--r-- | src/output/alsa_plugin.c | 8 | ||||
-rw-r--r-- | src/output/httpd_output_plugin.c | 5 | ||||
-rw-r--r-- | src/output/shout_plugin.c | 2 | ||||
-rw-r--r-- | src/output_thread.c | 7 | ||||
-rw-r--r-- | src/pipe.c | 5 | ||||
-rw-r--r-- | src/pipe.h | 5 | ||||
-rw-r--r-- | src/player_thread.c | 2 | ||||
-rw-r--r-- | src/playlist_control.c | 8 | ||||
-rw-r--r-- | src/update_walk.c | 10 |
13 files changed, 102 insertions, 39 deletions
@@ -21,9 +21,16 @@ ver 0.17 (2011/??/??) ver 0.16.4 (2011/??/??) * fix memory leaks +* don't resume playback when seeking to another song while paused +* apply follow_inside_symlinks to absolute symlinks * decoder: - ffmpeg: workaround for semantic API change in recent ffmpeg versions - flac: validate the sample rate when scanning the tag + - wavpack: obey all decoder commands, stop at CUE track border +* encoder: + - vorbis: don't send end-of-stream on flush +* output: + - alsa: fix SIGFPE when alsa announces a period size of 0 ver 0.16.3 (2011/06/04) diff --git a/src/decoder/wavpack_decoder_plugin.c b/src/decoder/wavpack_decoder_plugin.c index b14a41f93..200bf6455 100644 --- a/src/decoder/wavpack_decoder_plugin.c +++ b/src/decoder/wavpack_decoder_plugin.c @@ -34,9 +34,6 @@ #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "wavpack" -/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */ -#define CHUNK_SIZE 1020 - #define ERRORLEN 80 static struct { @@ -162,8 +159,6 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek) enum sample_format sample_format; struct audio_format audio_format; format_samples_t format_samples; - char chunk[CHUNK_SIZE]; - int samples_requested, samples_got; float total_time; int bytes_per_sample, output_sample_size; @@ -193,12 +188,15 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek) output_sample_size = audio_format_frame_size(&audio_format); /* wavpack gives us all kind of samples in a 32-bit space */ - samples_requested = sizeof(chunk) / (4 * audio_format.channels); + int32_t chunk[1024]; + const uint32_t samples_requested = G_N_ELEMENTS(chunk) / + audio_format.channels; decoder_initialized(decoder, &audio_format, can_seek, total_time); - do { - if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK) { + enum decoder_command cmd = decoder_get_command(decoder); + while (cmd != DECODE_COMMAND_STOP) { + if (cmd == DECODE_COMMAND_SEEK) { if (can_seek) { unsigned where = decoder_seek_where(decoder) * audio_format.sample_rate; @@ -213,29 +211,20 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek) } } - if (decoder_get_command(decoder) == DECODE_COMMAND_STOP) { + uint32_t samples_got = WavpackUnpackSamples(wpc, chunk, + samples_requested); + if (samples_got == 0) break; - } - samples_got = WavpackUnpackSamples( - wpc, (int32_t *)chunk, samples_requested - ); - if (samples_got > 0) { - int bitrate = (int)(WavpackGetInstantBitrate(wpc) / - 1000 + 0.5); - - format_samples( - bytes_per_sample, chunk, - samples_got * audio_format.channels - ); - - decoder_data( - decoder, NULL, chunk, - samples_got * output_sample_size, - bitrate - ); - } - } while (samples_got > 0); + int bitrate = (int)(WavpackGetInstantBitrate(wpc) / 1000 + + 0.5); + format_samples(bytes_per_sample, chunk, + samples_got * audio_format.channels); + + cmd = decoder_data(decoder, NULL, chunk, + samples_got * output_sample_size, + bitrate); + } } /** diff --git a/src/encoder/vorbis_encoder.c b/src/encoder/vorbis_encoder.c index 5b3110988..3e9d486b6 100644 --- a/src/encoder/vorbis_encoder.c +++ b/src/encoder/vorbis_encoder.c @@ -266,6 +266,15 @@ vorbis_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error) { struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; + encoder->flush = true; + return true; +} + +static bool +vorbis_encoder_pre_tag(struct encoder *_encoder, G_GNUC_UNUSED GError **error) +{ + struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; + vorbis_analysis_wrote(&encoder->vd, 0); vorbis_encoder_blockout(encoder); @@ -366,6 +375,7 @@ vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length) if (ret == 0 && encoder->flush) { encoder->flush = false; ret = ogg_stream_flush(&encoder->os, &page); + } if (ret == 0) @@ -398,6 +408,7 @@ const struct encoder_plugin vorbis_encoder_plugin = { .open = vorbis_encoder_open, .close = vorbis_encoder_close, .flush = vorbis_encoder_flush, + .pre_tag = vorbis_encoder_pre_tag, .tag = vorbis_encoder_tag, .write = vorbis_encoder_write, .read = vorbis_encoder_read, diff --git a/src/encoder_plugin.h b/src/encoder_plugin.h index 4a6e3805e..95b3da016 100644 --- a/src/encoder_plugin.h +++ b/src/encoder_plugin.h @@ -50,6 +50,8 @@ struct encoder_plugin { bool (*flush)(struct encoder *encoder, GError **error); + bool (*pre_tag)(struct encoder *encoder, GError **error); + bool (*tag)(struct encoder *encoder, const struct tag *tag, GError **error); @@ -148,8 +150,30 @@ encoder_flush(struct encoder *encoder, GError **error) } /** + * Prepare for sending a tag to the encoder. This is used by some + * encoders to flush the previous sub-stream, in preparation to begin + * a new one. + * + * @param encoder the encoder + * @param tag the tag object + * @param error location to store the error occuring, or NULL to ignore errors. + * @return true on success + */ +static inline bool +encoder_pre_tag(struct encoder *encoder, GError **error) +{ + /* this method is optional */ + return encoder->plugin->pre_tag != NULL + ? encoder->plugin->pre_tag(encoder, error) + : true; +} + +/** * Sends a tag to the encoder. * + * Instructions: call encoder_pre_tag(); then obtain flushed data with + * encoder_read(); finally call encoder_tag(). + * * @param encoder the encoder * @param tag the tag object * @param error location to store the error occurring, or NULL to ignore errors. diff --git a/src/output/alsa_plugin.c b/src/output/alsa_plugin.c index 1c8c7b7f7..0bbe231fd 100644 --- a/src/output/alsa_plugin.c +++ b/src/output/alsa_plugin.c @@ -508,6 +508,14 @@ configure_hw: g_debug("buffer_size=%u period_size=%u", (unsigned)alsa_buffer_size, (unsigned)alsa_period_size); + if (alsa_period_size == 0) + /* this works around a SIGFPE bug that occurred when + an ALSA driver indicated period_size==0; this + caused a division by zero in alsa_play(). By using + the fallback "1", we make sure that this won't + happen again. */ + alsa_period_size = 1; + ad->period_frames = alsa_period_size; ad->period_position = 0; diff --git a/src/output/httpd_output_plugin.c b/src/output/httpd_output_plugin.c index 20098c90e..3f570c7b9 100644 --- a/src/output/httpd_output_plugin.c +++ b/src/output/httpd_output_plugin.c @@ -493,7 +493,8 @@ httpd_output_pause(void *data) if (has_clients) { static const char silence[1020]; - return httpd_output_play(data, silence, sizeof(silence), NULL); + return httpd_output_play(data, silence, sizeof(silence), + NULL) > 0; } else { g_usleep(100000); return true; @@ -522,7 +523,7 @@ httpd_output_tag(void *data, const struct tag *tag) /* flush the current stream, and end it */ - encoder_flush(httpd->encoder, NULL); + encoder_pre_tag(httpd->encoder, NULL); httpd_output_encoder_to_clients(httpd); /* send the tag to the encoder - which starts a new diff --git a/src/output/shout_plugin.c b/src/output/shout_plugin.c index ec5fca3d9..7a4d70e70 100644 --- a/src/output/shout_plugin.c +++ b/src/output/shout_plugin.c @@ -518,7 +518,7 @@ static void my_shout_set_tag(void *data, if (sd->encoder->plugin->tag != NULL) { /* encoder plugin supports stream tags */ - ret = encoder_flush(sd->encoder, &error); + ret = encoder_pre_tag(sd->encoder, &error); if (!ret) { g_warning("%s", error->message); g_error_free(error); diff --git a/src/output_thread.c b/src/output_thread.c index 21096eb04..ec5fc5b31 100644 --- a/src/output_thread.c +++ b/src/output_thread.c @@ -641,8 +641,13 @@ static gpointer audio_output_task(gpointer arg) case AO_COMMAND_CANCEL: ao->chunk = NULL; - if (ao->open) + + if (ao->open) { + g_mutex_unlock(ao->mutex); ao_plugin_cancel(ao->plugin, ao->data); + g_mutex_lock(ao->mutex); + } + ao_command_finished(ao); /* the player thread will now clear our music diff --git a/src/pipe.c b/src/pipe.c index db80c2b2d..d8131432f 100644 --- a/src/pipe.c +++ b/src/pipe.c @@ -187,5 +187,8 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk) unsigned music_pipe_size(const struct music_pipe *mp) { - return mp->size; + g_mutex_lock(mp->mutex); + unsigned size = mp->size; + g_mutex_unlock(mp->mutex); + return size; } diff --git a/src/pipe.h b/src/pipe.h index 785c1b943..84b9869e0 100644 --- a/src/pipe.h +++ b/src/pipe.h @@ -20,6 +20,7 @@ #ifndef MPD_PIPE_H #define MPD_PIPE_H +#include <glib.h> #include <stdbool.h> #ifndef NDEBUG @@ -38,6 +39,7 @@ struct music_pipe; /** * Creates a new #music_pipe object. It is empty. */ +G_GNUC_MALLOC struct music_pipe * music_pipe_new(void); @@ -70,6 +72,7 @@ music_pipe_contains(const struct music_pipe *mp, * Returns the first #music_chunk from the pipe. Returns NULL if the * pipe is empty. */ +G_GNUC_PURE const struct music_chunk * music_pipe_peek(const struct music_pipe *mp); @@ -96,9 +99,11 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk); /** * Returns the number of chunks currently in this pipe. */ +G_GNUC_PURE unsigned music_pipe_size(const struct music_pipe *mp); +G_GNUC_PURE static inline bool music_pipe_empty(const struct music_pipe *mp) { diff --git a/src/player_thread.c b/src/player_thread.c index d47c5c851..58682f2ca 100644 --- a/src/player_thread.c +++ b/src/player_thread.c @@ -628,7 +628,9 @@ play_chunk(struct player_control *pc, return true; } + player_lock(pc); pc->bit_rate = chunk->bit_rate; + player_unlock(pc); /* send the chunk to the audio outputs */ diff --git a/src/playlist_control.c b/src/playlist_control.c index df9384a80..998294845 100644 --- a/src/playlist_control.c +++ b/src/playlist_control.c @@ -230,10 +230,12 @@ playlist_seek_song(struct playlist *playlist, struct player_control *pc, playlist->error_count = 0; if (!playlist->playing || (unsigned)playlist->current != i) { - /* seeking is not within the current song - first - start playing the new song */ + /* seeking is not within the current song - prepare + song change */ + + playlist->playing = true; + playlist->current = i; - playlist_play_order(playlist, pc, i); queued = NULL; } diff --git a/src/update_walk.c b/src/update_walk.c index e5ab4fc37..e70ead173 100644 --- a/src/update_walk.c +++ b/src/update_walk.c @@ -714,8 +714,14 @@ skip_symlink(const struct directory *directory, const char *utf8_name) return false; } - if (buffer[0] == '/') - return !follow_outside_symlinks; + if (g_path_is_absolute(buffer)) { + /* if the symlink points to an absolute path, see if + that path is inside the music directory */ + const char *relative = map_to_relative_path(buffer); + return relative > buffer + ? !follow_inside_symlinks + : !follow_outside_symlinks; + } p = buffer; while (*p == '.') { |