Android
App Kotlin dùng Jetpack Compose và Material 3 Expressive, chạy trên webrtc-sdk 150.7871.01. Cần Android 7.0 (API 24) trở lên.
File: docs/public/media/android-call.pngChạy configuration app từ Android Studio, hoặc chạy ./gradlew :app:installDebug trong android/.
Code nằm ở đâu
Mọi đường dẫn đều nằm dưới android/app/src/main/java/com/example/myapplication.
| File | Làm gì |
|---|---|
MainActivity.kt | Chứa lobby viết bằng Compose, khởi động CallActivity |
CallActivity.kt | Chứa màn hình cuộc gọi: quyền truy cập, các hộp chọn, MediaProjection, PiP |
call/ | BaseCallViewModel (state và điều khiển media dùng chung), CallViewModel (1:1), GroupCallViewModel (nhóm) |
webrtc/LocalMedia.kt | Factory, capturer, source, track, effect, quy tắc 300 ms. Dùng chung cho cả hai engine. |
webrtc/PeerConnectionClient.kt | Engine 1:1 |
webrtc/WebRtcPeer.kt | Một PeerConnection, kèm data channel và các cryptor của nó |
webrtc/SignalingSocket.kt | JSON qua WebSocket của OkHttp, callback chạy trên main thread. Cả hai engine đều dùng. |
webrtc/sfu/GroupCallClient.kt | Engine nhóm: connection publish và subscribe |
webrtc/E2eeManager.kt | Key provider cho FrameCryptor |
webrtc/effects/ | Effects processor bằng GLES, segmenter và face tracker của MediaPipe |
ui/video/TextureViewRenderer.kt | View hiển thị video |
Cách Android làm từng phần
- Chuyển nguồn. Camera và màn hình mỗi thứ có một track mới, đặt lên cùng một sender bằng
RtpSender.setTrack(). File thì dùng lạiVideoSourcecủa camera với một capturer khác. Chuyển nguồn video - Chia sẻ màn hình.
MediaProjectionkèm một foreground service,ScreenCaptureService.kt. Service vẫn sống khi bạn chia sẻ một app khác, nên nó biết khi máy xoay và đổi kích thước virtual display theo. - E2EE.
FrameCryptorcủa webrtc-sdk với các tùy chọn dùng chung. Mã hóa đầu cuối - Effect. Một
VideoProcessorgắn trênVideoSourcecủa camera. Mọi thứ chạy bằng shader GLES trên texture của camera, chỉ có một bản copy nhỏ được đọc ngược ra cho model. Phông nền và effect - Picture-in-picture. Trên Android 12+, app tự vào PiP khi bạn vuốt về màn hình chính. Picture-in-picture
Render video
Video được vẽ bằng TextureViewRenderer, một TextureView được EglRenderer cấp frame. Khác với SurfaceViewRenderer, TextureView có thể bị clip, bo góc và animate cùng với phần còn lại của cây Compose, là thứ mà view tự xem hình nổi của bạn cần.
Renderer luôn fill kín view của nó. Vì vậy view được layout theo đúng tỉ lệ của frame, vừa đủ lớn để phủ kín màn hình, còn "fit" thì thu nhỏ nó lại bằng graphicsLayer. Nhờ vậy chuyển giữa fit và fill chỉ là một animation chạy trên GPU, không bao giờ phải đổi kích thước TextureView. iOS, macOS và Windows cũng dùng cách này.
Thread
| Thread | Việc |
|---|---|
| Main | UI, mọi lời gọi vào PeerConnection, callback của socket |
| Signaling thread của WebRTC | Callback của PeerConnection.Observer, được chuyển sang main |
CaptureThread | Frame camera và phần việc GL của effect |
SegmenterInference, FaceTracker | Các model MediaPipe |
Khi cuộc gọi đã kết thúc thì callback bị bỏ qua, vì gọi vào một PeerConnection native đã bị dispose sẽ làm crash cả process.
Ghi chú
CallViewModelsống sót qua lần xoay màn hình và nắm WebRTC client cùngEglBasedùng chung, nên cuộc gọi vẫn chạy trong lúc activity được tạo lại. Thực raCallActivitytự xử lý việc đổi kích thước, nên trên thực tế nó không bị tạo lại.Camera2Sessiongắn hướng xoay của thiết bị vào từng frame, nên cầm điện thoại kiểu gì thì bên kia cũng thấy hình thẳng.- Material 3 Expressive chỉ có trong các bản alpha
material31.5, đang ghim ở1.5.0-alpha18. Đọc Xử lý sự cố trước khi nâng Compose. - Các model MediaPipe (
selfie_segmenter.tflite,face_landmarker.task) được lưu không nén trong assets của APK.
