fix(ios): recording resume after an interruption - #1257
Open
SomePersonFromMars wants to merge 13 commits into
Open
fix(ios): recording resume after an interruption#1257SomePersonFromMars wants to merge 13 commits into
SomePersonFromMars wants to merge 13 commits into
Conversation
…ting a closed writer
SomePersonFromMars
force-pushed
the
fix/ios-recording-interrupted
branch
from
August 28, 2026 12:06
36d372e to
900e04b
Compare
…s actually delivered
SomePersonFromMars
force-pushed
the
fix/ios-recording-interrupted
branch
from
August 28, 2026 12:08
900e04b to
6283fb7
Compare
Also JS handler is only executed on an engine state change.
SomePersonFromMars
force-pushed
the
fix/ios-recording-interrupted
branch
from
September 2, 2026 12:06
534c3c0 to
3bd07bf
Compare
SomePersonFromMars
force-pushed
the
fix/ios-recording-interrupted
branch
from
September 2, 2026 12:09
3bd07bf to
86a9ae7
Compare
SomePersonFromMars
requested review from
closetcaiman and
maciejmakowski2003
and removed request for
closetcaiman
September 4, 2026 11:09
SomePersonFromMars
marked this pull request as ready for review
September 4, 2026 11:09
maciejmakowski2003
requested changes
Sep 4, 2026
maciejmakowski2003
left a comment
Collaborator
There was a problem hiding this comment.
could you perform the same research on android? in perfect world we would like to provide SotA for interruptions om both platforms.
|
|
||
| Enable emission with `AudioManager.observeAudioInterruptions(true)`, then listen. | ||
|
|
||
| **Playback:** pause on `began` (native does not resume players). The AudioFile example resumes on `ended` when it had been playing. |
Collaborator
There was a problem hiding this comment.
Suggested change
| **Playback:** pause on `began` (native does not resume players). The AudioFile example resumes on `ended` when it had been playing. | |
| **Playback:** native pause on `began`, but does not resume any audio contexts. The AudioFile example resumes on `ended` when it had been playing. |
Collaborator
There was a problem hiding this comment.
could you research and add a section about background resume for playback? I am quite sure that below audio session settings allow it.
AudioManager.setAudioSessionOptions({
iosCategory: 'playback',
iosMode: 'spokenAudio',
})
Comment on lines
+131
to
+140
| - (void)emitInterruptionBeganIfAccepted:(bool)accepted | ||
| { | ||
| if (!self.audioInterruptionsObserved || !accepted) { | ||
| return; | ||
| } | ||
|
|
||
| [self.audioAPIModule invokeHandlerWithEventName:audioapi::AudioEvent::INTERRUPTION | ||
| payload:audioapi::InterruptionPayload{ | ||
| .type = "began", .shouldResume = false}]; | ||
| } |
Collaborator
There was a problem hiding this comment.
let's inline it
| AudioEngine *audioEngine = self.audioAPIModule.audioEngine; | ||
|
|
||
| if (self.interruptionEndedDelivered && [audioEngine getState] == AudioEngineStateInterrupted) { | ||
| [self performInterruptionEndOnEngine:audioEngine shouldResume:true]; |
Collaborator
There was a problem hiding this comment.
verify hardcoded shouldResume: true
Comment on lines
+442
to
+452
| if (!shouldResume && self.inputRegistration == nil) { | ||
| [self stopEngine]; | ||
| [self rebuildAudioEngine]; | ||
| self.state = AudioEngineState::AudioEngineStatePaused; | ||
| [self notifyInput:AudioEngineInputNotificationHardwareChanged]; | ||
| return AudioEngineInterruptionEndOutcomePaused; | ||
| } | ||
|
|
||
| if (![self.sessionManager ensureActive:true error:&error]) { | ||
| NSLog(@"Error while activating audio session after interruption: %@", [error debugDescription]); | ||
| return AudioEngineInterruptionEndOutcomeStillInterrupted; |
Collaborator
There was a problem hiding this comment.
what if we have both player and recorder paused <==> engine paused and interruption happens? does it mean that engine will auto-start after interruption
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1220
observeAudioInterruptions(true)no longer replaces native recovery. NativeonInterruptionBegin/onInterruptionEndalways run. The flag only enables JSinterruptionevents, which matches the existingAudioManagerdocs. Apps that subscribed because observing used to skip native resume should not own engine recovery for a recorder (in particular, do not callRecorder.pause()onbegan- that moves the engine toPausedand disables the native retry). Playback can still pause onbegan; native does not resume players.Introduced changes
Fixes iOS recording that never came back after an interruption. Updated library policy: the same take resumes when the app is, or becomes, foreground. Background restart after the interrupter leaves is best-effort; iOS can still refuse I/O while the app is backgrounded.
Responds to the issue
§1
onInterruptionEndnever activated the session; failure poisonedIdle; no input-node guardstartEngine).startAndReturnErrorfailure, stayInterruptedand return (do not setIdle). A later retry can run.Interruptedand treat it as capture lost (do not look like a successful start with no buffers).shouldResumeis false; playback-only still pauses when the OS says not to resume.§2 Docs vs behavior for
observeAudioInterruptionsobserveAudioInterruptions, an appropriate handler runs only whenAudioEngineactually transitioned to a new state. For example, if the engine wasInterrupted, the handler runs on a successful transition toRunning.§3 No-op
setActive:falsestill ran deactivationsetAudioSessionActivity(false)only calls session-deactivation handling if the session was actually managed and active beforesetActive. It no longer transitionsInterruptedtoPaused, which used to disableonInterruptionEnd.§3 / footnote -
resume()/ storeRecordingon a dead engineresume()still does not have a return type, but it is now guaranteed thatisRecording()is true if and only if the audio engine is not in an interrupted state and the recorder is actually running.AudioEngine'sonInterruptionEndrecovery afterAVAudioSessionInterruptionTypeEndedcan still fail. That can happen, for example, due to a race with a higher-priority app or internal iOS privacy policy. In that case we retry once the app returns to the foreground, which is then guaranteed by iOS to succeed.Consumer note -
mixWithOthers/CannotInterruptOthersplayAndRecordis required to avoidCannotInterruptOtherswhen activating in the background, and is not sufficient for background restart. The Record demo enablesmixWithOtherswithdefaultmode.Additional useful tweaks and fixes
WillEnterForegroundNotificationandDidBecomeActiveNotification. iOS guarantees that a foreground audio start succeeds.handleInputConfigurationChangeto handle errors. A failed engine resume now finalizes the audio recorder.startIfNecessary, matching the recorder. A later recorder start must not play leftover source audio.observeAudioInterruptions(true); freeze the waveform onbegan, unfreeze only onended.Docs
AudioManagersubsection "Resume recording after an interruption".Manual tests and reproductions used while debugging
This issue is hard to reproduce with automated tests. During research and implementation I used the following manual tests, with the
apps/fabric-exampleapp and the included Recorder demo.Checklist