第 21 期 - WebRTC 前端实战:1v1 通话与多路通信架构
logoFRONTALK AI/11月13日 16:34/阅读原文

摘要

本文是 WebRTC 探索系列文章的下篇,首先展示 1v1 音视频通话 demo 效果并详细解析其功能,包括呼叫应答、媒体流调整、类 IM 功能等,还阐述了实际问题与解决方案,接着探讨 WebRTC 多路通信的三种架构及其优缺点、应用场景、注意事项等内容,最后总结了整个 WebRTC 探索之旅的成果与意义。

一、文章整体结构

文章围绕 WebRTC 技术展开,主要分为三大部分:1v1 音视频通话 demo 相关内容、WebRTC 多路通信架构以及对整个 WebRTC 探索之旅的总结。

(一)1v1 音视频通话 Demo

1. 演示效果

// 调整视频轨道的启用状态示例代码
senders.find((s) => s.track.kind === 'video').track.enabled =!send.track.enabled

2. 功能详解

async initCallerInfo(callerId, calleeId) {
    // 初始化 PeerConnection 对象
    this.localRtcPc = new PeerConnection();
    // 获取本地媒体流(音频和视频)
    const localStream = await this.getLocalUserMedia({ audio: true, video: true });
    // 将本地媒体流中的每个轨道添加到 PeerConnection
    localStream.getTracks().forEach(track => this.localRtcPc.addTrack(track));
    // 将本地媒体流渲染到预览 DOM 元素
    await this.setDomVideoStream("localdemo", localStream);
    // 初始化回调函数来处理各种事件
    this.onPcEvent(this.localRtcPc, callerId, calleeId);
    // 创建并设置 offer 信令
    const offer = await this.localRtcPc.createOffer();
    await this.localRtcPc.setLocalDescription(offer);
    // 通过信令服务器将 offer 发送给被呼叫端
    this.linkSocket.emit("offer", { targetUid: calleeId, userId: callerId, offer });
}
onPcEvent(pc, localUid, remoteUid) {
    // 创建一个数据通道
    that.channel = pc.createDataChannel('chat');
    // 监听远程媒体流的轨道
    pc.ontrack = function(event) {
        that.setRemoteDomVideoStream('remoteVideo', event.track);
    };
    // 监听重新协商事件
    pc.onnegotiationneeded = function(e) {
        console.log('重新协商', e);
    };
    // 监听 ICE 候选的生成
    pc.onicecandidate = function(event) {
        if (event.candidate) {
            // 通过信令服务器发送 ICE 候选信息
            that.linkSocket.emit('candidate', {
                targetUid: remoteUid,
                userId: localUid,
                candidate: event.candidate,
            });
        } else {
            console.log('在此次协商中,没有更多的候选了');
        }
    };
}
async onRemoteOffer(fromUid, offer) {
    //  接受呼叫端的 offer 并设置为 remote desc
    await this.localRtcPc.setRemoteDescription(offer);
    // 创建应答信令
    const answer = await this.localRtcPc.createAnswer();
    // 设置为 local desc
    await this.localRtcPc.setLocalDescription(answer);
    // 通过信令服务器将 answer 发送给呼叫端
    this.linkSocket.emit("answer", { targetUid: fromUid, userId: getParams("userId"), answer });
}
// 切换视频模式(音频模式/视频模式)
const senders = this.localRtcPc.getSenders();
// 找到视频发送方的信息
const send = senders.find(s => s.track.kind === 'video');
// 切换视频轨道的启用状态
send.track.enabled =!send.track.enabled;
// 屏幕分享/摄像头切换
const stream = await this.getShareMedia();
// 获取视频轨道
const [videoTrack] = stream.getVideoTracks();
// 找到视频类型发送方的信息
const send = senders.find(s => s.track.kind === 'video');
// 替换视频轨道
send.replaceTrack(videoTrack);
// 创建数据通道
this.channel = pc.createDataChannel("my channel", {
    protocol: "json",
    ordered: true,
});
// 监听数据通道的事件
pc.ondatachannel = function(ev) {
    console.log('Data channel is created!');
    ev.channel.onopen = function() {
        console.log('Data channel ------------open----------------');
    };
    ev.channel.onmessage = function(ev) {
        console.log('Data channel ------------msg----------------');
    };
};

3. 实际问题与解决方案

(二)WebRTC 多路通信架构

1. 架构概述

2. Mesh 架构

3. SFU 架构

4. MCU 架构

5. 实际应用案例分析

(三)总结

 

扩展阅读

Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有