[player] Insert duplicate packets slowly on timer overrun

This commit is contained in:
ejurgensen 2016-04-06 08:18:54 +02:00
parent 084a86597b
commit 857055a18d

View File

@ -80,8 +80,6 @@
#define PLAYER_DEFAULT_VOLUME 50
// Used to keep the player from getting ahead of a rate limited source (see below)
#define PLAYER_TICKS_MAX_OVERRUN 2
// Skips ticks for about 2 secs (seems to bring us back in sync for about 20 min)
#define PLAYER_TICKS_SKIP 126
struct player_source
{
@ -270,6 +268,8 @@ static struct timespec packet_time = { 0, AIRTUNES_V2_STREAM_PERIOD };
// Will be positive if we need to skip some source reads (see below)
static int ticks_skip;
static int debug_counter;
/* Sync source */
static enum player_sync_source pb_sync_source;
@ -298,7 +298,10 @@ static struct player_source *cur_playing;
static struct player_source *cur_streaming;
static uint32_t cur_plid;
static uint32_t cur_plversion;
static struct evbuffer *audio_buf;
static uint8_t rawbuf[STOB(AIRTUNES_V2_PACKET_SAMPLES)];
/* Play queue */
static struct queue *queue;
@ -1207,6 +1210,9 @@ source_play()
ret = stream_play(cur_streaming);
ticks_skip = 0;
memset(rawbuf, 0, sizeof(rawbuf));
return ret;
}
@ -1463,11 +1469,9 @@ source_read(uint8_t *buf, int len, uint64_t rtptime)
return nbytes;
}
static void
playback_write(void)
playback_write(int read_skip)
{
uint8_t rawbuf[STOB(AIRTUNES_V2_PACKET_SAMPLES)];
int ret;
source_check();
@ -1478,16 +1482,19 @@ playback_write(void)
last_rtptime += AIRTUNES_V2_PACKET_SAMPLES;
memset(rawbuf, 0, sizeof(rawbuf));
ret = source_read(rawbuf, sizeof(rawbuf), last_rtptime);
if (ret < 0)
if (!read_skip)
{
DPRINTF(E_DBG, L_PLAYER, "Error reading from source, aborting playback\n");
ret = source_read(rawbuf, sizeof(rawbuf), last_rtptime);
if (ret < 0)
{
DPRINTF(E_DBG, L_PLAYER, "Error reading from source, aborting playback\n");
playback_abort();
return;
playback_abort();
return;
}
}
else
DPRINTF(E_SPAM, L_PLAYER, "Skipping read\n");
outputs_write(rawbuf, last_rtptime);
}
@ -1498,6 +1505,8 @@ player_playback_cb(int fd, short what, void *arg)
struct timespec next_tick;
uint64_t overrun;
int ret;
int skip;
int skip_first;
// Check if we missed any timer expirations
overrun = 0;
@ -1515,6 +1524,14 @@ player_playback_cb(int fd, short what, void *arg)
overrun = ret;
#endif /* __linux__ */
/*debug_counter++;
if (debug_counter % 2000 == 0)
{
DPRINTF(E_LOG, L_PLAYER, "Sleep a bit!!\n");
usleep(5 * AIRTUNES_V2_STREAM_PERIOD / 1000);
DPRINTF(E_LOG, L_PLAYER, "Wake again\n");
}*/
// The reason we get behind the playback timer may be that we are playing a
// network stream OR that the source is slow to open OR some interruption.
// For streams, we might be consuming faster than the stream delivers, so
@ -1523,24 +1540,28 @@ player_playback_cb(int fd, short what, void *arg)
// from the stream server.
//
// Our strategy to catch up with the timer depends on the source:
// - streams: We will skip reading data every second tick until we have
// skipt PLAYER_TICKS_SKIP ticks. That should make the source
// catch up. RTP destinations should be able to handle this
// gracefully if we just give them an rtptime that lets them know
// that some packets were "lost".
// - streams: We will skip reading data every second until we have countered
// the overrun by skipping reads for a number of ticks that is
// 3 times the overrun. That should make the source catch up. To
// keep the output happy we resend the previous rawbuf when we
// have skipped a read.
// - files: Just read and write like crazy until we have caught up.
skip_first = 0;
if (overrun > PLAYER_TICKS_MAX_OVERRUN)
{
DPRINTF(E_WARN, L_PLAYER, "Behind the playback timer with %" PRIu64 " ticks, initiating catch up\n", overrun);
if (cur_streaming->data_kind == DATA_KIND_HTTP || cur_streaming->data_kind == DATA_KIND_PIPE)
ticks_skip = 2 * PLAYER_TICKS_SKIP + 1;
{
ticks_skip = 3 * overrun;
// We always skip after a timer overrun, since another read will
// probably just give another time overrun
skip_first = 1;
}
else
ticks_skip = 0;
}
else if (ticks_skip > 0)
ticks_skip--;
// Decide how many packets to send
next_tick = timespec_add(pb_timer_last, tick_interval);
@ -1549,11 +1570,13 @@ player_playback_cb(int fd, short what, void *arg)
do
{
// Skip reading and writing every second tick if we are behind a nonfile source
if (ticks_skip % 2 == 0)
playback_write();
else
last_rtptime += AIRTUNES_V2_PACKET_SAMPLES;
skip = skip_first || ((ticks_skip > 0) && ((last_rtptime / AIRTUNES_V2_PACKET_SAMPLES) % 126 == 0));
playback_write(skip);
skip_first = 0;
if (skip)
ticks_skip--;
packet_timer_last = timespec_add(packet_timer_last, packet_time);
}