Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8bfdb4ed0c | ||
![]() |
70bd35abe2 | ||
![]() |
0efb67b51e | ||
![]() |
54ebf2a699 | ||
![]() |
d0119548c1 | ||
![]() |
95ac6071b9 | ||
![]() |
3a4e667078 | ||
![]() |
ce18c36ed9 | ||
![]() |
8e39cf62e7 | ||
![]() |
a9e351e00d | ||
![]() |
d65841a2db |
13
NEWS
13
NEWS
@@ -1,3 +1,16 @@
|
||||
ver 0.18.11 (2014/05/12)
|
||||
* decoder
|
||||
- opus: fix missing song length on high-latency files
|
||||
* fix race condition when using GLib event loop (non-Linux)
|
||||
|
||||
ver 0.18.10 (2014/04/10)
|
||||
* decoder
|
||||
- ffmpeg: fix seeking bug
|
||||
- ffmpeg: handle unknown stream start time
|
||||
- gme: fix memory leak
|
||||
- sndfile: work around libsndfile bug on partial read
|
||||
* don't interrupt playback when current song gets deleted
|
||||
|
||||
ver 0.18.9 (2014/03/02)
|
||||
* protocol
|
||||
- "findadd" requires the "add" permission
|
||||
|
@@ -1,6 +1,6 @@
|
||||
AC_PREREQ(2.60)
|
||||
|
||||
AC_INIT(mpd, 0.18.9, mpd-devel@musicpd.org)
|
||||
AC_INIT(mpd, 0.18.11, mpd-devel@musicpd.org)
|
||||
|
||||
VERSION_MAJOR=0
|
||||
VERSION_MINOR=18
|
||||
|
@@ -234,12 +234,8 @@ playlist::DeleteInternal(PlayerControl &pc,
|
||||
if (playing && current == (int)songOrder) {
|
||||
const bool paused = pc.GetState() == PlayerState::PAUSE;
|
||||
|
||||
/* the current song is going to be deleted: stop the player */
|
||||
|
||||
pc.Stop();
|
||||
playing = false;
|
||||
|
||||
/* see which song is going to be played instead */
|
||||
/* the current song is going to be deleted: see which
|
||||
song is going to be played instead */
|
||||
|
||||
current = queue.GetNextOrder(current);
|
||||
if (current == (int)songOrder)
|
||||
@@ -248,10 +244,12 @@ playlist::DeleteInternal(PlayerControl &pc,
|
||||
if (current >= 0 && !paused)
|
||||
/* play the song after the deleted one */
|
||||
PlayOrder(pc, current);
|
||||
else
|
||||
/* no songs left to play, stop playback
|
||||
completely */
|
||||
Stop(pc);
|
||||
else {
|
||||
/* stop the player */
|
||||
|
||||
pc.Stop();
|
||||
playing = false;
|
||||
}
|
||||
|
||||
*queued_p = nullptr;
|
||||
} else if (current == (int)songOrder)
|
||||
|
@@ -197,6 +197,29 @@ time_to_ffmpeg(double t, const AVRational time_base)
|
||||
time_base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace #AV_NOPTS_VALUE with the given fallback.
|
||||
*/
|
||||
static constexpr int64_t
|
||||
timestamp_fallback(int64_t t, int64_t fallback)
|
||||
{
|
||||
return gcc_likely(t != int64_t(AV_NOPTS_VALUE))
|
||||
? t
|
||||
: fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for AVStream::start_time that replaces AV_NOPTS_VALUE with
|
||||
* zero. We can't use AV_NOPTS_VALUE in calculations, and we simply
|
||||
* assume that the stream's start time is zero, which appears to be
|
||||
* the best way out of that situation.
|
||||
*/
|
||||
static int64_t
|
||||
start_time_fallback(const AVStream &stream)
|
||||
{
|
||||
return timestamp_fallback(stream.start_time, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
copy_interleave_frame2(uint8_t *dest, uint8_t **src,
|
||||
unsigned nframes, unsigned nchannels,
|
||||
@@ -263,7 +286,7 @@ ffmpeg_send_packet(Decoder &decoder, InputStream &is,
|
||||
{
|
||||
if (packet->pts >= 0 && packet->pts != (int64_t)AV_NOPTS_VALUE)
|
||||
decoder_timestamp(decoder,
|
||||
time_from_ffmpeg(packet->pts - stream->start_time,
|
||||
time_from_ffmpeg(packet->pts - start_time_fallback(*stream),
|
||||
stream->time_base));
|
||||
|
||||
AVPacket packet2 = *packet;
|
||||
@@ -493,10 +516,10 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
|
||||
int64_t where =
|
||||
time_to_ffmpeg(decoder_seek_where(decoder),
|
||||
av_stream->time_base) +
|
||||
av_stream->start_time;
|
||||
start_time_fallback(*av_stream);
|
||||
|
||||
if (av_seek_frame(format_context, audio_stream, where,
|
||||
AV_TIME_BASE) < 0)
|
||||
AVSEEK_FLAG_ANY) < 0)
|
||||
decoder_seek_error(decoder);
|
||||
else {
|
||||
avcodec_flush_buffers(codec_context);
|
||||
|
@@ -117,6 +117,7 @@ gme_container_scan(const char *path_fs, const unsigned int tnum)
|
||||
}
|
||||
|
||||
const unsigned num_songs = gme_track_count(emu);
|
||||
gme_delete(emu);
|
||||
/* if it only contains a single tune, don't treat as container */
|
||||
if (num_songs < 2)
|
||||
return nullptr;
|
||||
|
@@ -81,7 +81,7 @@ bool
|
||||
OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
|
||||
Decoder *decoder, InputStream &input_stream)
|
||||
{
|
||||
size_t remaining_skipped = 16384;
|
||||
size_t remaining_skipped = 32768;
|
||||
|
||||
while (true) {
|
||||
int r = ogg_sync_pageseek(&oy, &page);
|
||||
|
@@ -55,14 +55,28 @@ sndfile_vio_read(void *ptr, sf_count_t count, void *user_data)
|
||||
{
|
||||
InputStream &is = *(InputStream *)user_data;
|
||||
|
||||
sf_count_t total_bytes = 0;
|
||||
Error error;
|
||||
size_t nbytes = is.LockRead(ptr, count, error);
|
||||
if (nbytes == 0 && error.IsDefined()) {
|
||||
LogError(error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
/* this loop is necessary because libsndfile chokes on partial
|
||||
reads */
|
||||
|
||||
do {
|
||||
size_t nbytes = is.LockRead((char *)ptr + total_bytes,
|
||||
count - total_bytes, error);
|
||||
if (nbytes == 0) {
|
||||
if (error.IsDefined()) {
|
||||
LogError(error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
total_bytes += nbytes;
|
||||
} while (total_bytes < count);
|
||||
|
||||
return total_bytes;
|
||||
}
|
||||
|
||||
static sf_count_t
|
||||
|
@@ -27,9 +27,11 @@ DeferredMonitor::Cancel()
|
||||
#ifdef USE_EPOLL
|
||||
pending = false;
|
||||
#else
|
||||
const auto id = source_id.exchange(0);
|
||||
if (id != 0)
|
||||
g_source_remove(id);
|
||||
const ScopeLock protect(mutex);
|
||||
if (source_id != 0) {
|
||||
g_source_remove(source_id);
|
||||
source_id = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -40,10 +42,9 @@ DeferredMonitor::Schedule()
|
||||
if (!pending.exchange(true))
|
||||
fd.Write();
|
||||
#else
|
||||
const unsigned id = loop.AddIdle(Callback, this);
|
||||
const auto old_id = source_id.exchange(id);
|
||||
if (old_id != 0)
|
||||
g_source_remove(old_id);
|
||||
const ScopeLock protect(mutex);
|
||||
if (source_id == 0)
|
||||
source_id = loop.AddIdle(Callback, this);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -65,9 +66,16 @@ DeferredMonitor::OnSocketReady(unsigned)
|
||||
void
|
||||
DeferredMonitor::Run()
|
||||
{
|
||||
const auto id = source_id.exchange(0);
|
||||
if (id != 0)
|
||||
RunDeferred();
|
||||
{
|
||||
const ScopeLock protect(mutex);
|
||||
if (source_id == 0)
|
||||
/* cancelled */
|
||||
return;
|
||||
|
||||
source_id = 0;
|
||||
}
|
||||
|
||||
RunDeferred();
|
||||
}
|
||||
|
||||
gboolean
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include "SocketMonitor.hxx"
|
||||
#include "WakeFD.hxx"
|
||||
#else
|
||||
#include "thread/Mutex.hxx"
|
||||
#include <glib.h>
|
||||
#endif
|
||||
|
||||
@@ -48,7 +49,9 @@ class DeferredMonitor
|
||||
#else
|
||||
EventLoop &loop;
|
||||
|
||||
std::atomic<guint> source_id;
|
||||
Mutex mutex;
|
||||
|
||||
guint source_id;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
Reference in New Issue
Block a user