[speaker_source] Account for playback callbacks during speaker writes (#19499)

Co-authored-by: n-IA-hane <n-IA-hane@users.noreply.github.com>
This commit is contained in:
n-IA-hane
2026-09-29 10:36:37 -04:00
committed by GitHub
co-authored by n-IA-hane
parent 757a1c42f9
commit 627832f7ba
2 changed files with 13 additions and 4 deletions
@@ -196,10 +196,19 @@ size_t SpeakerSourceMediaPlayer::handle_media_output_(uint8_t pipeline, media_so
vTaskDelay(pdMS_TO_TICKS(timeout_ms));
return 0;
}
// Reserve frames before the speaker can play them and report their progress,
// then release the portion it did not accept.
const uint32_t reserved_frames = stream_info.bytes_to_frames(length);
ps.pending_frames.fetch_add(reserved_frames, std::memory_order_relaxed);
size_t bytes_written = ps.speaker->play(data, length, pdMS_TO_TICKS(timeout_ms));
if (bytes_written > 0) {
// Track frames sent to speaker for this source
ps.pending_frames.fetch_add(stream_info.bytes_to_frames(bytes_written), std::memory_order_relaxed);
const uint32_t unused_frames = reserved_frames - stream_info.bytes_to_frames(bytes_written);
if (unused_frames > 0) {
// The main loop may reset pending_frames while play() is blocked.
// Clamp the release so that a concurrent reset cannot cause underflow.
uint32_t pending = ps.pending_frames.load(std::memory_order_relaxed);
while (!ps.pending_frames.compare_exchange_weak(pending, pending - std::min(pending, unused_frames),
std::memory_order_relaxed)) {
}
}
return bytes_written;
}
@@ -4,4 +4,4 @@ substitutions:
packages:
i2s_audio: !include ../../test_build_components/common/i2s_audio/esp32-idf.yaml
<<: !include common.yaml
speaker_source: !include common.yaml