Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bluealsa-aplay delay reporting improvements #738

Merged
merged 2 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/spellcheck-wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ ATRAC
BlueALSA
BlueZ
Fraunhofer
iPhone
IWYU
LDAC
LHDC
Expand Down
14 changes: 7 additions & 7 deletions doc/bluealsa-aplay.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ OPTIONS

--pcm-buffer-time=INT
Set the playback PCM buffer duration time to *INT* microseconds.
The default is 500000. It is recommended to choose a buffer time that is
The default is 200000. It is recommended to choose a buffer time that is
an exact multiple of the period time to avoid potential issues with some
ALSA plugins (see --pcm-period-time option below).
ALSA may choose the nearest available alternative if the requested value is
Expand All @@ -101,7 +101,7 @@ OPTIONS

--pcm-period-time=INT
Set the playback PCM period duration time to *INT* microseconds.
The default is 100000.
The default is 50000.
ALSA may choose the nearest available alternative if the requested value is
not supported.

Expand Down Expand Up @@ -232,12 +232,12 @@ Instead it will choose its own values, which can lead to rounding errors in the
period size calculation when used with the ALSA `rate` plugin. To avoid this,
it is recommended to explicitly define the hardware period size and buffer size
for dmix in your ALSA configuration. For example, suppose we want a period time
of 100000 µs and a buffer holding 5 periods with an Intel 'PCH' card:
of 50000 µs and a buffer holding 4 periods with an Intel 'PCH' card:

::

defaults.dmix.PCH.period_time 100000
defaults.dmix.PCH.periods 5
defaults.dmix.PCH.period_time 50000
defaults.dmix.PCH.periods 4

Alternatively we can define a PCM with the required setting:

Expand All @@ -250,8 +250,8 @@ Alternatively we can define a PCM with the required setting:
ipc_key 12345
slave {
pcm "hw:0,0"
period_time 100000
periods 5
period_time 50000
periods 4
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions utils/aplay/alsa-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,16 @@ static int alsa_pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t buffer_size,
goto fail;
}

/* start the transfer when the buffer is full (or almost full) */
snd_pcm_uframes_t threshold = (buffer_size / period_size) * period_size;
/* Start the transfer when the buffer is half full - this allows
* spare capacity to accommodate bursts and short breaks in the
* Bluetooth stream. */
snd_pcm_uframes_t threshold = buffer_size / 2;
if ((err = snd_pcm_sw_params_set_start_threshold(pcm, params, threshold)) != 0) {
snprintf(buf, sizeof(buf), "Set start threshold: %s: %lu", snd_strerror(err), threshold);
goto fail;
}

/* allow the transfer when at least period_size samples can be processed */
/* Allow the transfer when at least period_size samples can be processed. */
if ((err = snd_pcm_sw_params_set_avail_min(pcm, params, period_size)) != 0) {
snprintf(buf, sizeof(buf), "Set avail min: %s: %lu", snd_strerror(err), period_size);
goto fail;
Expand Down
20 changes: 16 additions & 4 deletions utils/aplay/aplay.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <string.h>
#include <syslog.h>
#include <sys/eventfd.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/time.h>
#include <unistd.h>
Expand Down Expand Up @@ -86,8 +87,11 @@ static bool ba_profile_a2dp = true;
static bool ba_addr_any = false;
static bdaddr_t *ba_addrs = NULL;
static size_t ba_addrs_count = 0;
static unsigned int pcm_buffer_time = 500000;
static unsigned int pcm_period_time = 100000;
/* Many devices cannot synchronize A/V with very high audio latency. To keep
* the overall latency below 400ms we choose ALSA parameters such that the
* ALSA latency is below 200ms. */
static unsigned int pcm_buffer_time = 200000;
static unsigned int pcm_period_time = 50000;

/* local PCM muted state for software mute */
static bool pcm_muted = false;
Expand Down Expand Up @@ -817,11 +821,19 @@ static void *io_worker_routine(struct io_worker *w) {
ffb_shift(&buffer, frames * w->ba_pcm.channels);

int ret;
if ((ret = snd_pcm_delay(w->snd_pcm,
&pcm_delay_frames[pcm_delay_frames_i++ % ARRAYSIZE(pcm_delay_frames)])) != 0)
snd_pcm_sframes_t delay_frames = 0;
if ((ret = snd_pcm_delay(w->snd_pcm, &delay_frames)) != 0)
warn("Couldn't get PCM delay: %s", snd_strerror(ret));
else {

unsigned int buffered = 0;
ioctl(w->ba_pcm_fd, FIONREAD, &buffered);
buffered += ffb_blen_out(&buffer);
delay_frames += buffered / (w->ba_pcm.channels * pcm_format_size);

pcm_delay_frames[pcm_delay_frames_i % ARRAYSIZE(pcm_delay_frames)] = delay_frames;
pcm_delay_frames_i++;

struct timespec ts_now;
/* Rate limit delay updates to 1 update per second. */
struct timespec ts_delay = { .tv_sec = 1 };
Expand Down