Add camera input selection to the call window#159
Open
rexbron wants to merge 2 commits into
Open
Conversation
Lets users pick which camera feeds the call — built-in, external/USB, or an iPhone via Continuity Camera — like FaceTime. - CameraDevice value type (id/name/kind) added to CallViewModelProtocol so the UI lists and picks cameras without depending on AVFoundation; the concrete CallViewModel maps these to/from AVCaptureDevice by uniqueID. - CallViewModel enumerates via CameraCapturer.captureDevices(), switches the live capturer on selection (works whether the camera is on or muted), and remembers the last-used camera by uniqueID in UserDefaults, preferring it on the next call when still present. - CallView shows a split control: the camera on/off toggle plus a disclosure menu listing cameras with a checkmark on the active one. The list refreshes each time the menu opens so a camera connected mid-call appears. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirrors the camera picker for audio inputs, backed by LiveKit's AudioManager (macOS): a mute toggle + disclosure menu of microphones on the call control pill, with the last-used input remembered across calls. - AudioInputDevice value type + protocol surface (availableAudioInputs, selectedAudioInputID, refreshAudioInputs, selectAudioInput). - refreshAudioInputs reads AudioManager.shared.inputDevices and dedupes by name — Bluetooth inputs like AirPods register several CoreAudio objects under one name, so they collapse to a single entry. - selectAudioInput updates the UI immediately, then applies the switch off the main actor: AudioManager retargets the device module synchronously and on Bluetooth rebuilds the whole engine, which would otherwise block the UI for seconds. Known limitation: switching inputs mid-call churns LiveKit's AVAudioEngine audio device module (voice-processing graph rebuild); selection works but is not yet seamless. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Summary
Lets users choose which camera and microphone feed a call — built-in,
external/USB, or an iPhone via Continuity Camera for video — like FaceTime.
Previously the call always used whatever devices LiveKit picked by default.
How it works
Both pickers share one pattern (protocol →
CallViewModel→CallView→preview) and surface as a split control on the call pill: the existing
on/off toggle plus a disclosure menu of devices with a checkmark on the active
one, shown only when more than one device exists. Menus refresh their list each
time they open, so a device connected mid-call appears.
CameraDevice/AudioInputDevicevalue types (inRelayInterface,id/name[/kind]) let the UI list and pick devices without depending onAVFoundation or the audio SDK;
CallViewModelmaps them to/from the SDKtypes by id.
CameraCapturer.captureDevices()(surfaces.continuityCamera/.externalon macOS 14+). Selecting retargets the livecapturer (
capturer.set(options: options.copyWith(device:))) whether thecamera is on or muted, so the self-view never drifts from the menu.
AudioManager(inputDevices/inputDevice). Same-named CoreAudio duplicates (e.g. AirPods registerseveral) are collapsed to one entry. The switch runs off the main actor so
the UI stays responsive while the audio engine reconfigures.
device id) in
UserDefaultsand preferred on the next call when stillpresent; a stale id validates against the current device list and falls back
to the system default.
Known limitation (audio)
Switching microphones mid-call churns LiveKit's AVAudioEngine audio device
module (voice-processing graph rebuild) — selection works but isn't yet
seamless, and there's a brief audio glitch during the switch. This is an
upstream SDK/CoreAudio robustness area; tracked for a follow-up (candidate: the
.platformDefaultADM, or a newer SDK). Camera switching is unaffected.Requirements
No new permission — reuses the existing camera/microphone authorizations.
Testing
checkmark on active; selecting switches local + remote video live; switching
while the camera is off keeps the choice; a new call opens on the last-used
camera.
selecting switches the input without freezing the UI; last-used mic preferred
on the next call.
CallViewrenders both split controls with mock devices.🤖 Generated with Claude Code