diff --git a/NEWS b/NEWS
index c5d581a7b..3db3c6e2a 100644
--- a/NEWS
+++ b/NEWS
@@ -42,6 +42,17 @@ ver 0.20 (not yet released)
* update
- apply .mpdignore matches to subdirectories
+ver 0.19.11 (2015/10/27)
+* tags
+ - ape: fix buffer overflow
+* decoder
+ - ffmpeg: fix crash due to wrong avio_alloc_context() call
+ - gme: don't loop forever, fall back to GME's default play length
+* encoder
+ - flac: fix crash with 32 bit playback
+* mixer
+ - fix mixer lag after enabling/disabling output
+
ver 0.19.10 (2015/06/21)
* input
- curl: fix deadlock on small responses
diff --git a/doc/user.xml b/doc/user.xml
index f66f6e397..e30b667ca 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -1025,6 +1025,58 @@ database {
plugin).
+
+
+ Real-Time Scheduling
+
+
+ On Linux, MPD attempts to configure
+ real-time
+ scheduling for some threads that benefit from it.
+
+
+
+ This is only possible you allow MPD
+ to do it. This privilege is controlled by
+ RLIMIT_RTPRIO
+ RLIMIT_RTTIME. You can configure this
+ privilege with ulimit before launching
+ MPD:
+
+
+ ulimit -HS -r 50; mpd
+
+
+ Or you can use the prlimit program from the
+ util-linux package:
+
+
+ prlimit --rtprio=50 --rttime=unlimited mpd
+
+
+ The systemd service file shipped
+ with MPD comes with this setting.
+
+
+
+ This works only if the Linux kernel was compiled with
+ CONFIG_RT_GROUP_SCHED disabled. Use the
+ following command to check this option for your current
+ kernel:
+
+
+ zgrep ^CONFIG_RT_GROUP_SCHED /proc/config.gz
+
+
+
+ There is a rumor that real-time scheduling improves audio
+ quality. That is not true. All it does is reduce the
+ probability of skipping (audio buffer xruns) when the
+ computer is under heavy load.
+
+
+
diff --git a/src/decoder/plugins/FfmpegIo.cxx b/src/decoder/plugins/FfmpegIo.cxx
index 9603a131d..08fddffa5 100644
--- a/src/decoder/plugins/FfmpegIo.cxx
+++ b/src/decoder/plugins/FfmpegIo.cxx
@@ -28,7 +28,10 @@
AvioStream::~AvioStream()
{
- av_free(io);
+ if (io != nullptr) {
+ av_free(io->buffer);
+ av_free(io);
+ }
}
inline int
@@ -90,9 +93,18 @@ AvioStream::_Seek(void *opaque, int64_t pos, int whence)
bool
AvioStream::Open()
{
- io = avio_alloc_context(buffer, sizeof(buffer),
+ constexpr size_t BUFFER_SIZE = 8192;
+ auto buffer = (unsigned char *)av_malloc(BUFFER_SIZE);
+ if (buffer == nullptr)
+ return false;
+
+ io = avio_alloc_context(buffer, BUFFER_SIZE,
false, this,
_Read, nullptr,
input.IsSeekable() ? _Seek : nullptr);
+ /* If avio_alloc_context() fails, who frees the buffer? The
+ libavformat API documentation does not specify this, it
+ only says that AVIOContext.buffer must be freed in the end,
+ however no AVIOContext exists in that failure code path. */
return io != nullptr;
}
diff --git a/src/decoder/plugins/FfmpegIo.hxx b/src/decoder/plugins/FfmpegIo.hxx
index add4b40e7..2deb7fd38 100644
--- a/src/decoder/plugins/FfmpegIo.hxx
+++ b/src/decoder/plugins/FfmpegIo.hxx
@@ -37,8 +37,6 @@ struct AvioStream {
AVIOContext *io;
- uint8_t buffer[8192];
-
AvioStream(Decoder *_decoder, InputStream &_input)
:decoder(_decoder), input(_input), io(nullptr) {}
diff --git a/src/decoder/plugins/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx
index 693237f2f..cdaa68dcf 100644
--- a/src/decoder/plugins/GmeDecoderPlugin.cxx
+++ b/src/decoder/plugins/GmeDecoderPlugin.cxx
@@ -157,8 +157,11 @@ gme_file_decode(Decoder &decoder, Path path_fs)
return;
}
- const SignedSongTime song_len = ti->length > 0
- ? SignedSongTime::FromMS(ti->length)
+ const int length = ti->play_length;
+ gme_free_info(ti);
+
+ const SignedSongTime song_len = length > 0
+ ? SignedSongTime::FromMS(length)
: SignedSongTime::Negative();
/* initialize the MPD decoder */
@@ -169,7 +172,6 @@ gme_file_decode(Decoder &decoder, Path path_fs)
SampleFormat::S16, GME_CHANNELS,
error)) {
LogError(error);
- gme_free_info(ti);
gme_delete(emu);
return;
}
@@ -180,8 +182,8 @@ gme_file_decode(Decoder &decoder, Path path_fs)
if (gme_err != nullptr)
LogWarning(gme_domain, gme_err);
- if (ti->length > 0)
- gme_set_fade(emu, ti->length);
+ if (length > 0)
+ gme_set_fade(emu, length);
/* play */
DecoderCommand cmd;
@@ -197,16 +199,17 @@ gme_file_decode(Decoder &decoder, Path path_fs)
if (cmd == DecoderCommand::SEEK) {
unsigned where = decoder_seek_time(decoder).ToMS();
gme_err = gme_seek(emu, where);
- if (gme_err != nullptr)
+ if (gme_err != nullptr) {
LogWarning(gme_domain, gme_err);
- decoder_command_finished(decoder);
+ decoder_seek_error(decoder);
+ } else
+ decoder_command_finished(decoder);
}
if (gme_track_ended(emu))
break;
} while (cmd != DecoderCommand::STOP);
- gme_free_info(ti);
gme_delete(emu);
}
@@ -214,9 +217,9 @@ static void
ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
const struct tag_handler *handler, void *handler_ctx)
{
- if (info.length > 0)
+ if (info.play_length > 0)
tag_handler_invoke_duration(handler, handler_ctx,
- SongTime::FromMS(info.length));
+ SongTime::FromMS(info.play_length));
if (info.song != nullptr) {
if (track_count > 1) {
diff --git a/src/encoder/plugins/FlacEncoderPlugin.cxx b/src/encoder/plugins/FlacEncoderPlugin.cxx
index afeef3b84..86a3588df 100644
--- a/src/encoder/plugins/FlacEncoderPlugin.cxx
+++ b/src/encoder/plugins/FlacEncoderPlugin.cxx
@@ -157,8 +157,6 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
unsigned bits_per_sample;
- encoder->audio_format = audio_format;
-
/* FIXME: flac should support 32bit as well */
switch (audio_format.format) {
case SampleFormat::S8:
@@ -178,6 +176,8 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
audio_format.format = SampleFormat::S24_P32;
}
+ encoder->audio_format = audio_format;
+
/* allocate the encoder */
encoder->fse = FLAC__stream_encoder_new();
if (encoder->fse == nullptr) {
diff --git a/src/output/OutputCommand.cxx b/src/output/OutputCommand.cxx
index 83abcf2ca..dc7a540a2 100644
--- a/src/output/OutputCommand.cxx
+++ b/src/output/OutputCommand.cxx
@@ -30,6 +30,7 @@
#include "Internal.hxx"
#include "player/Control.hxx"
#include "mixer/MixerControl.hxx"
+#include "mixer/Volume.hxx"
#include "Idle.hxx"
extern unsigned audio_output_state_version;
@@ -47,6 +48,11 @@ audio_output_enable_index(MultipleOutputs &outputs, unsigned idx)
ao.enabled = true;
idle_add(IDLE_OUTPUT);
+ if (ao.mixer != nullptr) {
+ InvalidateHardwareVolume();
+ idle_add(IDLE_MIXER);
+ }
+
ao.player_control->UpdateAudio();
++audio_output_state_version;
@@ -70,6 +76,7 @@ audio_output_disable_index(MultipleOutputs &outputs, unsigned idx)
Mixer *mixer = ao.mixer;
if (mixer != nullptr) {
mixer_close(mixer);
+ InvalidateHardwareVolume();
idle_add(IDLE_MIXER);
}
@@ -94,6 +101,7 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx)
Mixer *mixer = ao.mixer;
if (mixer != nullptr) {
mixer_close(mixer);
+ InvalidateHardwareVolume();
idle_add(IDLE_MIXER);
}
}
diff --git a/src/output/plugins/PipeOutputPlugin.cxx b/src/output/plugins/PipeOutputPlugin.cxx
index a18890804..c70e2d498 100644
--- a/src/output/plugins/PipeOutputPlugin.cxx
+++ b/src/output/plugins/PipeOutputPlugin.cxx
@@ -51,7 +51,6 @@ public:
}
size_t Play(const void *chunk, size_t size, Error &error);
-
};
inline bool
diff --git a/src/tag/ApeLoader.cxx b/src/tag/ApeLoader.cxx
index c42d088a8..b1759a730 100644
--- a/src/tag/ApeLoader.cxx
+++ b/src/tag/ApeLoader.cxx
@@ -79,12 +79,12 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback)
/* get the key */
const char *key = p;
- while (remaining > size && *p != '\0') {
- p++;
- remaining--;
- }
- p++;
- remaining--;
+ const char *key_end = (const char *)memchr(p, '\0', remaining);
+ if (key_end == nullptr)
+ break;
+
+ p = key_end + 1;
+ remaining -= p - key;
/* get the value */
if (remaining < size)
diff --git a/systemd/mpd.service.in b/systemd/mpd.service.in
index c4600406d..6dce7d93d 100644
--- a/systemd/mpd.service.in
+++ b/systemd/mpd.service.in
@@ -10,15 +10,6 @@ ExecStart=@prefix@/bin/mpd --no-daemon
LimitRTPRIO=50
LimitRTTIME=-1
-# move MPD to a top-level cgroup, as real-time budget assignment fails
-# in cgroup /system/mpd.service, because /system has a zero real-time
-# budget; see
-# http://www.freedesktop.org/wiki/Software/systemd/MyServiceCantGetRealtime/
-ControlGroup=cpu:/mpd
-
-# assign a real-time budget
-ControlGroupAttribute=cpu.rt_runtime_us 500000
-
# disallow writing to /usr, /bin, /sbin, ...
ProtectSystem=yes