Compare commits

...

15 Commits

Author SHA1 Message Date
Max Kellermann
b0ff3bc7a3 release v0.19.10 2015-06-21 15:51:50 +02:00
Max Kellermann
06301e279c PlayerThread: start the decoder on PlayerCommand::QUEUE
Fixes missing SongBorder() call, which causes "single" mode breakage.
2015-06-21 15:38:48 +02:00
Max Kellermann
6d6f274648 DecoderAPI: discard unused song tag early
If there's a stream tag, don't let the song tag override it in the
next update_stream_tag() call.
2015-06-21 15:10:55 +02:00
Max Kellermann
9acefcb256 DecoderThread: set Decoder::song_tag only for local files
If the song tag comes from a stream, and MPD playback restarts, MPD
would believe the tag should override the newly received tag.  This
makes the previous tag appear stuck.  This change passes the song tag
only if it's authoritative - i.e. if it's a song file.
2015-06-21 15:02:14 +02:00
Max Kellermann
e4d0293a31 DecoderAPI: "move" the Tag object
Reduce runtime overhead.
2015-06-20 16:06:09 +02:00
Romain Rollet
ae77542a11 OutputControl: fix fail_timer check right after booting
Right after booting, the monotonic clock starts with a very small
value, and AudioOutput::LockUpdate() may believe that the fail_timer
has not recovered yet.
2015-06-20 15:37:19 +02:00
Max Kellermann
980187f856 system/PeriodClock: make IsDefined() "constexpr" 2015-06-20 15:33:17 +02:00
Max Kellermann
327a8e6c59 decoder/ffmpeg: skip unwanted samples after seeking
When seeking to the beginning of a packet, skip the samples that come
before the desired time stamp.
2015-06-20 15:01:05 +02:00
Max Kellermann
d11e2724c4 decoder/ffmpeg: use AVSEEK_FLAG_BACKWARD for seeking
Ask FFmpeg to seek to the next packet boundary *before* the seek
position, so we don't miss audio data.  Now we get too much, but we'll
solve that in the next commit.
2015-06-20 14:29:42 +02:00
Max Kellermann
f768ca3a2d decoder/ffmpeg: move code to StreamRelativePts() 2015-06-19 18:56:29 +02:00
Max Kellermann
947e902288 input/curl: trigger the condition variable in RequestDone()
Fixes deadlock on small responses.
2015-06-19 16:45:38 +02:00
Thomas Guillem
3436a646b5 storage/nfs: fix deadlock when connecting
The Connect method can be called between Schedule and lock. In that case, when
locked, the state is already set to CONNECTING of READY and the condition won't
be signaled anymore.
2015-05-29 22:39:14 +02:00
jai
aed0af1e00 input/smbclient: fix DFF playback 2015-05-29 22:37:49 +02:00
Michal Nazarewicz
0d7ee2b014 OpusEncoderPlugin: initialise granulepos so we end up with sane values
Not initialising granulepos leads to it having arbitrary values in the
encoded stream including possibly negative values which are not valid
and confuse opusdec.  Explicitly initialise opus_encoder::granulepos
to avoid that problem.
2015-03-25 18:02:54 +01:00
Max Kellermann
2f5fd91bd8 configure.ac: prepare for 0.19.10 2015-03-25 18:02:54 +01:00
12 changed files with 113 additions and 20 deletions

15
NEWS

@@ -1,3 +1,18 @@
ver 0.19.10 (2015/06/21)
* input
- curl: fix deadlock on small responses
- smbclient: fix DFF playback
* decoder
- ffmpeg: improve seeking accuracy
- fix stuck stream tags
* encoder
- opus: fix bogus granulepos
* output
- fix failure to open device right after booting
* neighbor
- nfs: fix deadlock when connecting
* fix "single" mode breakage due to queue edits
ver 0.19.9 (2015/02/06)
* decoder
- dsdiff, dsf: raise ID3 tag limit to 1 MB

@@ -1,10 +1,10 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.19.9, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.19.10, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=19
VERSION_REVISION=9
VERSION_REVISION=10
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])

@@ -612,6 +612,12 @@ Player::ProcessCommand()
queued = true;
pc.CommandFinished();
pc.Unlock();
if (dc.LockIsIdle())
StartDecoder(*new MusicPipe());
pc.Lock();
break;
case PlayerCommand::PAUSE:

