mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-15 16:53:18 -05:00
[player] Insert duplicate packets slowly on timer overrun
This commit is contained in:
parent
084a86597b
commit
857055a18d
63
src/player.c
63
src/player.c
@ -80,8 +80,6 @@
|
|||||||
#define PLAYER_DEFAULT_VOLUME 50
|
#define PLAYER_DEFAULT_VOLUME 50
|
||||||
// Used to keep the player from getting ahead of a rate limited source (see below)
|
// Used to keep the player from getting ahead of a rate limited source (see below)
|
||||||
#define PLAYER_TICKS_MAX_OVERRUN 2
|
#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
|
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)
|
// Will be positive if we need to skip some source reads (see below)
|
||||||
static int ticks_skip;
|
static int ticks_skip;
|
||||||
|
|
||||||
|
static int debug_counter;
|
||||||
|
|
||||||
/* Sync source */
|
/* Sync source */
|
||||||
static enum player_sync_source pb_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 struct player_source *cur_streaming;
|
||||||
static uint32_t cur_plid;
|
static uint32_t cur_plid;
|
||||||
static uint32_t cur_plversion;
|
static uint32_t cur_plversion;
|
||||||
|
|
||||||
static struct evbuffer *audio_buf;
|
static struct evbuffer *audio_buf;
|
||||||
|
static uint8_t rawbuf[STOB(AIRTUNES_V2_PACKET_SAMPLES)];
|
||||||
|
|
||||||
|
|
||||||
/* Play queue */
|
/* Play queue */
|
||||||
static struct queue *queue;
|
static struct queue *queue;
|
||||||
@ -1207,6 +1210,9 @@ source_play()
|
|||||||
|
|
||||||
ret = stream_play(cur_streaming);
|
ret = stream_play(cur_streaming);
|
||||||
|
|
||||||
|
ticks_skip = 0;
|
||||||
|
memset(rawbuf, 0, sizeof(rawbuf));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1463,11 +1469,9 @@ source_read(uint8_t *buf, int len, uint64_t rtptime)
|
|||||||
return nbytes;
|
return nbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
playback_write(void)
|
playback_write(int read_skip)
|
||||||
{
|
{
|
||||||
uint8_t rawbuf[STOB(AIRTUNES_V2_PACKET_SAMPLES)];
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
source_check();
|
source_check();
|
||||||
@ -1478,8 +1482,8 @@ playback_write(void)
|
|||||||
|
|
||||||
last_rtptime += AIRTUNES_V2_PACKET_SAMPLES;
|
last_rtptime += AIRTUNES_V2_PACKET_SAMPLES;
|
||||||
|
|
||||||
memset(rawbuf, 0, sizeof(rawbuf));
|
if (!read_skip)
|
||||||
|
{
|
||||||
ret = source_read(rawbuf, sizeof(rawbuf), last_rtptime);
|
ret = source_read(rawbuf, sizeof(rawbuf), last_rtptime);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
@ -1488,6 +1492,9 @@ playback_write(void)
|
|||||||
playback_abort();
|
playback_abort();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DPRINTF(E_SPAM, L_PLAYER, "Skipping read\n");
|
||||||
|
|
||||||
outputs_write(rawbuf, last_rtptime);
|
outputs_write(rawbuf, last_rtptime);
|
||||||
}
|
}
|
||||||
@ -1498,6 +1505,8 @@ player_playback_cb(int fd, short what, void *arg)
|
|||||||
struct timespec next_tick;
|
struct timespec next_tick;
|
||||||
uint64_t overrun;
|
uint64_t overrun;
|
||||||
int ret;
|
int ret;
|
||||||
|
int skip;
|
||||||
|
int skip_first;
|
||||||
|
|
||||||
// Check if we missed any timer expirations
|
// Check if we missed any timer expirations
|
||||||
overrun = 0;
|
overrun = 0;
|
||||||
@ -1515,6 +1524,14 @@ player_playback_cb(int fd, short what, void *arg)
|
|||||||
overrun = ret;
|
overrun = ret;
|
||||||
#endif /* __linux__ */
|
#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
|
// 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.
|
// 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
|
// 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.
|
// from the stream server.
|
||||||
//
|
//
|
||||||
// Our strategy to catch up with the timer depends on the source:
|
// Our strategy to catch up with the timer depends on the source:
|
||||||
// - streams: We will skip reading data every second tick until we have
|
// - streams: We will skip reading data every second until we have countered
|
||||||
// skipt PLAYER_TICKS_SKIP ticks. That should make the source
|
// the overrun by skipping reads for a number of ticks that is
|
||||||
// catch up. RTP destinations should be able to handle this
|
// 3 times the overrun. That should make the source catch up. To
|
||||||
// gracefully if we just give them an rtptime that lets them know
|
// keep the output happy we resend the previous rawbuf when we
|
||||||
// that some packets were "lost".
|
// have skipped a read.
|
||||||
// - files: Just read and write like crazy until we have caught up.
|
// - files: Just read and write like crazy until we have caught up.
|
||||||
|
|
||||||
|
skip_first = 0;
|
||||||
if (overrun > PLAYER_TICKS_MAX_OVERRUN)
|
if (overrun > PLAYER_TICKS_MAX_OVERRUN)
|
||||||
{
|
{
|
||||||
DPRINTF(E_WARN, L_PLAYER, "Behind the playback timer with %" PRIu64 " ticks, initiating catch up\n", 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)
|
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
|
else
|
||||||
ticks_skip = 0;
|
ticks_skip = 0;
|
||||||
}
|
}
|
||||||
else if (ticks_skip > 0)
|
|
||||||
ticks_skip--;
|
|
||||||
|
|
||||||
// Decide how many packets to send
|
// Decide how many packets to send
|
||||||
next_tick = timespec_add(pb_timer_last, tick_interval);
|
next_tick = timespec_add(pb_timer_last, tick_interval);
|
||||||
@ -1549,11 +1570,13 @@ player_playback_cb(int fd, short what, void *arg)
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// Skip reading and writing every second tick if we are behind a nonfile source
|
skip = skip_first || ((ticks_skip > 0) && ((last_rtptime / AIRTUNES_V2_PACKET_SAMPLES) % 126 == 0));
|
||||||
if (ticks_skip % 2 == 0)
|
|
||||||
playback_write();
|
playback_write(skip);
|
||||||
else
|
|
||||||
last_rtptime += AIRTUNES_V2_PACKET_SAMPLES;
|
skip_first = 0;
|
||||||
|
if (skip)
|
||||||
|
ticks_skip--;
|
||||||
|
|
||||||
packet_timer_last = timespec_add(packet_timer_last, packet_time);
|
packet_timer_last = timespec_add(packet_timer_last, packet_time);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user