Skip to content

iOS ​

App SwiftUI dùng Liquid Glass, chạy trên binary webrtc-sdk 150.7871.01. Cần iOS 26 trở lên, build bằng Xcode 26.

Ảnh chụp cần thêm
Một iPhone đang ở màn hình chính (hoặc trong app khác), cuộc gọi nằm trong cửa sổ picture-in-picture của hệ thống, hiện video của người kia.
File: docs/public/media/call-ios.png

Mở ios/WebRTCDemo.xcodeproj, chọn scheme WebRTCDemo và chạy trên thiết bị thật. Camera và chia sẻ màn hình cần phần cứng thật.

Code nằm ở đâu ​

Các file nằm trong ios/WebRTCDemo. Phần lớn cũng được compile vào app Mac.

FileLàm gì
CallViewModel.swiftState cuộc gọi dạng @Observable. Nắm LocalMedia và engine 1:1 hoặc engine nhóm.
LocalMedia.swiftFactory, track local, RTCVideoSource duy nhất, chia sẻ, audio session, đưa VP8 lên đầu
PeerConnectionClient.swiftWebRTCClient, engine 1:1: một peer connection và một data channel
GroupCallClient.swiftEngine nhóm: WebSocket tới SFU, connection publish và subscribe
SignalingSocket.swiftJSON qua URLSessionWebSocketTask. Cả hai engine đều dùng.
FrameEncryption.swiftKey provider E2EE và các frame cryptor
EffectsProcessor.swiftVision + Core Image, chạy dưới dạng một proxy capturer delegate
PictureInPicture.swiftPiP của hệ thống qua một AVSampleBufferDisplayLayer
VideoView.swiftWrapper của RTCMTLVideoView và phần chụp ảnh cho placeholder
CallView.swift, GroupCallView.swiftCác màn hình cuộc gọi
WebRTCDemoScreenBroadcast/Broadcast extension của ReplayKit

WebRTCClient gọi delegate từ các thread của WebRTC, còn view model chuyển sang main queue rồi mới đụng tới state.

Chia sẻ màn hình ​

Trên iOS, cách duy nhất để một app quay toàn bộ màn hình, kể cả sau khi bạn đã rời app, là dùng broadcast upload extension của ReplayKit. Extension chạy trong process riêng và không chạy được WebRTC, nên nó gửi frame cho app qua một Unix domain socket nằm trong app group dùng chung. Sau đó FlutterBroadcastScreenCapturer đẩy các frame này vào chính RTCVideoSource mà camera đang dùng.

Mỗi frame là một message kiểu HTTP: các header Content-Length, Buffer-Width, Buffer-Height và Buffer-Orientation, rồi tới phần body JPEG. Phía app của socket được chép từ plugin flutter-webrtc. Hãy giữ nguyên logic đọc của nó: nếu reader đòi stream trả về 0 byte, iOS sẽ báo hết stream và broadcast dừng ngay sau frame đầu tiên.

Chia sẻ vẫn tiếp tục khi bạn rời app, và để làm được điều đó cần hai thứ:

  • App phải còn sống. UIBackgroundModes có audio và voip, và cuộc gọi giữ một audio session playAndRecord luôn active, nên iOS không suspend app và socket của nó.
  • Encoder phải còn chạy. Khi app chạy nền, iOS vô hiệu hóa encoder H264 phần cứng của VideoToolbox, nên frame nào cũng encode lỗi và bên kia chỉ thấy hình đứng yên. Vì thế iOS luôn đưa VP8 (encode bằng phần mềm) lên đầu, dù có bật E2EE hay không. Đổi lại thì tốn CPU và pin hơn.

App và extension phải dùng chung một App Group (group.com.ducmai.webrtc.broadcast). Nếu bạn đổi team hoặc bundle ID, cập nhật nó trong cả hai file .entitlements, RTCAppGroupIdentifier trong Info.plist, SampleHandler.swift, và bundle ID của extension trong RTCScreenSharingExtension.

Audio session ​

App tự cấu hình audio session: trước mỗi cuộc gọi, CallViewModel.configureCallAudio() đặt cấu hình của WebRTC thành playAndRecord + voiceChat. Nếu không làm vậy, bản fork webrtc-sdk sẽ chép category lúc khởi động của session (soloAmbient), mà iOS lại từ chối category này khi đi kèm tùy chọn Bluetooth HFP, và cuộc gọi sẽ không có micro, không có tiếng. Xem Xử lý sự cố.

Cách iOS làm từng phần ​

  • Chuyển nguồn. Capturer của camera, file và màn hình đều cấp frame cho một RTCVideoSource duy nhất, nên track, sender và cryptor của nó không bao giờ thay đổi. Chuyển nguồn video
  • E2EE. RTCFrameCryptor với các tùy chọn dùng chung.
  • Effect. Một proxy RTCVideoCapturerDelegate nằm giữa camera và source, chạy Vision và Core Image trên Metal. Phông nền và effect
  • Picture-in-picture. PiP dành cho cuộc gọi video, chỉ 1:1. Picture-in-picture
  • Cuộc gọi nhóm. Mọi offer subscribe đều được parse để lấy a=mid và a=msid, vì libwebrtc không bắn event receiver mới khi một m-line được dùng lại cho người khác. Cuộc gọi nhóm (SFU)

Ghi chú ​

  • WebRTC lấy từ local Swift package ios/Packages/WebRTC, vì manifest Specs của bản release này không resolve được. Muốn nâng cấp, đổi URL và checksum trong Package.swift của package đó.
  • Info.plist cho phép HTTP thường (NSAllowsArbitraryLoads) để server nào trong mạng LAN cũng dùng được.