From 5655d2e5922955763455134814035e7ad76d2529 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 23 Aug 2008 15:52:44 -0700 Subject: outputBuffer: fix buffer_before_play handling buffer_before_play is a prebuffer; always respecting it is almost as good as having no buffer at all. So we only respect it when we haven't played anything. Bugs that were a side effect of this also got fixed: The player would not stop when we got to the end of the last song on non-repeating playlists. The playlist would continuously show the song in the last few seconds of playback, and never move. Having crossfade enabled would also amplify the above effect. So, as a side effect, crossfade now correctly handles end-of-playlist conditions, as well. It will fade out to silence when we're at the end of a playlist. --- src/outputBuffer.c | 46 +++++++++++++++++++++++++++++++----------- src/outputBuffer_config_init.h | 2 +- src/outputBuffer_xfade.h | 45 ++++++++++++++++++----------------------- 3 files changed, 55 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/outputBuffer.c b/src/outputBuffer.c index 12ab6eb2d..92d90184d 100644 --- a/src/outputBuffer.c +++ b/src/outputBuffer.c @@ -37,6 +37,8 @@ struct ob_chunk { char data[CHUNK_SIZE]; }; +static struct ob_chunk silence; + enum ob_xfade_state { XFADE_DISABLED = 0, XFADE_ENABLED @@ -49,7 +51,10 @@ static struct condition ob_seq_cond = STATIC_COND_INITIALIZER; struct output_buffer { struct ringbuf *index; /* index for chunks */ struct ob_chunk *chunks; - size_t nr_bpp; /* nr (chunks) buffered before play */ + + size_t bpp_max; /* buffer_before_play, user setting, in chunks */ + size_t bpp_cur; /* current prebuffer size (in chunks) */ + enum ob_state state; /* protected by ob_action_cond */ enum ob_action action; /* protected by ob_action_cond */ enum ob_xfade_state xfade_state; /* thread-internal */ @@ -325,7 +330,7 @@ static struct ob_chunk *get_chunk(struct iovec vec[2], size_t i) { if (vec[0].iov_len > i) return &ob.chunks[vec[0].iov_base + i - ob.index->buf]; - if (vec[1].iov_base) { + if (i && vec[1].iov_base) { assert(vec[0].iov_len > 0); i -= vec[0].iov_len; if (vec[1].iov_len > i) @@ -336,8 +341,7 @@ static struct ob_chunk *get_chunk(struct iovec vec[2], size_t i) static void prevent_buffer_underrun(void) { - static const char silence[CHUNK_SIZE]; - if (playAudio(silence, sizeof(silence)) < 0) + if (playAudio(silence.data, sizeof(silence.data)) < 0) stop_playback(); } @@ -407,16 +411,23 @@ static void play_next_chunk(void) assert(pthread_equal(pthread_self(), ob.thread)); nr = ringbuf_get_read_vector(ob.index, vec); - if (mpd_unlikely(!nr && - (dc.state == DC_STATE_STOP) && - ! playlist_playing())) { - stop_playback(); + if (mpd_unlikely(!nr)) { + if (dc.state == DC_STATE_STOP && ! playlist_playing()) + stop_playback(); + else + prevent_buffer_underrun(); return; } - if (nr < ((ob.xfade_time <= 0) ? ob.nr_bpp : xfade_chunks_needed(vec))) - { + + if (ob.xfade_time <= 0 && nr < ob.bpp_cur) { prevent_buffer_underrun(); return; + } else if (nr < xfade_chunks_needed(vec)) { + if (dc.state != DC_STATE_STOP && playlist_playing()) { + prevent_buffer_underrun(); + return; + } + /* nearing end of last track, xfade to silence.. */ } a = get_chunk(vec, 0); @@ -424,8 +435,14 @@ static void play_next_chunk(void) if (! a->len) goto out; - if (nr > 1 && ob.xfade_state == XFADE_ENABLED) { - struct ob_chunk *b = get_chunk(vec, ob.xfade_max); + if (ob.xfade_state == XFADE_ENABLED) { + struct ob_chunk *b; + b = get_chunk(vec, ob.xfade_max); + if (!b) { /* xfade to silence */ + b = &silence; + b->len = a->len; + b->seq = a->seq + 1; + } xfade_mix(a, b); } @@ -444,6 +461,10 @@ static void play_next_chunk(void) out: ringbuf_read_advance(ob.index, 1); + /* we've played our first chunk, stop prebuffering */ + if (mpd_unlikely(ob.bpp_cur)) + ob.bpp_cur = 0; + /* unblock ob_send() if it was waiting on a full buffer */ dc_try_unhalt(); } @@ -466,6 +487,7 @@ static void * ob_task(mpd_unused void *arg) case OB_STATE_PAUSE: case OB_STATE_SEEK: assert(as != AS_DEFERRED); + ob.bpp_cur = ob.bpp_max; /* enable prebuffer */ if (as == AS_INPROGRESS) ob_finalize_action(); cond_wait(&ob_halt_cond); diff --git a/src/outputBuffer_config_init.h b/src/outputBuffer_config_init.h index ba5b7f137..7461c4d73 100644 --- a/src/outputBuffer_config_init.h +++ b/src/outputBuffer_config_init.h @@ -59,7 +59,7 @@ void config_output_buffer(void) buffered_before_play = (perc / 100) * buffered_chunks; if (buffered_before_play > buffered_chunks) buffered_before_play = buffered_chunks; - ob.nr_bpp = buffered_before_play; + ob.bpp_max = buffered_before_play; assert(buffered_chunks > 0 && !ob.index && !ob.chunks); ob.index = ringbuf_create(buffered_chunks); diff --git a/src/outputBuffer_xfade.h b/src/outputBuffer_xfade.h index 0f3a4d5a7..80efe704e 100644 --- a/src/outputBuffer_xfade.h +++ b/src/outputBuffer_xfade.h @@ -18,7 +18,7 @@ static size_t calculate_xfade_chunks(struct iovec vec[2]) if (!ob.total_time || (ob.elapsed_time + ob.xfade_time) < ob.total_time || !isCurrentAudioFormat(af)) - return ob.nr_bpp; /* too early, don't enable xfade yet */ + return ob.bpp_cur; /* too early, don't enable xfade yet */ assert(af->bits > 0); assert(af->channels > 0); @@ -26,35 +26,30 @@ static size_t calculate_xfade_chunks(struct iovec vec[2]) chunks = af->sampleRate * af->bits * af->channels / 8.0 / CHUNK_SIZE; chunks = chunks * (ob.xfade_time + 0.5); + assert(chunks); - assert(ob.index->size >= ob.nr_bpp); - if (chunks > (ob.index->size - ob.nr_bpp)) - chunks = ob.index->size - ob.nr_bpp; + assert(ob.index->size >= ob.bpp_cur); + if (chunks > (ob.index->size - ob.bpp_cur)) + chunks = ob.index->size - ob.bpp_cur; DEBUG("calculated xfade chunks: %d\n", chunks); nr = vec[0].iov_len + vec[1].iov_len; - if (chunks > nr) - return chunks; /* not enough work with */ - - c = get_chunk(vec, chunks); - assert(c); - if (c->seq == ob.seq_player) { - do { - if (!(c = get_chunk(vec, ++chunks))) - return chunks; /* not enough to work with */ - } while (c->seq == ob.seq_player); - } else { - do { - c = get_chunk(vec, --chunks); - assert(c); - } while (c->seq == ob.seq_decoder); - assert((c = get_chunk(vec, chunks))); - assert(c->seq != ob.seq_decoder); - ++chunks; - assert((c = get_chunk(vec, chunks))); - assert(c->seq == ob.seq_decoder); + if (chunks <= nr) { + c = get_chunk(vec, chunks); + assert(c); + if (c->seq != ob.seq_player) { + do { + c = get_chunk(vec, --chunks); + assert(c); + } while (chunks && c->seq == ob.seq_decoder); + assert((c = get_chunk(vec, chunks))); + assert(c->seq != ob.seq_decoder); + ++chunks; + assert((c = get_chunk(vec, chunks))); + assert(c->seq == ob.seq_decoder); + } + DEBUG("adjusted xfade chunks: %d\n", chunks); } - DEBUG("adjusted xfade chunks: %d\n", chunks); ob.xfade_cur = chunks; ob.xfade_max = chunks; -- cgit v1.2.3