You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Request microphone permission before audio capture starts (#1183)
Flutter counterpart of client-sdk-swift #1085, matching its final merged
behavior. Builds on #1182.
## Why
webrtc-sdk/webrtc#265 (first shipped in `m144.7559.12`) removed the
blocking mic permission request from the AudioEngine device. The
pre-enable check is now passive: it returns
`kAudioEngineErrorInsufficientDevicePermission` (-9000) instead of
prompting, so requesting permission is the SDK's job.
flutter-webrtc still pins `144.7559.10`, so this is not load-bearing
yet. It is harmless there, since `getUserMedia` in flutter-webrtc
already prompts and the status is resolved before the device's blocking
path runs. Once flutter-webrtc bumps past `.12` and the pin here
follows, this is what keeps the current behavior.
## What Flutter already had
flutter-webrtc's `getUserMedia` calls `AVCaptureDevice
requestAccessForMediaType:` and waits for the answer, so every
livekit_client mic path (publish, `restartTrack` on unmute, pre-connect
audio) already prompted before the audio device saw the track. That part
of #1085 needs no port. #1182 already maps -9000 to
`TrackCreateException` for the direct ADM entry points
(`setEngineAvailability`, `startLocalRecording`).
## What this adds
The one behavior from #1085 that was missing: only prompt while the app
can show the alert.
- Native `ensureMicrophoneAccess` in `LiveKitPlugin.swift`: `authorized`
passes, `denied`/`restricted` fail, `notDetermined` requests access. On
iOS the request is only made while
`UIApplication.shared.applicationState == .active`. An inactive or
backgrounded app (locked screen, CallKit wake, app switcher) has the
alert deferred by the system, and awaiting it would suspend
`getUserMedia` and the `_publishRunner` behind it, blocking camera and
screen share publishes for as long as the app stays there. Failing fast
lets the next foreground attempt prompt normally. macOS can present the
prompt regardless, so it always requests. No app extension concern here,
the plugin is app-only.
- The gate is skipped while engine input availability is disabled
(`setEngineAvailability`, the CallKit flow), mirroring the same late fix
in #1085: the audio device module defers opening input entirely and runs
no permission check there, so gating would turn a working background
connect into a `deviceAccessDenied` failure. The check reads the
plugin's tracked availability value, so it also covers gating done
natively before the Flutter engine exists.
- `LocalTrack.createStream` calls it for `AudioCaptureOptions` on Apple
platforms before `getUserMedia`. That is the Flutter choke point:
`LocalAudioTrack.create()`, `restartTrack()` and
`PreConnectAudioBuffer.startRecording()` all reach it. Since the prompt
in Flutter happens at `getUserMedia` rather than at capture start, the
gate sits in front of that instead of in `startCapture` as in Swift.
- Failures surface as `TrackCreateException` through the
`deviceAccessDenied` code introduced in #1182.
- Docs for `withPreConnectAudio` and
`PreConnectAudioBuffer.startRecording` now say permission is requested
at recording start but only while the app is active, so callers running
at app launch should request it up front (matching the final #1085
wording). `Native.setEngineAvailability` documents that permission is
not requested there and must be granted before input availability is
restored.
## Testing
- `flutter analyze`, `flutter test`, `dart format
--set-exit-if-changed`, `import_sorter --exit-if-changed` clean.
- Unit tests cover `Native.ensureMicrophoneAccess` (no-op when
unimplemented, propagates `deviceAccessDenied`). The `createStream` gate
is behind `lkPlatformIsApple()` and not reachable from unit tests.
- The example app builds for iOS (device SDK) and macOS with the change,
re-verified after the rebase onto `main`. On-device run against a fresh
install (first-launch prompt) and a CallKit background wake still to do.
Refs CLT-3243, client-sdk-swift#1085
patch type="fixed" "iOS/macOS: request microphone permission before audio capture starts, failing fast with TrackCreateException while the app is not in the foreground"
patch type="fixed" "Pre-connect audio buffer returns to a reusable state when recording fails to start, instead of ignoring retries and leaking the agent timeout"
denied("Microphone permission could not be requested because the app is not in the foreground. Request it while the app is active before enabling recording.")
583
+
return
584
+
}
585
+
#endif
586
+
AVCaptureDevice.requestAccess(for:.audio){ granted in
0 commit comments