Skip to content

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.

Ảnh chụp cần thêm
Android trong cuộc gọi 1:1, màn hình dọc: video của người kia chiếm cả màn hình, tile của bạn ở một góc, toolbar nổi hiện ở phía dưới.
File: docs/public/media/android-call.png

Chạ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.

FileLàm gì
MainActivity.ktChứa lobby viết bằng Compose, khởi động CallActivity
CallActivity.ktChứ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.ktFactory, capturer, source, track, effect, quy tắc 300 ms. Dùng chung cho cả hai engine.
webrtc/PeerConnectionClient.ktEngine 1:1
webrtc/WebRtcPeer.ktMột PeerConnection, kèm data channel và các cryptor của nó
webrtc/SignalingSocket.ktJSON qua WebSocket của OkHttp, callback chạy trên main thread. Cả hai engine đều dùng.
webrtc/sfu/GroupCallClient.ktEngine nhóm: connection publish và subscribe
webrtc/E2eeManager.ktKey provider cho FrameCryptor
webrtc/effects/Effects processor bằng GLES, segmenter và face tracker của MediaPipe
ui/video/TextureViewRenderer.ktView 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ại VideoSource của camera với một capturer khác. Chuyển nguồn video
  • Chia sẻ màn hình. MediaProjection kè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. FrameCryptor của webrtc-sdk với các tùy chọn dùng chung. Mã hóa đầu cuối
  • Effect. Một VideoProcessor gắn trên VideoSource củ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 ​

ThreadViệc
MainUI, mọi lời gọi vào PeerConnection, callback của socket
Signaling thread của WebRTCCallback của PeerConnection.Observer, được chuyển sang main
CaptureThreadFrame camera và phần việc GL của effect
SegmenterInference, FaceTrackerCá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ú ​

  • CallViewModel sống sót qua lần xoay màn hình và nắm WebRTC client cùng EglBase dùng chung, nên cuộc gọi vẫn chạy trong lúc activity được tạo lại. Thực ra CallActivity tự xử lý việc đổi kích thước, nên trên thực tế nó không bị tạo lại.
  • Camera2Session gắ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 material3 1.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.