Skip to content

Web ​

Một single-page app viết bằng Vue 3. App chạy trên trình duyệt của máy tính lẫn điện thoại, và chỉ dùng đúng API WebRTC có sẵn của trình duyệt.

Ảnh chụp cần thêm
Web client trên trình duyệt máy tính trong một cuộc gọi 1:1: video của người kia chiếm cả cửa sổ, tile của bạn nằm ở một góc, toolbar đang hiện và có tooltip trên một nút, panel chat bên cạnh đang mở.
File: docs/public/media/web-call.png

Stack: Vue 3, TypeScript, Vite, Tailwind CSS v4, shadcn-vue (reka-ui), Lucide, motion-v, MediaPipe Tasks Vision.

sh
cd web
npm install
npm run dev   # http://localhost:5173

Code nằm ở đâu ​

FileLàm gì
call/useCall.tsEngine 1:1: WebSocket signaling, RTCPeerConnection, chat
call/useGroupCall.tsEngine nhóm: WebSocket tới SFU, connection publish và subscribe, người đang nói
call/media.tsNguồn local (micro, camera, màn hình, file), effect, preview, quy tắc 300 ms. Dùng chung cho cả hai engine.
call/frameCrypto.tsGiữ key của frame và gắn các transform E2EE. Dùng chung cho cả hai engine.
e2ee.tsĐịnh dạng frame tương thích với FrameCryptor
encryptionWorker.tsWorker chạy các transform E2EE
effects/Danh mục effect, đặt sticker, xử lý bằng MediaPipe
composables/usePictureInPicture.tsDocument PiP, dự phòng bằng video PiP
components/Lobby, màn hình cuộc gọi, toolbar, chat, view PiP

Các engine là composable dạng singleton. Chúng nắm socket, các peer connection, media local và chat, rồi expose state ra dưới dạng ref. Component chỉ render state đó và gọi các action của nó.

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

  • Signaling. Một WebSocket bình thường của trình duyệt tới /ws. Lobby gọi GET / mỗi 5 s để cập nhật chấm trạng thái, còn socket chỉ mở khi bạn vào phòng. Signaling
  • Chuyển nguồn. Mỗi nguồn một track, và gọi replaceTrack() trên cùng một sender. Chuyển nguồn video
  • E2EE. createEncodedStreams() trên Chrome, RTCRtpScriptTransform trên Safari và Firefox, cả hai đều chạy trong worker để việc mã hóa không bao giờ chặn phần render. Mã hóa đầu cuối
  • Effect. MediaPipe và một canvas 2D, chỉ tải khi bạn bật effect lần đầu. Phông nền và effect
  • Picture-in-picture. Trên Chrome và Edge là cả một cửa sổ cuộc gọi thu nhỏ, từ Chrome 134 còn tự mở khi bạn chuyển tab. Picture-in-picture
  • Cúp máy. Data channel bị đóng khi connection vẫn đang sống được tính là bên kia đã cúp máy. Signaling

Ghi chú ​

  • Audio của bên kia phát từ một phần tử <audio> ẩn duy nhất, còn mọi phần tử <video> đều bị tắt tiếng, nên preview, khung chính và cửa sổ PiP không bao giờ phát tiếng hai lần. Trong cuộc gọi nhóm, mỗi người tham gia có một <audio> ẩn riêng.
  • Tile local được điều khiển bằng motion value. Khi kéo, app dựa vào vận tốc lúc thả tay để chọn góc, rồi tile bật về góc đó theo hiệu ứng lò xo.
  • Từ 1024 px trở lên, chat là panel bên cạnh, hẹp hơn thì là drawer ở dưới.
  • Web client được phục vụ qua HTTPS thì không mở được socket ws://. Trong trường hợp đó, đặt một TLS proxy phía trước các server.