Use local mutex for audio player

Replace SDL_LockAudioDevice() by a local mutex, to minimize the lock
section and to make the code independent of SDL.
This commit is contained in:
Romain Vimont 2024-09-23 22:39:45 +02:00
parent 2e7a15a998
commit 42fb947780
2 changed files with 23 additions and 8 deletions

View File

@ -66,8 +66,6 @@ static void SDLCALL
sc_audio_player_sdl_callback(void *userdata, uint8_t *stream, int len_int) { sc_audio_player_sdl_callback(void *userdata, uint8_t *stream, int len_int) {
struct sc_audio_player *ap = userdata; struct sc_audio_player *ap = userdata;
// This callback is called with the lock used by SDL_LockAudioDevice()
assert(len_int > 0); assert(len_int > 0);
size_t len = len_int; size_t len = len_int;
uint32_t count = TO_SAMPLES(len); uint32_t count = TO_SAMPLES(len);
@ -76,6 +74,10 @@ sc_audio_player_sdl_callback(void *userdata, uint8_t *stream, int len_int) {
LOGD("[Audio] SDL callback requests %" PRIu32 " samples", count); LOGD("[Audio] SDL callback requests %" PRIu32 " samples", count);
#endif #endif
// A lock is necessary in the rare case where the producer needs to drop
// samples already pushed (when the buffer is full)
sc_mutex_lock(&ap->mutex);
bool played = atomic_load_explicit(&ap->played, memory_order_relaxed); bool played = atomic_load_explicit(&ap->played, memory_order_relaxed);
if (!played) { if (!played) {
uint32_t buffered_samples = sc_audiobuf_can_read(&ap->buf); uint32_t buffered_samples = sc_audiobuf_can_read(&ap->buf);
@ -88,12 +90,15 @@ sc_audio_player_sdl_callback(void *userdata, uint8_t *stream, int len_int) {
// whole buffer with silence (len is small compared to the // whole buffer with silence (len is small compared to the
// arbitrary margin value). // arbitrary margin value).
memset(stream, 0, len); memset(stream, 0, len);
sc_mutex_unlock(&ap->mutex);
return; return;
} }
} }
uint32_t read = sc_audiobuf_read(&ap->buf, stream, count); uint32_t read = sc_audiobuf_read(&ap->buf, stream, count);
sc_mutex_unlock(&ap->mutex);
if (read < count) { if (read < count) {
uint32_t silence = count - read; uint32_t silence = count - read;
// Insert silence. In theory, the inserted silent samples replace the // Insert silence. In theory, the inserted silent samples replace the
@ -183,7 +188,7 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
// All samples that could be written without locking have been written, // All samples that could be written without locking have been written,
// now we need to lock to drop/consume old samples // now we need to lock to drop/consume old samples
SDL_LockAudioDevice(ap->device); sc_mutex_lock(&ap->mutex);
// Retry with the lock // Retry with the lock
written += sc_audiobuf_write(&ap->buf, written += sc_audiobuf_write(&ap->buf,
@ -196,7 +201,7 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
assert(skipped_samples == remaining); assert(skipped_samples == remaining);
} }
SDL_UnlockAudioDevice(ap->device); sc_mutex_unlock(&ap->mutex);
if (written < samples) { if (written < samples) {
// Now there is enough space // Now there is enough space
@ -229,7 +234,7 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
if (can_read > max_buffered_samples) { if (can_read > max_buffered_samples) {
uint32_t skip_samples = 0; uint32_t skip_samples = 0;
SDL_LockAudioDevice(ap->device); sc_mutex_lock(&ap->mutex);
can_read = sc_audiobuf_can_read(&ap->buf); can_read = sc_audiobuf_can_read(&ap->buf);
if (can_read > max_buffered_samples) { if (can_read > max_buffered_samples) {
skip_samples = can_read - max_buffered_samples; skip_samples = can_read - max_buffered_samples;
@ -238,7 +243,7 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
(void) r; (void) r;
skipped_samples += skip_samples; skipped_samples += skip_samples;
} }
SDL_UnlockAudioDevice(ap->device); sc_mutex_unlock(&ap->mutex);
if (skip_samples) { if (skip_samples) {
if (played) { if (played) {
@ -411,12 +416,17 @@ sc_audio_player_frame_sink_open(struct sc_frame_sink *sink,
// without locking. // without locking.
uint32_t audiobuf_samples = ap->target_buffering + ap->sample_rate; uint32_t audiobuf_samples = ap->target_buffering + ap->sample_rate;
size_t sample_size = nb_channels * out_bytes_per_sample; bool ok = sc_mutex_init(&ap->mutex);
bool ok = sc_audiobuf_init(&ap->buf, sample_size, audiobuf_samples);
if (!ok) { if (!ok) {
goto error_free_swr_ctx; goto error_free_swr_ctx;
} }
size_t sample_size = nb_channels * out_bytes_per_sample;
ok = sc_audiobuf_init(&ap->buf, sample_size, audiobuf_samples);
if (!ok) {
goto error_destroy_mutex;
}
size_t initial_swr_buf_size = TO_BYTES(4096); size_t initial_swr_buf_size = TO_BYTES(4096);
ap->swr_buf = malloc(initial_swr_buf_size); ap->swr_buf = malloc(initial_swr_buf_size);
if (!ap->swr_buf) { if (!ap->swr_buf) {
@ -450,6 +460,8 @@ sc_audio_player_frame_sink_open(struct sc_frame_sink *sink,
error_destroy_audiobuf: error_destroy_audiobuf:
sc_audiobuf_destroy(&ap->buf); sc_audiobuf_destroy(&ap->buf);
error_destroy_mutex:
sc_mutex_destroy(&ap->mutex);
error_free_swr_ctx: error_free_swr_ctx:
swr_free(&ap->swr_ctx); swr_free(&ap->swr_ctx);
error_close_audio_device: error_close_audio_device:
@ -468,6 +480,7 @@ sc_audio_player_frame_sink_close(struct sc_frame_sink *sink) {
free(ap->swr_buf); free(ap->swr_buf);
sc_audiobuf_destroy(&ap->buf); sc_audiobuf_destroy(&ap->buf);
sc_mutex_destroy(&ap->mutex);
swr_free(&ap->swr_ctx); swr_free(&ap->swr_ctx);
} }

View File

@ -20,6 +20,8 @@ struct sc_audio_player {
SDL_AudioDeviceID device; SDL_AudioDeviceID device;
sc_mutex mutex;
// The target buffering between the producer and the consumer. This value // The target buffering between the producer and the consumer. This value
// is directly use for compensation. // is directly use for compensation.
// Since audio capture and/or encoding on the device typically produce // Since audio capture and/or encoding on the device typically produce