Trạng thái camera và micro
Video track bị disable không ngừng gửi. Nó gửi frame đen. Vì vậy chỉ nhìn video thì bên kia không phân biệt được "tắt camera" với "phòng tối". Thay vào đó, mỗi client tự báo trạng thái của mình bằng message media state:
{ "type": "media state", "state": { "audio": true, "video": false, "screen": false } }Message này được gửi mỗi khi có thay đổi, và gửi thêm một lần khi hai peer kết nối, để người vào sau cũng nhận được trạng thái hiện tại. screen là true khi đang chia sẻ màn hình hoặc file video. Lúc đó app nhận sẽ hiện video ở chế độ fit (thấy trọn frame) thay vì crop.
Quy tắc 300 ms
- Khi tắt camera, app gửi state trước, 300 ms sau mới disable track. Nhờ vậy peer bên kia đã chuyển sang placeholder trước khi có frame đen nào tới.
- Khi bật camera, app enable track trước, 300 ms sau mới gửi state. Peer bên kia chỉ chuyển lại khi frame thật đã chạy, nên không bao giờ hiện frame cuối bị đứng hình.
Tắt camera cũng giải phóng luôn camera, nên đèn camera tắt.
Placeholder
File: docs/public/media/camera-off.pngKhi video của người kia đang tắt (họ tự tắt, hoặc bạn ẩn đi), app nào cũng hiện một bản làm mờ của frame cuối cùng của họ phía sau một avatar gradient:
- Bản copy này rất nhỏ, rộng 36 px, chụp mỗi 500 ms. Frame gần như đen, như frame mà track bị disable gửi đi, sẽ bị bỏ qua.
- Màu avatar lấy từ hash của room ID, nên với cùng một phòng thì nền tảng nào cũng vẽ ra cùng một avatar.
- Vòng tròn quanh avatar chạy theo
audioLevelcủa bên kia lấy từgetStats()(inbound-rtp, audio), và chỉ đọc khi placeholder đang hiện.
Cách mỗi nền tảng chụp ảnh này quan trọng hơn bạn nghĩ. iOS copy từ frame trên CPU trong một video sink nhỏ. Android đọc ngược lại từ renderer sau khi vẽ (EglRenderer.addFrameListener), vì nếu chuyển texture của decoder bằng toI420() ngay trong track sink thì thread của decoder bị chặn và video của bên kia đứng hình.
| Nền tảng | Ở đâu |
|---|---|
| Web | PeerPlaceholder.vue |
| Android | FrameSnapshotter.kt, PeerPlaceholder.kt |
| iOS, macOS | VideoView.swift, PeerPlaceholderView.swift |
| Windows | RemoteSnapshotter.cs, RemotePlaceholder.cs |
Mức âm lượng micro của bạn
Khi micro bật, tile của bạn hiện ba vạch nhỏ nhảy theo giọng nói, để bạn biết micro đang hoạt động. Web client đo track micro bằng một analyser của Web Audio. Các app native đo phần âm thanh WebRTC đang gửi đi trong cuộc gọi. Khi bạn đang chờ một mình trong phòng 1:1 thì chưa có peer connection, nên các app này đọc thẳng từ micro để hiện mức âm lượng, và giải phóng micro trước khi WebRTC bắt đầu thu âm.
Những thứ chỉ diễn ra ở phía bạn
Tắt tiếng người kia hay ẩn video của họ chỉ disable remote track trên thiết bị của bạn. Không có gì được gửi đi. Trên iOS, setRemoteAudioEnabled() còn áp dụng cho cả những receiver được thêm vào ở các lần renegotiate sau.
Mỗi nền tảng đặt quy tắc này ở đúng một chỗ, dùng chung cho cả engine 1:1 lẫn engine nhóm: media.ts trên web, LocalMedia.kt trên Android, CallViewModel.swift trên iOS và macOS, và CallViewModel.cs trên Windows.
