The assumption that underflow and overbuffering are caused by jitter
(and that the delay between the producer and consumer will be caught up)
does not always hold.
For example, if the consumer does not consume at the expected rate (the
SDL callback is not called often enough, which is an audio output
issue), many samples will be dropped due to overbuffering, decreasing
the average buffering indefinitely.
Prevent the average buffering to become negative to limit the
consequences of an unexpected behavior.
PR #4572 <https://github.com/Genymobile/scrcpy/pull/4572>
The buffering level does not change continuously: it increases abruptly
when a packet is received, and decreases abruptly when an audio block is
consumed.
To estimate the buffering level, a rolling average is used.
To make the buffering more stable, increase the smoothness of this
rolling average. This decreases the risk of enabling audio compensation
due to an estimation error.
PR #4572 <https://github.com/Genymobile/scrcpy/pull/4572>
Use different thresholds for enabling and disabling compensation.
Concretely, enable compensation if the difference between the average
and the target buffering levels exceeds 4 ms (instead of 1 ms). This
avoids unnecessary compensation due to small noise in buffering level
estimation.
But keep a smaller threshold (1 ms) for disabling compensation, so that
the buffering level is restored closer to the target value. This avoids
to keep the actual level close to the compensation threshold.
PR #4572 <https://github.com/Genymobile/scrcpy/pull/4572>
The audio output thread only reads samples from the buffer, and most of
the time, the audio receiver thread only writes samples to the buffer.
In these cases, using atomics avoids lock contention.
There are still corner cases where the audio receiver thread needs to
"read" samples (and drop them), so lock only in these cases.
PR #4572 <https://github.com/Genymobile/scrcpy/pull/4572>
On some systems, the SDL audio callback is not called frequently enough
(for example it requests 5ms of samples every 10ms), because the output
buffer is too small.
By default, we want to use a small value (5ms) to minimize latency and
buffer underrun, but if it does not work well, users need a way to
increase it.
Refs #3793 <https://github.com/Genymobile/scrcpy/issues/3793>
An int was compared with an unsigned:
../app/src/audio_player.c:290:27: warning: comparison of integers of
different signs: 'int' and 'unsigned int' [-Wsign-compare]
if (abs(diff) < ap->sample_rate / 1000) {
~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~
On buffer underflow, the average buffering must be updated, but it is
intended to be accessed only from the receiver thread.
Make the player and the receiver thread communicate the underflow via a
new field (ap->underflow).
Play the decoded audio using SDL.
The audio player frame sink receives the audio frames, resample them
and write them to a byte buffer (introduced by this commit).
On SDL audio callback (from an internal SDL thread), copy samples from
this byte buffer to the SDL audio buffer.
The byte buffer is protected by the SDL_AudioDeviceLock(), but it has
been designed so that the producer and the consumer may write and read
in parallel, provided that they don't access the same slices of the
ring-buffer buffer.
PR #3757 <https://github.com/Genymobile/scrcpy/pull/3757>
Co-authored-by: Simon Chan <1330321+yume-chan@users.noreply.github.com>