player/Thread: calculate `buffered_before_play` based on a fixed duration

Previously, there was the setting `buffered_before_play` which
specified a percentage of the audio buffer, defaulting to `10%`.  That
was working well enough for quite some time, until high-quality audio
formats became common.

At 44.1 kHz, 16 bit stereo, MPD collected 2.3 seconds worth of data in
the buffer before starting playback.  With the same default settings
and 192 kHz, 24 bit stereo, that was only 0.27 seconds.

Making this depend on the byte size only leads to high latency at low
quality, and too little data at high quality.  The natural choice
would be to use a duration instead of a byte size, which should give
the same good experience with all audio formats.

Since the `buffered_before_play` configuration setting was not
understood well by users and caused more harm than good, this commit
deprecates it.  It has now no effect.
This commit is contained in:
Max Kellermann 2018-09-23 15:26:42 +02:00
parent c1600bcf3d
commit 5b2374b949
3 changed files with 17 additions and 4 deletions

1
NEWS
View File

@ -14,6 +14,7 @@ ver 0.21 (not yet released)
- proxy: require libmpdclient 2.9
- proxy: forward `sort` and `window` to server
* player
- hard-code "buffer_before_play" to 1 second, independent of audio format
- "one-shot" single mode
* input
- curl: download to buffer instead of throttling transfer

View File

@ -54,7 +54,7 @@ const ConfigTemplate config_param_templates[] = {
{ "volume_normalization" },
{ "samplerate_converter" },
{ "audio_buffer_size" },
{ "buffer_before_play" },
{ "buffer_before_play", false, true },
{ "http_proxy_host", false, true },
{ "http_proxy_port", false, true },
{ "http_proxy_user", false, true },

View File

@ -57,6 +57,12 @@
static constexpr Domain player_domain("player");
/**
* Start playback as soon as enough data for this duration has been
* pushed to the decoder pipe.
*/
static constexpr auto buffer_before_play_duration = std::chrono::seconds(1);
class Player {
PlayerControl &pc;
@ -80,9 +86,10 @@ class Player {
/**
* Start playback as soon as this number of chunks has been
* pushed to the decoder pipe.
* pushed to the decoder pipe. This is calculated based on
* #buffer_before_play_duration.
*/
const unsigned buffer_before_play;
unsigned buffer_before_play;
/**
* If the decoder pipe gets consumed below this threshold,
@ -191,7 +198,6 @@ public:
Player(PlayerControl &_pc, DecoderControl &_dc,
MusicBuffer &_buffer) noexcept
:pc(_pc), dc(_dc), buffer(_buffer),
buffer_before_play(pc.buffered_before_play),
decoder_wakeup_threshold(buffer.GetSize() * 3 / 4)
{
}
@ -517,6 +523,12 @@ Player::CheckDecoderStartup() noexcept
play_audio_format = dc.out_audio_format;
decoder_starting = false;
const size_t buffer_before_play_size =
play_audio_format.TimeToSize(buffer_before_play_duration);
buffer_before_play =
(buffer_before_play_size + sizeof(MusicChunk::data) - 1)
/ sizeof(MusicChunk::data);
idle_add(IDLE_PLAYER);
if (pending_seek > SongTime::zero()) {