aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--NEWS9
-rw-r--r--src/decoder/ffmpeg_decoder_plugin.c4
-rw-r--r--src/decoder/mp4ff_decoder_plugin.c8
-rw-r--r--src/decoder_control.c2
-rw-r--r--src/input/rewind_input_plugin.c6
-rw-r--r--src/output_all.c4
-rw-r--r--src/output_control.c37
-rw-r--r--src/output_internal.h3
-rw-r--r--src/output_thread.c15
-rw-r--r--src/pipe.h6
-rw-r--r--src/player_thread.c60
11 files changed, 109 insertions, 45 deletions
diff --git a/NEWS b/NEWS
index 9f8d87b8d..c8f06e8ae 100644
--- a/NEWS
+++ b/NEWS
@@ -109,6 +109,15 @@ ver 0.16 (20??/??/??)
* make single mode 'sticky'
+ver 0.15.14 (2010/11/06)
+* player_thread: fix assertion failure due to wrong music pipe on seek
+* output_thread: fix assertion failure due to race condition in OPEN
+* input:
+ - rewind: fix double free bug
+* decoders:
+ - mp4ff, ffmpeg: add extension ".m4b" (audio book)
+
+
ver 0.15.13 (2010/10/10)
* output_thread: fix race condition after CANCEL command
* output:
diff --git a/src/decoder/ffmpeg_decoder_plugin.c b/src/decoder/ffmpeg_decoder_plugin.c
index ea5b97d08..f9d4eb8a9 100644
--- a/src/decoder/ffmpeg_decoder_plugin.c
+++ b/src/decoder/ffmpeg_decoder_plugin.c
@@ -524,7 +524,9 @@ static const char *const ffmpeg_suffixes[] = {
"atrac", "au", "aud", "avi", "avm2", "avs", "bap", "bfi", "c93", "cak",
"cin", "cmv", "cpk", "daud", "dct", "divx", "dts", "dv", "dvd", "dxa",
"eac3", "film", "flac", "flc", "fli", "fll", "flx", "flv", "g726",
- "gsm", "gxf", "iss", "m1v", "m2v", "m2t", "m2ts", "m4a", "m4v", "mad",
+ "gsm", "gxf", "iss", "m1v", "m2v", "m2t", "m2ts",
+ "m4a", "m4b", "m4v",
+ "mad",
"mj2", "mjpeg", "mjpg", "mka", "mkv", "mlp", "mm", "mmf", "mov", "mp+",
"mp1", "mp2", "mp3", "mp4", "mpc", "mpeg", "mpg", "mpga", "mpp", "mpu",
"mve", "mvi", "mxf", "nc", "nsv", "nut", "nuv", "oga", "ogm", "ogv",
diff --git a/src/decoder/mp4ff_decoder_plugin.c b/src/decoder/mp4ff_decoder_plugin.c
index ce4848409..861b08194 100644
--- a/src/decoder/mp4ff_decoder_plugin.c
+++ b/src/decoder/mp4ff_decoder_plugin.c
@@ -417,7 +417,13 @@ mp4_stream_tag(struct input_stream *is)
return tag;
}
-static const char *const mp4_suffixes[] = { "m4a", "mp4", NULL };
+static const char *const mp4_suffixes[] = {
+ "m4a",
+ "m4b",
+ "mp4",
+ NULL
+};
+
static const char *const mp4_mime_types[] = { "audio/mp4", "audio/m4a", NULL };
const struct decoder_plugin mp4ff_decoder_plugin = {
diff --git a/src/decoder_control.c b/src/decoder_control.c
index 224abbf31..a5e6e4ad3 100644
--- a/src/decoder_control.c
+++ b/src/decoder_control.c
@@ -20,6 +20,7 @@
#include "config.h"
#include "decoder_control.h"
#include "player_control.h"
+#include "pipe.h"
#include <assert.h>
@@ -106,6 +107,7 @@ dc_start(struct decoder_control *dc, struct song *song,
assert(song != NULL);
assert(buffer != NULL);
assert(pipe != NULL);
+ assert(music_pipe_empty(pipe));
dc->song = song;
dc->buffer = buffer;
diff --git a/src/input/rewind_input_plugin.c b/src/input/rewind_input_plugin.c
index 714927c60..cee0d3cd4 100644
--- a/src/input/rewind_input_plugin.c
+++ b/src/input/rewind_input_plugin.c
@@ -80,14 +80,16 @@ copy_attributes(struct input_rewind *r)
struct input_stream *dest = &r->base;
const struct input_stream *src = r->input;
+ assert(dest != src);
+ assert(dest->mime != src->mime);
+
dest->ready = src->ready;
dest->seekable = src->seekable;
dest->size = src->size;
dest->offset = src->offset;
if (src->mime != NULL) {
- if (dest->mime != NULL)
- g_free(dest->mime);
+ g_free(dest->mime);
dest->mime = g_strdup(src->mime);
}
}
diff --git a/src/output_all.c b/src/output_all.c
index 5b066ba26..19c0f0166 100644
--- a/src/output_all.c
+++ b/src/output_all.c
@@ -323,7 +323,7 @@ audio_output_all_open(const struct audio_format *audio_format,
else
/* if the pipe hasn't been cleared, the the audio
format must not have changed */
- assert(music_pipe_size(g_mp) == 0 ||
+ assert(music_pipe_empty(g_mp) ||
audio_format_equals(audio_format,
&input_audio_format));
@@ -436,7 +436,7 @@ audio_output_all_check(void)
assert(g_mp != NULL);
while ((chunk = music_pipe_peek(g_mp)) != NULL) {
- assert(music_pipe_size(g_mp) > 0);
+ assert(!music_pipe_empty(g_mp));
if (!chunk_is_consumed(chunk))
/* at least one output is not finished playing
diff --git a/src/output_control.c b/src/output_control.c
index 5b9b2b902..161404f78 100644
--- a/src/output_control.c
+++ b/src/output_control.c
@@ -102,6 +102,12 @@ audio_output_disable(struct audio_output *ao)
g_mutex_unlock(ao->mutex);
}
+static void
+audio_output_close_locked(struct audio_output *ao);
+
+/**
+ * Object must be locked (and unlocked) by the caller.
+ */
static bool
audio_output_open(struct audio_output *ao,
const struct audio_format *audio_format,
@@ -173,6 +179,8 @@ audio_output_open(struct audio_output *ao,
static void
audio_output_close_locked(struct audio_output *ao)
{
+ assert(ao != NULL);
+
if (ao->mixer != NULL)
mixer_auto_close(ao->mixer);
@@ -251,25 +259,6 @@ void audio_output_cancel(struct audio_output *ao)
g_mutex_unlock(ao->mutex);
}
-void audio_output_close(struct audio_output *ao)
-{
- if (ao->mixer != NULL)
- mixer_auto_close(ao->mixer);
-
- g_mutex_lock(ao->mutex);
-
- assert(!ao->open || ao->fail_timer == NULL);
-
- if (ao->open)
- ao_command(ao, AO_COMMAND_CLOSE);
- else if (ao->fail_timer != NULL) {
- g_timer_destroy(ao->fail_timer);
- ao->fail_timer = NULL;
- }
-
- g_mutex_unlock(ao->mutex);
-}
-
void
audio_output_release(struct audio_output *ao)
{
@@ -279,6 +268,16 @@ audio_output_release(struct audio_output *ao)
audio_output_close(ao);
}
+void audio_output_close(struct audio_output *ao)
+{
+ assert(ao != NULL);
+ assert(!ao->open || ao->fail_timer == NULL);
+
+ g_mutex_lock(ao->mutex);
+ audio_output_close_locked(ao);
+ g_mutex_unlock(ao->mutex);
+}
+
void audio_output_finish(struct audio_output *ao)
{
audio_output_close(ao);
diff --git a/src/output_internal.h b/src/output_internal.h
index 9e4d1f25d..18d431352 100644
--- a/src/output_internal.h
+++ b/src/output_internal.h
@@ -196,7 +196,8 @@ struct audio_output {
const struct music_pipe *pipe;
/**
- * This mutex protects #open, #chunk and #chunk_finished.
+ * This mutex protects #open, #fail_timer, #chunk and
+ * #chunk_finished.
*/
GMutex *mutex;
diff --git a/src/output_thread.c b/src/output_thread.c
index 8b120325c..380956fac 100644
--- a/src/output_thread.c
+++ b/src/output_thread.c
@@ -134,10 +134,18 @@ ao_open(struct audio_output *ao)
struct audio_format_string af_string;
assert(!ao->open);
- assert(ao->fail_timer == NULL);
assert(ao->pipe != NULL);
assert(ao->chunk == NULL);
+ if (ao->fail_timer != NULL) {
+ /* this can only happen when this
+ output thread fails while
+ audio_output_open() is run in the
+ player thread */
+ g_timer_destroy(ao->fail_timer);
+ ao->fail_timer = NULL;
+ }
+
/* enable the device (just in case the last enable has failed) */
if (!ao_enable(ao))
@@ -455,7 +463,12 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
/* don't automatically reopen this device for
10 seconds */
+ g_mutex_lock(ao->mutex);
+
+ assert(ao->fail_timer == NULL);
ao->fail_timer = g_timer_new();
+
+ g_mutex_unlock(ao->mutex);
return false;
}
diff --git a/src/pipe.h b/src/pipe.h
index 676412bb5..7eabb6e54 100644
--- a/src/pipe.h
+++ b/src/pipe.h
@@ -99,4 +99,10 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk);
unsigned
music_pipe_size(const struct music_pipe *mp);
+static inline bool
+music_pipe_empty(const struct music_pipe *mp)
+{
+ return music_pipe_size(mp) == 0;
+}
+
#endif
diff --git a/src/player_thread.c b/src/player_thread.c
index 13d1c2ebb..2d8822eb0 100644
--- a/src/player_thread.c
+++ b/src/player_thread.c
@@ -149,6 +149,32 @@ player_dc_start(struct player *player, struct music_pipe *pipe)
}
/**
+ * Is the decoder still busy on the same song as the player?
+ *
+ * Note: this function does not check if the decoder is already
+ * finished.
+ */
+static bool
+player_dc_at_current_song(const struct player *player)
+{
+ assert(player != NULL);
+ assert(player->pipe != NULL);
+
+ return player->dc->pipe == player->pipe;
+}
+
+/**
+ * Returns true if the decoder is decoding the next song (or has begun
+ * decoding it, or has finished doing it), and the player hasn't
+ * switched to that song yet.
+ */
+static bool
+player_dc_at_next_song(const struct player *player)
+{
+ return player->dc->pipe != NULL && !player_dc_at_current_song(player);
+}
+
+/**
* Stop the decoder and clears (and frees) its music pipe.
*
* Player lock is not held.
@@ -173,17 +199,6 @@ player_dc_stop(struct player *player)
}
/**
- * Returns true if the decoder is decoding the next song (or has begun
- * decoding it, or has finished doing it), and the player hasn't
- * switched to that song yet.
- */
-static bool
-decoding_next_song(const struct player *player)
-{
- return player->dc->pipe != NULL && player->dc->pipe != player->pipe;
-}
-
-/**
* After the decoder has been started asynchronously, wait for the
* "START" command to finish. The decoder may not be initialized yet,
* i.e. there is no audio_format information yet.
@@ -405,6 +420,14 @@ static bool player_seek_decoder(struct player *player)
return false;
}
} else {
+ if (!player_dc_at_current_song(player)) {
+ /* the decoder is already decoding the "next" song,
+ but it is the same song file; exchange the pipe */
+ music_pipe_clear(player->pipe, player_buffer);
+ music_pipe_free(player->pipe);
+ player->pipe = dc->pipe;
+ }
+
pc.next_song = NULL;
player->queued = false;
}
@@ -473,7 +496,7 @@ static void player_process_command(struct player *player)
case PLAYER_COMMAND_QUEUE:
assert(pc.next_song != NULL);
assert(!player->queued);
- assert(dc->pipe == NULL || dc->pipe == player->pipe);
+ assert(!player_dc_at_next_song(player));
player->queued = true;
player_command_finished_locked();
@@ -527,7 +550,7 @@ static void player_process_command(struct player *player)
return;
}
- if (decoding_next_song(player)) {
+ if (player_dc_at_next_song(player)) {
/* the decoder is already decoding the song -
stop it and reset the position */
player_unlock();
@@ -635,7 +658,7 @@ play_next_chunk(struct player *player)
return true;
if (player->xfade == XFADE_ENABLED &&
- decoding_next_song(player) &&
+ player_dc_at_next_song(player) &&
(cross_fade_position = music_pipe_size(player->pipe))
<= player->cross_fade_chunks) {
/* perform cross fade */
@@ -881,12 +904,13 @@ static void do_play(struct decoder_control *dc)
dc->pipe == player.pipe) {
/* the decoder has finished the current song;
make it decode the next song */
+
assert(dc->pipe == NULL || dc->pipe == player.pipe);
player_dc_start(&player, music_pipe_new());
}
- if (decoding_next_song(&player) &&
+ if (player_dc_at_next_song(&player) &&
player.xfade == XFADE_UNKNOWN &&
!decoder_lock_is_starting(dc)) {
/* enable cross fading in this song? if yes,
@@ -919,7 +943,7 @@ static void do_play(struct decoder_control *dc)
if (pc.command == PLAYER_COMMAND_NONE)
player_wait();
continue;
- } else if (music_pipe_size(player.pipe) > 0) {
+ } else if (!music_pipe_empty(player.pipe)) {
/* at least one music chunk is ready - send it
to the audio output */
@@ -931,7 +955,7 @@ static void do_play(struct decoder_control *dc)
/* XXX synchronize in a better way */
g_usleep(10000);
- } else if (decoding_next_song(&player)) {
+ } else if (player_dc_at_next_song(&player)) {
/* at the beginning of a new song */
if (!player_song_border(&player))
@@ -940,7 +964,7 @@ static void do_play(struct decoder_control *dc)
/* check the size of the pipe again, because
the decoder thread may have added something
since we last checked */
- if (music_pipe_size(player.pipe) == 0) {
+ if (music_pipe_empty(player.pipe)) {
/* wait for the hardware to finish
playback */
audio_output_all_drain();