@@ -433,8 +433,11 @@ update_stream_tag(Decoder &decoder, InputStream *is)
/* no stream tag present - submit the song tag
instead */
decoder.song_tag = nullptr;
}
} else
/* discard the song tag; we don't need it */
delete decoder.song_tag;
decoder.song_tag = nullptr;
delete decoder.stream_tag;
decoder.stream_tag = tag;
@@ -566,7 +569,7 @@ decoder_tag(Decoder &decoder, InputStream *is,
/* save the tag */
delete decoder.decoder_tag;
decoder.decoder_tag = new Tag(tag);
decoder.decoder_tag = new Tag(std::move(tag));
/* check for a new stream tag */

@@ -380,7 +380,11 @@ decoder_run_song(DecoderControl &dc,
const DetachedSong &song, const char *uri, Path path_fs)
{
Decoder decoder(dc, dc.start_time.IsPositive(),
new Tag(song.GetTag()));
/* pass the song tag only if it's
authoritative, i.e. if it's a local file -
tags on "stream" songs are just remembered
from the last time we played it*/
song.IsFile() ? new Tag(song.GetTag()) : nullptr);
int ret;
dc.state = DecoderState::START;

@@ -305,20 +305,60 @@ copy_interleave_frame(const AVCodecContext *codec_context,
return data_size;
}
/**
* Convert AVPacket::pts to a stream-relative time stamp (still in
* AVStream::time_base units). Returns a negative value on error.
*/
gcc_pure
static int64_t
StreamRelativePts(const AVPacket &packet, const AVStream &stream)
{
auto pts = packet.pts;
if (pts < 0 || pts == int64_t(AV_NOPTS_VALUE))
return -1;
auto start = start_time_fallback(stream);
return pts - start;
}
/**
* Convert a non-negative stream-relative time stamp in
* AVStream::time_base units to a PCM frame number.
*/
gcc_pure
static uint64_t
PtsToPcmFrame(uint64_t pts, const AVStream &stream,
const AVCodecContext &codec_context)
{
return av_rescale_q(pts, stream.time_base, codec_context.time_base);
}
/**
* @param min_frame skip all data before this PCM frame number; this
* is used after seeking to skip data in an AVPacket until the exact
* desired time stamp has been reached
*/
static DecoderCommand
ffmpeg_send_packet(Decoder &decoder, InputStream &is,
const AVPacket *packet,
AVCodecContext *codec_context,
const AVStream *stream,
AVFrame *frame,
uint64_t min_frame, size_t pcm_frame_size,
uint8_t **buffer, int *buffer_size)
{
if (packet->pts >= 0 && packet->pts != (int64_t)AV_NOPTS_VALUE) {
auto start = start_time_fallback(*stream);
if (packet->pts >= start)
size_t skip_bytes = 0;
const auto pts = StreamRelativePts(*packet, *stream);
if (pts >= 0) {
if (min_frame > 0) {
auto cur_frame = PtsToPcmFrame(pts, *stream,
*codec_context);
if (cur_frame < min_frame)
skip_bytes = pcm_frame_size * (min_frame - cur_frame);
} else
decoder_timestamp(decoder,
time_from_ffmpeg(packet->pts - start,
stream->time_base));
time_from_ffmpeg(pts, stream->time_base));
}
AVPacket packet2 = *packet;
@@ -354,8 +394,20 @@ ffmpeg_send_packet(Decoder &decoder, InputStream &is,
if (audio_size <= 0)
continue;
const uint8_t *data = output_buffer;
if (skip_bytes > 0) {
if (skip_bytes >= size_t(audio_size)) {
skip_bytes -= audio_size;
continue;
}
data += skip_bytes;
audio_size -= skip_bytes;
skip_bytes = 0;
}
cmd = decoder_data(decoder, is,
output_buffer, audio_size,
data, audio_size,
codec_context->bit_rate / 1000);
}
return cmd;
@@ -559,6 +611,8 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
uint8_t *interleaved_buffer = nullptr;
int interleaved_buffer_size = 0;
uint64_t min_frame = 0;
DecoderCommand cmd;
do {
AVPacket packet;
@@ -566,13 +620,15 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
/* end of file */
break;
if (packet.stream_index == audio_stream)
if (packet.stream_index == audio_stream) {
cmd = ffmpeg_send_packet(decoder, input,
&packet, codec_context,
av_stream,
frame,
min_frame, audio_format.GetFrameSize(),
&interleaved_buffer, &interleaved_buffer_size);
else
min_frame = 0;
} else
cmd = decoder_get_command(decoder);
av_free_packet(&packet);
@@ -583,11 +639,16 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
av_stream->time_base) +
start_time_fallback(*av_stream);
/* AVSEEK_FLAG_BACKWARD asks FFmpeg to seek to
the packet boundary before the seek time
stamp, not after */
if (av_seek_frame(format_context, audio_stream, where,
AVSEEK_FLAG_ANY) < 0)
AVSEEK_FLAG_ANY|AVSEEK_FLAG_BACKWARD) < 0)
decoder_seek_error(decoder);
else {
avcodec_flush_buffers(codec_context);
min_frame = decoder_seek_where_frame(decoder);
decoder_command_finished(decoder);
}
}

@@ -66,7 +66,7 @@ struct opus_encoder {
ogg_int64_t granulepos;
opus_encoder():encoder(opus_encoder_plugin) {}
opus_encoder():encoder(opus_encoder_plugin), granulepos(0) {}
};
static constexpr Domain opus_encoder_domain("opus_encoder");

@@ -453,6 +453,8 @@ CurlInputStream::RequestDone(CURLcode result, long status)
SeekDone();
else if (!IsReady())
SetReady();
else
cond.broadcast();
}
static void

@@ -132,6 +132,7 @@ SmbclientInputStream::Read(void *ptr, size_t read_size, Error &error)
nbytes = 0;
}
offset += nbytes;
return nbytes;
}

@@ -184,7 +184,8 @@ AudioOutput::LockUpdate(const AudioFormat audio_format,
const ScopeLock protect(mutex);
if (enabled && really_enabled) {
if (fail_timer.Check(REOPEN_AFTER * 1000)) {
if (!fail_timer.IsDefined() ||
fail_timer.Check(REOPEN_AFTER * 1000)) {
return Open(audio_format, mp);
}
} else if (IsOpen())

@@ -177,6 +177,8 @@ private:
mutex.unlock();
DeferredMonitor::Schedule();
mutex.lock();
if (state == State::INITIAL)
cond.wait(mutex);
break;
case State::CONNECTING:
@@ -188,8 +190,6 @@ private:
error.Set(last_error);
return false;
}
cond.wait(mutex);
}
}

@@ -63,7 +63,7 @@ protected:
}
public:
bool IsDefined() const {
constexpr bool IsDefined() const {
return last != 0;
}