(WebRTC + WebSocket)

简述:

  1. A、B 都连接信令服务器(ws);
  2. A 创建本地视频,并获取会话描述对象(offer sdp)信息;
  3. A 将 offer sdp 通过 ws 发送给 B;
  4. B 收到信令后,B 创建本地视频,并获取会话描述对象(answer sdp)信息;
  5. B 将 answer sdp 通过 ws 发送给 A;
  6. A 和 B 开始打洞,收集并通过 ws 交换 ice 信息;
  7. 完成打洞后,A 和 B 开始为安全的媒体通信协商秘钥;
  8. 至此, A 和 B 可以进行音视频通话。

第二部

代码:

    (1) 获取视频标签,连接信令服务器,创建 RTCPeerConnection 对象。其中 RTCPeerConnection 的作用是在两个对等端之间建立连接,其构造函数支持传一个配置对象,包含ICE“打洞”(由于本示例在本机进行测试,故不需要)。

    

const localVideo = document.querySelector('#local-video');
const remoteVideo = document.querySelector('#remote-video');
const socket = new WebSocket('ws://localhost:8080');
const peer = new RTCPeerConnection();

socket.onmessage = () => { // todo }
peer.ontrack = () => { // todo }
peer.onicecandidate = () => { // todo }


(2)获取本地摄像头/麦克风(需要允许使用权限),拿到本地媒体流(MediaStream)后,需要将其中所有媒体轨道(MediaStreamTrack)添加到轨道集,这些轨道将被发送到另一对等方。
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
    .then(stream => {
        localVideo.srcObject = stream;
        stream.getTracks().forEach(track => {
            peer.addTrack(track, stream);
        });
    });
(3)创建发起方会话描述对象(createOffer),设置本地SDP(setLocalDescription),并通过信令服务器发送到对等端,以启动与远程对等端的新WebRTC连接。
peer.createOffer().then(offer => {
    peer.setLocalDescription(offer);
    socket.send(JSON.stringify(offer));
});

当调用 setLocalDescription 方法,PeerConnection 开始收集候选人(ice信息),并发送offer_ice到对等方。这边补充第一步中的peer.onicecandidatesocket.onmessage

对等方收到ice信息后,通过调用 addIceCandidate 将接收的候选者信息传递给浏览器的ICE代理。

peer.onicecandidate = e => {
    if (e.candidate) {
        socket.send(JSON.stringify({
            type: 'offer_ice',
            iceCandidate: e.candidate
        }));
    } 
};

socket.onmessage = e => {
    const { type, sdp, iceCandidate } = JSON.parse(e.data);
    if (type === 'offer_ice') {
        peer.addIceCandidate(iceCandidate);
    }
}
(4)接收方收到了offer信令后,开始获取摄像头/麦克风,与发起方操作一致。同时将收到offer SDP指定为连接的远程对等方属性(setRemoteDescription),并创建应答SDP(createAnswer),发送到对等端。这边补充第一步中的socket.onmessage
socket.onmessage = e => {
    const { type, sdp, iceCandidate } = JSON.parse(e.data);
    if (type === 'offer') {
        navigator.mediaDevices.getUserMedia();        // 与发起方一致,省略
        const offerSdp = new RTCSessionDescription({ type, sdp });
        peer.setRemoteDescription(offerSdp).then(() => {
            peer.createAnswer(answer => {
                socket.send(JSON.stringify(answer));
                peer.setLocalDescription(answer)
            });
        });
    }
}

注意:当 setLocalDescription 方法调用后,开始收集候选人信息,并发送 answer_ice 到对等方。与发送方同理,不赘述。
(5)通过不断收集ICE信息(onicecandidate),发起方和应答方最终将建立一条最优的连接方式,此时会触发 ontrack 回调,即可获取到对等方的媒体流。
peer.ontrack = e => {
    if (e && e.streams) {
        remoteVideo.srcObject = e.streams[0];
    }
};

完整示例相关代码已上传 github.com/shushushv/webrtc-p2p 
代码原网址:https://segmentfault.com/a/1190000020780854


posted @ 2021-12-24 09:56  诡道也  阅读(269)  评论(0编辑  收藏  举报