Commit 94f850a5 authored by Max Kellermann's avatar Max Kellermann

Merge tag 'v0.19.11'

parents 3882c114 db9997a1
......@@ -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
......
......@@ -1025,6 +1025,58 @@ database {
plugin).
</para>
</section>
<section id="realtime">
<title>Real-Time Scheduling</title>
<para>
On Linux, <application>MPD</application> attempts to configure
<ulink
url="https://en.wikipedia.org/wiki/Real-time_computing">real-time
scheduling</ulink> for some threads that benefit from it.
</para>
<para>
This is only possible you allow <application>MPD</application>
to do it. This privilege is controlled by
<varname>RLIMIT_RTPRIO</varname>
<varname>RLIMIT_RTTIME</varname>. You can configure this
privilege with <command>ulimit</command> before launching
<application>MPD</application>:
</para>
<programlisting>ulimit -HS -r 50; mpd</programlisting>
<para>
Or you can use the <command>prlimit</command> program from the
<application>util-linux</application> package:
</para>
<programlisting>prlimit --rtprio=50 --rttime=unlimited mpd</programlisting>
<para>
The <application>systemd</application> service file shipped
with <application>MPD</application> comes with this setting.
</para>
<para>
This works only if the Linux kernel was compiled with
<varname>CONFIG_RT_GROUP_SCHED</varname> disabled. Use the
following command to check this option for your current
kernel:
</para>
<programlisting>zgrep ^CONFIG_RT_GROUP_SCHED /proc/config.gz</programlisting>
<note>
<para>
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.
</para>
</note>
</section>
</chapter>
<chapter id="use">
......
......@@ -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;
}
......@@ -37,8 +37,6 @@ struct AvioStream {
AVIOContext *io;
uint8_t buffer[8192];
AvioStream(Decoder *_decoder, InputStream &_input)
:decoder(_decoder), input(_input), io(nullptr) {}
......
......@@ -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) {
......
......@@ -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) {
......
......@@ -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);
}
}
......
......@@ -51,7 +51,6 @@ public:
}
size_t Play(const void *chunk, size_t size, Error &error);
};
inline bool
......
......@@ -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)
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment