Skip to content

Add camera input selection to the call window#159

Open
rexbron wants to merge 2 commits into
subpop:mainfrom
rexbron:call-camera-selection
Open

Add camera input selection to the call window#159
rexbron wants to merge 2 commits into
subpop:mainfrom
rexbron:call-camera-selection

Conversation

@rexbron

@rexbron rexbron commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

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 → CallViewModelCallView
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 / AudioInputDevice value types (in RelayInterface,
    id/name[/kind]) let the UI list and pick devices without depending on
    AVFoundation or the audio SDK; CallViewModel maps them to/from the SDK
    types by id.
  • Camera — enumerated via CameraCapturer.captureDevices() (surfaces
    .continuityCamera/.external on macOS 14+). Selecting retargets the live
    capturer (capturer.set(options: options.copyWith(device:))) whether the
    camera is on or muted, so the self-view never drifts from the menu.
  • Microphone — backed by LiveKit's AudioManager (inputDevices /
    inputDevice). Same-named CoreAudio duplicates (e.g. AirPods register
    several) are collapsed to one entry. The switch runs off the main actor so
    the UI stays responsive while the audio engine reconfigures.
  • Persistence — the last-used camera and microphone are remembered (by
    device id) in UserDefaults and preferred on the next call when still
    present; 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
.platformDefault ADM, or a newer SDK). Camera switching is unaffected.

Requirements

No new permission — reuses the existing camera/microphone authorizations.

Testing

  • Camera: source menu lists built-in plus external and iPhone (Continuity),
    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.
  • Microphone: source menu lists inputs (AirPods shown once, not duplicated);
    selecting switches the input without freezing the UI; last-used mic preferred
    on the next call.
  • SwiftUI preview of CallView renders both split controls with mock devices.

🤖 Generated with Claude Code

rexbron and others added 2 commits June 30, 2026 12:54
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant