mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-14 04:59:56 +00:00
5.8 KiB
5.8 KiB
Audio / Video Call
Related spec: spec/services/calls.md
Purpose
Make and receive end-to-end encrypted audio and video calls over WebRTC. Supports CallKit integration for native iOS call UI, picture-in-picture for video calls, audio device selection, and collapsible call overlay.
Route / Navigation
- Entry point (outgoing): Tap audio or video call button in
ChatInfoViewaction buttons orChatViewtoolbar - Entry point (incoming):
IncomingCallViewbanner appears at top of screen; or native CallKit UI if enabled - Presented by:
ActiveCallViewis overlaid on the main app view whenchatModel.activeCallis set - Collapsible: Call view can be collapsed via
chatModel.activeCallViewIsCollapsedto return to chat while call continues - Dismiss: Call ends when user taps end button or remote party disconnects
Page Sections
Incoming Call Banner (IncomingCallView)
Displayed as an overlay banner when CallController.activeCallInvitation is set:
| Element | Description |
|---|---|
| Profile avatar | User profile image (shown when multiple profiles exist) |
| Call type icon | video.fill (green) for video calls, phone.fill (green) for audio |
| Call type text | "Audio call" or "Video call" with caller info |
| Caller profile | ProfilePreview showing caller name and image |
| Reject button | Red phone.down.fill icon -- ends the invitation |
| Ignore button | Neutral multiply icon -- dismisses the banner without rejecting |
| Accept button | Green checkmark icon -- accepts the call; if another call is active, ends it first |
Sound: Ringtone plays via SoundPlayer.startRingtone() while banner is visible (unless call view is already showing).
Active Call View (ActiveCallView)
Full-screen overlay with black background:
| Element | Description |
|---|---|
| Remote video | Full-screen CallViewRemote showing remote party's camera feed; tap toggles between scaleAspectFill and scaleAspectFit |
| Local video preview | Small floating CallViewLocal in top-right corner (30% width); shows local camera with rounded corners |
| Call overlay | ActiveCallOverlay with call controls (hidden when PiP is active for video calls) |
| Screen keep-on | AppDelegate.keepScreenOn(true) prevents screen dimming during calls |
Call Controls (ActiveCallOverlay)
Bottom bar of the active call:
| Control | Description |
|---|---|
| Mute toggle | Microphone on/off |
| Speaker toggle | Speaker/receiver switch |
| Camera switch | Front/back camera toggle (video calls) |
| Video toggle | Enable/disable video during call |
| End call | Red phone-down button to terminate |
| Audio device picker | AudioDevicePicker / CallAudioDeviceManager for selecting output (receiver, speaker, Bluetooth, AirPods) |
Picture-in-Picture (PiP)
- When
pipShown == trueand call has video, the call overlay is hidden - PiP window shows the remote video feed
- User can interact with the app normally while call continues
CallKit Integration
Managed by CallController:
| Feature | Description |
|---|---|
| Native incoming call UI | iOS system call screen for incoming calls (when CallKit is enabled) |
| Call history | Optionally shown in Phone app recents (DEFAULT_CALL_KIT_CALLS_IN_RECENTS) |
| System audio routing | CallKit manages audio session configuration |
| Lock screen answering | Call can be answered from lock screen via system UI |
When CallKit is not used, the app falls back to IncomingCallView banner.
WebRTC Client
| Component | Description |
|---|---|
WebRTCClient |
Manages peer connection, ICE candidates, media tracks |
WebRTC.swift |
Bridge between native code and WebRTC JavaScript via WKWebView |
CallViewRenderers |
CallViewLocal and CallViewRemote SwiftUI wrappers for video renderers |
Loading / Error States
| State | Behavior |
|---|---|
| Permissions required | Prompts for microphone (and camera for video) permissions on first call |
| Connecting | Call overlay shows connecting state; SoundPlayer plays connecting tone |
| WebRTC client creation | createWebRTCClient() called on appear and when canConnectCall changes |
| Call ended | CallSoundsPlayer.vibrate(long: true) on disconnect if was connected; audio session reset to .soloAmbient |
| Call failed | Call dismissed; WebRTC client cleaned up |
| No call invitation | IncomingCallView body is empty when no active invitation |
Audio Session Management
- During call: Audio session configured for voice chat
- Camera permissions:
AVFoundation.AVCaptureDeviceauthorization checked - Audio device management:
CallAudioDeviceManagerhandles routing changes and device enumeration - Post-call cleanup: Audio session reverted to
.soloAmbient
Related Specs
spec/services/calls.md-- Call service specification- Chat -- Call buttons in chat navigation bar
- Contact Info -- Call buttons in contact info action row
- Settings -- Call settings (CallKit, ICE servers, relay policy)
Source Files
Shared/Views/Call/ActiveCallView.swift-- Main active call view with video renderers and overlayShared/Views/Call/IncomingCallView.swift-- Incoming call notification bannerShared/Views/Call/CallController.swift-- CallKit integration and call lifecycle managementShared/Views/Call/CallManager.swift-- Call state management and CXProvider delegateShared/Views/Call/CallAudioDeviceManager.swift-- Audio device enumeration and routingShared/Views/Call/AudioDevicePicker.swift-- Audio output device picker UIShared/Views/Call/WebRTC.swift-- WebRTC signaling bridge via WKWebViewShared/Views/Call/WebRTCClient.swift-- WebRTC peer connection managementShared/Views/Call/CallViewRenderers.swift-- SwiftUI wrappers for local and remote video viewsShared/Views/Call/SoundPlayer.swift-- Ringtone and call sound playback