Skip to content

fix: AudioContext::resume reanimates the failed Oboe stream due to the change in AudioPlayer::resume - #1252

Open
basiav wants to merge 8 commits into
mainfrom
fix/oboe-stream-dies
Open

basiav wants to merge 8 commits into
mainfrom
fix/oboe-stream-dies

Conversation

@basiav

@basiav basiav commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Closes #1230

⚠️ Breaking changes ⚠️

Introduced changes

AudioPlayer::onErrorAfterClose

  • More errors are handled in AudioPlayer::onErrorAfterClose: ErrorDisconnected, ErrorTimeout, ErrorNoService, ErrorInternal.
  • There is one try of rebuild of the stream, if it fails, AudioContext::onStreamFail is called.

AudioContext::onStreamFail

  • Calls the AudioPlayer::cleanup procedure, sets isInitialized_ to false, invokes onerror defined in JS.

Checklist

  • Linked relevant issue
  • Updated relevant documentation
  • Added/Conducted relevant tests
  • Performed self-review of the code
  • Updated Web Audio API coverage
  • Added support for web
  • Updated old arch android spec file

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

WPT non-regression comparison

PASS — no regressions · 5 improved section(s) · overall 2632 → 2717 (+85)

Spec section Base pass Head pass Delta
AudioBufferSourceNode 208 218 +10
AudioParam 599 629 +30
ConstantSourceNode 59 64 +5
ConvolverNode 170 203 +33
WaveShaperNode 75 82 +7

Per-file data unavailable in one of the reports — category-level comparison only.

Unchanged sections (23)
Spec section Base pass Head pass Delta
Processing model 0 0 0
Other 52 52 0
AnalyserNode 138 138 0
AudioBuffer 140 140 0
AudioContext 58 58 0
AudioNode 261 261 0
BiquadFilterNode 275 275 0
ChannelMergerNode 30 30 0
ChannelSplitterNode 7 7 0
DelayNode 104 104 0
DestinationNode 0 0 0
DynamicsCompressorNode 4 4 0
GainNode 15 15 0
IIRFilterNode 87 87 0
MediaElementAudioSourceNode 0 0 0
MediaStreamAudioDestinationNode 1 1 0
MediaStreamAudioSourceNode 0 0 0
OfflineAudioContext 35 35 0
OscillatorNode 104 104 0
PannerNode 75 75 0
PeriodicWave 33 33 0
ScriptProcessorNode 0 0 0
StereoPannerNode 102 102 0

Baseline: 77a9d5c938a871166827e30635163e5dd322372f · Candidate: d603c6ed35cdddb3e1fad047d5cf1ddb5178aad3

Workflow run · this comment is updated on every push.

@closetcaiman closetcaiman changed the title Fix on Android: AudioContext::resume reanimates the failed Oboe stream due to the change in AudioPlayer::resume fix: AudioContext::resume reanimates the failed Oboe stream due to the change in AudioPlayer::resume Aug 25, 2026
@closetcaiman closetcaiman added fix Code changes specifically addressing and resolving a bug android Native Android implementation, C++/Java/Kotlin bindings, or Android-specific issues labels Aug 25, 2026
Comment on lines +190 to +195
const bool wasRunning = isRunning_.load(std::memory_order_acquire);

if (!rebuildStream()) {
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If rebuildStream fails, isRunning flag may stay true.

Comment on lines -161 to +171
if (error != oboe::Result::ErrorDisconnected || driverMutex_ == nullptr) {
// error != oboe::Result::ErrorDisconnected condition is deleted to handle more cases of errors
if (driverMutex_ == nullptr) {

@closetcaiman closetcaiman Aug 25, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this guard seems really dangerous to me. Consider this: should all types of errors result in stream rebuild retry? What about some non-recoverable errors, won't that create an infinite loop? Maybe it is better to choose action in response to specific errors - oboe::Result is not that big of an enum.

Comment on lines 90 to 105
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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of resume being responsible for rebuilding the stream. Seems like it tries to act both as resume and start.

@basiav
basiav marked this pull request as ready for review September 14, 2026 13:09
@basiav
basiav requested a review from mdydek September 14, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

android Native Android implementation, C++/Java/Kotlin bindings, or Android-specific issues fix Code changes specifically addressing and resolving a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] AudioContext.resume() fails permanently after the Oboe stream dies with a non-Disconnected error (audio silent for the rest of the session)

2 participants