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