audio: alsa_utils: increase start threshold to prevent underruns

* For some reason underruns are often observed when using the default
  parameters. Workaround this by increasing start threshold by 20 ms
  (i.e. at 48000Hz there's 960 additional frames in the ring buffer before
  the playback starts). Buffer size also needs to be increased to allow
  larger start threshold. Balance between avoiding underruns and adding
  latency.
* Add logging for the ALSA parameters.
This commit is contained in:
Konsta
2025-10-28 11:52:10 +02:00
parent 3ca268d760
commit fabdcf5490

View File

@@ -32,7 +32,8 @@
#include "include/alsa_logging.h"
#define DEFAULT_PERIOD_SIZE 1024
#define DEFAULT_PERIOD_SIZE 1024
#define EXTRA_START_THRESHOLD 960 // 20ms
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -243,15 +244,17 @@ int hdmi_proxy_open(alsa_device_proxy * proxy)
snd_pcm_hw_params_set_format(pcm, hwp, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_rate(pcm, hwp, proxy->alsa_config.rate, 0);
snd_pcm_hw_params_set_channels(pcm, hwp, proxy->alsa_config.channels);
snd_pcm_hw_params_set_periods(pcm, hwp, proxy->alsa_config.period_count, 0);
snd_pcm_hw_params_set_periods(pcm, hwp, proxy->alsa_config.period_count * 2, 0);
snd_pcm_hw_params_set_period_size(pcm, hwp, proxy->alsa_config.period_size, 0);
snd_pcm_hw_params_set_buffer_size(pcm, hwp, proxy->alsa_config.period_count * proxy->alsa_config.period_size);
snd_pcm_hw_params_set_buffer_size(pcm, hwp,
proxy->alsa_config.period_count * proxy->alsa_config.period_size * 2);
snd_pcm_hw_params(pcm, hwp);
snd_pcm_sw_params_t *swp;
snd_pcm_sw_params_alloca(&swp);
snd_pcm_sw_params_current(pcm, swp);
snd_pcm_sw_params_set_start_threshold(pcm, swp, proxy->alsa_config.period_count * proxy->alsa_config.period_size);
snd_pcm_sw_params_set_start_threshold(pcm, swp,
proxy->alsa_config.period_count * proxy->alsa_config.period_size + EXTRA_START_THRESHOLD);
snd_pcm_sw_params(pcm, swp);
if ((err = snd_pcm_prepare(pcm)) < 0) {
@@ -261,6 +264,22 @@ int hdmi_proxy_open(alsa_device_proxy * proxy)
return -ENOMEM;
}
unsigned int rate, channels, periods;
snd_pcm_uframes_t period_size, buffer_size, avail_min, start_threshold, stop_threshold, silence_threshold;
snd_pcm_hw_params_get_rate(hwp, &rate, 0);
snd_pcm_hw_params_get_channels(hwp, &channels);
snd_pcm_hw_params_get_periods(hwp, &periods, 0);
snd_pcm_hw_params_get_period_size(hwp, &period_size, 0);
snd_pcm_hw_params_get_buffer_size(hwp, &buffer_size);
snd_pcm_sw_params_get_avail_min(swp, &avail_min);
snd_pcm_sw_params_get_start_threshold(swp, &start_threshold);
snd_pcm_sw_params_get_stop_threshold(swp, &stop_threshold);
snd_pcm_sw_params_get_silence_threshold(swp, &silence_threshold);
ALOGI("rate %i, channels %i", rate, channels);
ALOGI("periods %i, period_size %lu, buffer_size %lu", periods, period_size, buffer_size);
ALOGI("avail_min %lu, start_threshold %lu, stop_threshold %lu, silence_threshold %lu",
avail_min, start_threshold, stop_threshold, silence_threshold);
proxy->pcm_alsa = pcm;
return 0;
@@ -383,7 +402,7 @@ int hdmi_proxy_get_presentation_position(const alsa_device_proxy * proxy,
struct timespec alsaTs;
if (proxy->pcm_alsa != NULL
&& snd_pcm_htimestamp(proxy->pcm_alsa, &avail, &alsaTs) == 0) {
const snd_pcm_uframes_t kernel_buffer_size = proxy->alsa_config.period_count * proxy->alsa_config.period_size;
const snd_pcm_uframes_t kernel_buffer_size = proxy->alsa_config.period_count * proxy->alsa_config.period_size * 2;
if (avail > kernel_buffer_size) {
// pcm_get_htimestamp() computes the available frames by comparing the ALSA driver
// hw_ptr and the appl_ptr levels. In underrun, the hw_ptr may keep running and report
@@ -400,6 +419,7 @@ int hdmi_proxy_get_presentation_position(const alsa_device_proxy * proxy,
// It is possible to compensate for additional driver and device delay
// by changing signed_frames. Example:
// signed_frames -= 20 /* ms */ * proxy->alsa_config.rate / 1000;
signed_frames -= EXTRA_START_THRESHOLD;
if (signed_frames >= 0) {
*frames = signed_frames;
ret = 0;