[alsa] Try to fix issue where devices with small buffers would

overrun, rendering the device unusable because snd_pcm_writei()
starts blocking. This is just a poor temporary fix until we
either start using SND_PCM_NONBLOCK or put alsa in it's own thread
or implement Pulseaudio...
This commit is contained in:
ejurgensen 2016-03-16 22:54:27 +01:00
parent 5f3578ec65
commit 8b2c68af0e
1 changed files with 29 additions and 10 deletions

View File

@ -52,6 +52,7 @@ static uint64_t pcm_start_pos;
static int pcm_last_error;
static int pcm_recovery;
static int pcm_buf_threshold;
static int pcm_period_size;
static struct pcm_packet *pcm_pkt_head;
static struct pcm_packet *pcm_pkt_tail;
@ -213,7 +214,7 @@ laudio_alsa_write(uint8_t *buf, uint64_t rtptime)
return;
}
else if ((pcm_status != LAUDIO_RUNNING) && (pcm_pos + pcm_buf_threshold >= pcm_start_pos))
else if ((pcm_status != LAUDIO_RUNNING) && (pcm_pos >= pcm_start_pos))
{
/* Kill threshold */
ret = laudio_alsa_set_start_threshold(0);
@ -283,9 +284,8 @@ laudio_alsa_write(uint8_t *buf, uint64_t rtptime)
}
/* Don't let ALSA fill up the buffer too much */
// Disabled - seems to cause buffer underruns
// if (nsamp == AIRTUNES_V2_PACKET_SAMPLES)
// return;
if (nsamp == AIRTUNES_V2_PACKET_SAMPLES)
return;
}
}
@ -358,17 +358,16 @@ laudio_alsa_start(uint64_t cur_pos, uint64_t next_pkt)
}
DPRINTF(E_DBG, L_LAUDIO, "Start local audio curpos %" PRIu64 ", next_pkt %" PRIu64 "\n", cur_pos, next_pkt);
DPRINTF(E_DBG, L_LAUDIO, "PCM will start after %d samples (%d packets)\n", pcm_buf_threshold, pcm_buf_threshold / AIRTUNES_V2_PACKET_SAMPLES);
/* Make pcm_pos the rtptime of the packet containing cur_pos */
pcm_pos = next_pkt;
while (pcm_pos > cur_pos)
pcm_pos -= AIRTUNES_V2_PACKET_SAMPLES;
pcm_start_pos = next_pkt + pcm_buf_threshold;
pcm_start_pos = next_pkt + pcm_period_size;
/* Compensate threshold, as it's taken into account by snd_pcm_delay() */
//pcm_pos += pcm_buf_threshold;
/* Compensate period size, otherwise get_pos won't be correct */
pcm_pos += pcm_period_size;
DPRINTF(E_DBG, L_LAUDIO, "PCM pos %" PRIu64 ", start pos %" PRIu64 "\n", pcm_pos, pcm_start_pos);
@ -378,6 +377,7 @@ laudio_alsa_start(uint64_t cur_pos, uint64_t next_pkt)
pcm_last_error = 0;
pcm_recovery = 0;
// alsa doesn't actually seem to wait for this threshold?
ret = laudio_alsa_set_start_threshold(pcm_buf_threshold);
if (ret < 0)
{
@ -532,6 +532,7 @@ laudio_alsa_open(void)
{
snd_pcm_hw_params_t *hw_params;
snd_pcm_uframes_t bufsize;
snd_pcm_uframes_t period_size;
int ret;
hw_params = NULL;
@ -611,7 +612,24 @@ laudio_alsa_open(void)
goto out_fail;
}
DPRINTF(E_DBG, L_LAUDIO, "Buffer size is %lu samples\n", bufsize);
// With a small period size we seem to get underruns because the period time
// passes before we manage to feed with samples (if the player is slightly
// behind - especially critical during startup when the buffer is low)
// Internet suggests period_size should be /2 bufsize, but default seems to be
// much lower, so compromise on /4 (but not more than 65536 frames = almost 2 sec).
period_size = bufsize / 4;
if (period_size > 65536)
period_size = 65536;
ret = snd_pcm_hw_params_set_period_size_near(hdl, hw_params, &period_size, NULL);
if (ret < 0)
{
DPRINTF(E_LOG, L_LAUDIO, "Could not set period size: %s\n", snd_strerror(ret));
goto out_fail;
}
DPRINTF(E_DBG, L_LAUDIO, "Buffer size is %lu samples, period size is %lu samples\n", bufsize, period_size);
ret = snd_pcm_hw_params(hdl, hw_params);
if (ret < 0)
@ -627,7 +645,8 @@ laudio_alsa_open(void)
pcm_pos = 0;
pcm_last_error = 0;
pcm_recovery = 0;
pcm_buf_threshold = (bufsize / AIRTUNES_V2_PACKET_SAMPLES) * AIRTUNES_V2_PACKET_SAMPLES;
pcm_buf_threshold = ((bufsize - period_size) / AIRTUNES_V2_PACKET_SAMPLES) * AIRTUNES_V2_PACKET_SAMPLES;
pcm_period_size = period_size;
ret = mixer_open();
if (ret < 0)