From f15c40aadec0b005e90ff06ac776d5524efd1de8 Mon Sep 17 00:00:00 2001 From: Barbara Wojtarowicz Date: Mon, 24 Aug 2026 16:11:29 +0200 Subject: [PATCH 1/3] fix(AudioPlayer): modify AudioPlayer::resume, expand AudioPlayer::onErrorAfterClose --- .../cpp/audioapi/android/core/AudioPlayer.cpp | 37 +++++++++++++++---- .../cpp/audioapi/android/core/AudioPlayer.h | 2 + 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp index 38371018f..9ddbc8557 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp @@ -88,11 +88,20 @@ bool AudioPlayer::resume() { } if (mStream_ != nullptr) { - auto result = mStream_->requestStart() == oboe::Result::OK; - isRunning_.store(result, std::memory_order_release); - return result; + if (mStream_->requestStart() == oboe::Result::OK) { + isRunning_.store(true, std::memory_order_release); + return true; + } } + if (rebuildStream()) { + if (mStream_ != nullptr && mStream_->requestStart() == oboe::Result::OK) { + isRunning_.store(true, std::memory_order_release); + return true; + } + } + + isRunning_.store(false, std::memory_order_release); return false; } @@ -158,7 +167,8 @@ AudioPlayer::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numF } void AudioPlayer::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result error) { - if (error != oboe::Result::ErrorDisconnected || driverMutex_ == nullptr) { + // error != oboe::Result::ErrorDisconnected condition is deleted to handle more cases of errors + if (driverMutex_ == nullptr) { return; } @@ -176,9 +186,17 @@ void AudioPlayer::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result erro return; } - cleanup(); - if (openAudioStream()) { - resume(); + // Check if the stream was expected to be running when the error occurred + const bool wasRunning = isRunning_.load(std::memory_order_acquire); + + if (!rebuildStream()) { + return; + } + + // Restart the stream if it was expected to be running when the error occurred + if (wasRunning) { + auto result = mStream_->requestStart() == oboe::Result::OK; + isRunning_.store(result, std::memory_order_release); } } @@ -225,4 +243,9 @@ double AudioPlayer::getOutputLatency() const { return minBaseLatency; } + +bool AudioPlayer::rebuildStream() { + cleanup(); + return openAudioStream(); +} } // namespace audioapi diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h index 2f9a4ac01..760fd5c7f 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h @@ -69,6 +69,8 @@ class AudioPlayer : public CommonPlayer, std::weak_ptr context_; bool openAudioStream(); + + bool rebuildStream(); }; } // namespace audioapi From 88e61675d8c5cfc288cfef44b905057abbb200a8 Mon Sep 17 00:00:00 2001 From: Barbara Wojtarowicz Date: Wed, 26 Aug 2026 15:54:02 +0200 Subject: [PATCH 2/3] fix(AudioPlayer): update onErrorAfterClose, delete stream-rebuilding logic from resume --- .../cpp/audioapi/android/core/AudioPlayer.cpp | 70 +++++++++++-------- .../cpp/audioapi/android/core/AudioPlayer.h | 1 - .../common/cpp/audioapi/core/AudioContext.cpp | 2 + 3 files changed, 43 insertions(+), 30 deletions(-) diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp index 9ddbc8557..595b312db 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp @@ -55,21 +55,38 @@ bool AudioPlayer::openAudioStream() { return true; } +bool AudioPlayer::rebuildStream() { + cleanup(); + return openAudioStream(); +} + bool AudioPlayer::start() { std::scoped_lock lock(streamMutex_); - if (!isInitialized_.load(std::memory_order_acquire)) { + + // Ensure a live stream exists. A prior Oboe death can leave isInitialized_ true + // with a closed/null stream, or a stream that rejects requestStart. + if (!isInitialized_.load(std::memory_order_acquire) || mStream_ == nullptr) { + if (isInitialized_.load(std::memory_order_acquire) || mStream_ != nullptr) { + cleanup(); + } if (!openAudioStream()) { return false; } } - if (mStream_ != nullptr) { - auto result = mStream_->requestStart() == oboe::Result::OK; - isRunning_.store(result, std::memory_order_release); - return result; + if (mStream_ != nullptr && mStream_->requestStart() == oboe::Result::OK) { + isRunning_.store(true, std::memory_order_release); + return true; } - return false; + if (!rebuildStream() || mStream_ == nullptr) { + isRunning_.store(false, std::memory_order_release); + return false; + } + + const bool started = mStream_->requestStart() == oboe::Result::OK; + isRunning_.store(started, std::memory_order_release); + return started; } void AudioPlayer::stop() { @@ -88,20 +105,11 @@ bool AudioPlayer::resume() { } if (mStream_ != nullptr) { - if (mStream_->requestStart() == oboe::Result::OK) { - isRunning_.store(true, std::memory_order_release); - return true; - } - } - - if (rebuildStream()) { - if (mStream_ != nullptr && mStream_->requestStart() == oboe::Result::OK) { - isRunning_.store(true, std::memory_order_release); - return true; - } + auto result = mStream_->requestStart() == oboe::Result::OK; + isRunning_.store(result, std::memory_order_release); + return result; } - isRunning_.store(false, std::memory_order_release); return false; } @@ -167,11 +175,19 @@ AudioPlayer::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numF } void AudioPlayer::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result error) { - // error != oboe::Result::ErrorDisconnected condition is deleted to handle more cases of errors if (driverMutex_ == nullptr) { return; } + switch (error) { + case oboe::Result::ErrorDisconnected: + case oboe::Result::ErrorTimeout: + case oboe::Result::ErrorInternal: + break; + default: + return; + } + // Serialize with start()/resume()/suspend()/close() on the JS / promise-pool threads. std::scoped_lock lock(*driverMutex_, streamMutex_); @@ -186,17 +202,18 @@ void AudioPlayer::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result erro return; } - // Check if the stream was expected to be running when the error occurred + // Capture before rebuild: cleanup clears the stream that was expected to run. const bool wasRunning = isRunning_.load(std::memory_order_acquire); if (!rebuildStream()) { + isRunning_.store(false, std::memory_order_release); return; } - // Restart the stream if it was expected to be running when the error occurred - if (wasRunning) { - auto result = mStream_->requestStart() == oboe::Result::OK; - isRunning_.store(result, std::memory_order_release); + // requestStart only — do not call resume() (keeps resume free of rebuild side effects). + if (wasRunning && mStream_ != nullptr) { + const bool started = mStream_->requestStart() == oboe::Result::OK; + isRunning_.store(started, std::memory_order_release); } } @@ -243,9 +260,4 @@ double AudioPlayer::getOutputLatency() const { return minBaseLatency; } - -bool AudioPlayer::rebuildStream() { - cleanup(); - return openAudioStream(); -} } // namespace audioapi diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h index 760fd5c7f..107ec9236 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h @@ -69,7 +69,6 @@ class AudioPlayer : public CommonPlayer, std::weak_ptr context_; bool openAudioStream(); - bool rebuildStream(); }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp index c7a038e48..113d48ca4 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp @@ -116,6 +116,8 @@ bool AudioContext::resume(const std::shared_ptr> &p getGraph()->disableProducerSelfDrain(); if (audioPlayer_->resume()) { result = true; + } else if (audioPlayer_->rebuildStream() && audioPlayer_->resume()) { + result = true; } else { getGraph()->enableProducerSelfDrain(); } From 194e3bc59a9667cfe40f3f025f03b2a93f253d94 Mon Sep 17 00:00:00 2001 From: Barbara Wojtarowicz Date: Wed, 26 Aug 2026 15:54:02 +0200 Subject: [PATCH 3/3] fix(AudioPlayer): update onErrorAfterClose, remove rebuilding the stream from resume --- .../cpp/audioapi/android/core/AudioPlayer.cpp | 66 +++++++++++-------- .../cpp/audioapi/android/core/AudioPlayer.h | 1 - 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp index 9ddbc8557..47ea5c8fe 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp @@ -55,21 +55,38 @@ bool AudioPlayer::openAudioStream() { return true; } +bool AudioPlayer::rebuildStream() { + cleanup(); + return openAudioStream(); +} + bool AudioPlayer::start() { std::scoped_lock lock(streamMutex_); - if (!isInitialized_.load(std::memory_order_acquire)) { + + // Ensure a live stream exists. A prior Oboe death can leave isInitialized_ true + // with a closed/null stream, or a stream that rejects requestStart. + if (!isInitialized_.load(std::memory_order_acquire) || mStream_ == nullptr) { + if (isInitialized_.load(std::memory_order_acquire) || mStream_ != nullptr) { + cleanup(); + } if (!openAudioStream()) { return false; } } - if (mStream_ != nullptr) { - auto result = mStream_->requestStart() == oboe::Result::OK; - isRunning_.store(result, std::memory_order_release); - return result; + if (mStream_ != nullptr && mStream_->requestStart() == oboe::Result::OK) { + isRunning_.store(true, std::memory_order_release); + return true; } - return false; + if (!rebuildStream() || mStream_ == nullptr) { + isRunning_.store(false, std::memory_order_release); + return false; + } + + const bool started = mStream_->requestStart() == oboe::Result::OK; + isRunning_.store(started, std::memory_order_release); + return started; } void AudioPlayer::stop() { @@ -88,20 +105,11 @@ bool AudioPlayer::resume() { } if (mStream_ != nullptr) { - if (mStream_->requestStart() == oboe::Result::OK) { - isRunning_.store(true, std::memory_order_release); - return true; - } - } - - if (rebuildStream()) { - if (mStream_ != nullptr && mStream_->requestStart() == oboe::Result::OK) { - isRunning_.store(true, std::memory_order_release); - return true; - } + auto result = mStream_->requestStart() == oboe::Result::OK; + isRunning_.store(result, std::memory_order_release); + return result; } - isRunning_.store(false, std::memory_order_release); return false; } @@ -167,11 +175,19 @@ AudioPlayer::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numF } void AudioPlayer::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result error) { - // error != oboe::Result::ErrorDisconnected condition is deleted to handle more cases of errors if (driverMutex_ == nullptr) { return; } + switch (error) { + case oboe::Result::ErrorDisconnected: + case oboe::Result::ErrorTimeout: + case oboe::Result::ErrorInternal: + break; + default: + return; + } + // Serialize with start()/resume()/suspend()/close() on the JS / promise-pool threads. std::scoped_lock lock(*driverMutex_, streamMutex_); @@ -190,13 +206,14 @@ void AudioPlayer::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result erro const bool wasRunning = isRunning_.load(std::memory_order_acquire); if (!rebuildStream()) { + isRunning_.store(false, std::memory_order_release); return; } // Restart the stream if it was expected to be running when the error occurred - if (wasRunning) { - auto result = mStream_->requestStart() == oboe::Result::OK; - isRunning_.store(result, std::memory_order_release); + if (wasRunning && mStream_ != nullptr) { + const bool started = mStream_->requestStart() == oboe::Result::OK; + isRunning_.store(started, std::memory_order_release); } } @@ -243,9 +260,4 @@ double AudioPlayer::getOutputLatency() const { return minBaseLatency; } - -bool AudioPlayer::rebuildStream() { - cleanup(); - return openAudioStream(); -} } // namespace audioapi diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h index 760fd5c7f..107ec9236 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h @@ -69,7 +69,6 @@ class AudioPlayer : public CommonPlayer, std::weak_ptr context_; bool openAudioStream(); - bool rebuildStream(); };