[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:
parent
5f3578ec65
commit
8b2c68af0e
|
@ -52,6 +52,7 @@ static uint64_t pcm_start_pos;
|
||||||
static int pcm_last_error;
|
static int pcm_last_error;
|
||||||
static int pcm_recovery;
|
static int pcm_recovery;
|
||||||
static int pcm_buf_threshold;
|
static int pcm_buf_threshold;
|
||||||
|
static int pcm_period_size;
|
||||||
|
|
||||||
static struct pcm_packet *pcm_pkt_head;
|
static struct pcm_packet *pcm_pkt_head;
|
||||||
static struct pcm_packet *pcm_pkt_tail;
|
static struct pcm_packet *pcm_pkt_tail;
|
||||||
|
@ -213,7 +214,7 @@ laudio_alsa_write(uint8_t *buf, uint64_t rtptime)
|
||||||
|
|
||||||
return;
|
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 */
|
/* Kill threshold */
|
||||||
ret = laudio_alsa_set_start_threshold(0);
|
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 */
|
/* Don't let ALSA fill up the buffer too much */
|
||||||
// Disabled - seems to cause buffer underruns
|
if (nsamp == AIRTUNES_V2_PACKET_SAMPLES)
|
||||||
// if (nsamp == AIRTUNES_V2_PACKET_SAMPLES)
|
return;
|
||||||
// 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, "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 */
|
/* Make pcm_pos the rtptime of the packet containing cur_pos */
|
||||||
pcm_pos = next_pkt;
|
pcm_pos = next_pkt;
|
||||||
while (pcm_pos > cur_pos)
|
while (pcm_pos > cur_pos)
|
||||||
pcm_pos -= AIRTUNES_V2_PACKET_SAMPLES;
|
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() */
|
/* Compensate period size, otherwise get_pos won't be correct */
|
||||||
//pcm_pos += pcm_buf_threshold;
|
pcm_pos += pcm_period_size;
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_LAUDIO, "PCM pos %" PRIu64 ", start pos %" PRIu64 "\n", pcm_pos, pcm_start_pos);
|
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_last_error = 0;
|
||||||
pcm_recovery = 0;
|
pcm_recovery = 0;
|
||||||
|
|
||||||
|
// alsa doesn't actually seem to wait for this threshold?
|
||||||
ret = laudio_alsa_set_start_threshold(pcm_buf_threshold);
|
ret = laudio_alsa_set_start_threshold(pcm_buf_threshold);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
|
@ -532,6 +532,7 @@ laudio_alsa_open(void)
|
||||||
{
|
{
|
||||||
snd_pcm_hw_params_t *hw_params;
|
snd_pcm_hw_params_t *hw_params;
|
||||||
snd_pcm_uframes_t bufsize;
|
snd_pcm_uframes_t bufsize;
|
||||||
|
snd_pcm_uframes_t period_size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
hw_params = NULL;
|
hw_params = NULL;
|
||||||
|
@ -611,7 +612,24 @@ laudio_alsa_open(void)
|
||||||
goto out_fail;
|
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);
|
ret = snd_pcm_hw_params(hdl, hw_params);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -627,7 +645,8 @@ laudio_alsa_open(void)
|
||||||
pcm_pos = 0;
|
pcm_pos = 0;
|
||||||
pcm_last_error = 0;
|
pcm_last_error = 0;
|
||||||
pcm_recovery = 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();
|
ret = mixer_open();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
|
Loading…
Reference in New Issue