65.9K
CodeProject 正在变化。 阅读更多。
Home

4 步实现基于 WebRTC 的多方视频会议

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2018 年 4 月 22 日

CPOL

3分钟阅读

viewsIcon

12829

4 步轻松实现多方视频会议

引言

Simple-peer 是一个优秀的库,可以使开发 WebRTC 解决方案变得易如反掌。它最好的地方在于它优雅地隐藏了所有的复杂性,并为您提供了一个易于使用的 WebRTC 接口,用于视频会议和数据传输。由于屏幕共享几乎与传递视频数据相同,因此您也可以将其用于屏幕广播。

让我将其分解为几个简单的步骤。

步骤 1

https://github.com/feross/simple-peer 获取 simplepeer.min.js,并将其包含在您的 HTML 文件中。

第二步

找到一个 websocket 库,用于来回传递数据进行初始通信。
在我的例子中,我使用了 easyrtc (https://github.com/priologic/easyrtc)。

步骤 3

围绕 websocket 层编写一个包装器,以便在主代码中,socket 库不会被暴露。这样,您就可以随时更改内部 websocket 库,而无需更改 WebRTC 通信建立的代码。

这是我创建的一个名为 hub (hub.js) 的包装器。在 hub.js 内部,.on 方法用于监听 custom/user-defined 事件。每当有新的 peer 加入时,我都会抛出带有 peer id 的 app.peer 事件(例如在步骤 4 中)。 同样,还有一些由内部 websocket 库引发的其他事件,这些事件会妥善传递到 hub。

var hub = { 

    msgMap: {}, 
    
    connect:function connect(url){
        easyrtcConnect(url);
        },
    
    send: function(peerid, msgType, content){
        easyrtc.sendDataWS(peerid, msgType,  content);
    },

    sendToAll: function(msgType, content){
        easyrtc.sendDataWS({targetRoom:"default"}, msgType, content);        
    },

    on: function(type, callback){
        this.msgMap[type] = callback;
    },
    
    event: function(peerid, type, msg){
        var callback = this.msgMap[type];
        if(callback){
            callback(peerid, msg);
        };      
    },

    peerMap: {},

    setPeer: function(peerid, peer){
        this.peerMap[peerid] = peer;
    },
    
    getPeer: function(peerid){
        return this.peerMap[peerid];
    },

    removePeer: function(peerid){
        delete this.peerMap[peerid];
    },

    iteratePeers: function(callback){
        var value;
        for (var key in this.peerMap) {
                value = this.peerMap[key];
                callback(key, value);
        }
    }    
};

步骤 4

如前所述,当新的 peer/用户加入时(即,在线/连接到 socket),我会通知 hub。

hub.event(easyrtcid, "app.peer", userid);

为了进行多方会议,最好的方法是让最新在线的用户在登录时向所有其他 peer 发送一个“hello”消息。
easyrtc 库在加入时会立即给我提供在线连接的列表。从内部层,我抛出带有 easyrtc id(即 socket.id)的 app.peer 事件。对于多个连接(即用户),会多次抛出 hub.event(easyrtcid, "app.peer", userid)

hub.on('app.peer', function(peerid, userid){
    
    hub.send(peerid, 'hello'); // sending hello
});

稍微比喻一下,就会得到以下结果

-----------------------|- HELLO -> User A
User D-->Sends-->------|- HELLO -> User B
-----------------------|- HELLO -> User C

当收到呼叫请求时,用户默认接受。

在接受时,我们创建用于 WebRTC 连接的 SimplePeer 对象。
请查阅 simple-peer 的文档以了解初始化和事件。

hub.on('hello', function (peerid, msg) {         
    var peer = new SimplePeer({ initiator: false, stream: localStream });       
    hub.setPeer(peerid, peer);
    peer.on('signal', function (data) {        
        hub.send(peerid, 'signal', data);    
    });
    peerCreated(peerid, peer);
    hub.send(peerid, 'ack', '1'); // sending acknowledgement  
});

请注意,上述事件处理程序用于接收新连接用户的“hello”,作为回报,我们发送一个“ack”。

ON HELLO 用户 A ---- 发送 ACK ----> 用户 D

ON HELLO 用户 B ---- 发送 ACK ----> 用户 D

ON HELLO 用户 C ---- 发送 ACK ----> 用户 D

剩下的很简单,当发起通信的用户收到 ack 时,他/她会创建一个作为发起者的 SimplePeer 对象。剩下的就是将 SDP Offer 信息传递给另一方,当 simple-peer 库优雅地通过其 signal 事件通知时:peer.on('signal' .. )

hub.on('ack', function (peerid, msg) {
    //debugger;
    if(msg == "1"){
        var peer = new SimplePeer({ initiator: true, stream: localStream});    
        hub.setPeer(peerid, peer);          
        peer.on('signal', function (data) {
            hub.send(peerid, 'signal', data);
        });
        peerCreated(peerid, peer);
    }
});

peerCreated 方法内部,我们根据 simple-peer 文档执行必要的措施。

function peerCreated(peerid, peer){
    
    peer.peerid = peerid; // you can choose to skip this

    //debugger;

    peer.on('connect', function () {
        console.log('CONNECT')
        peer.send('call established .. ' + selfID);
    });
    
    peer.on('error', function (err) { console.log('error', err) });
    

    peer.on('data', function (data) {
        console.log('data: ' + data)
    });

    peer.on('stream', function (stream) {
        console.log('new stream arrived .. ', this.peerid); 
        
        createRemoteVideoElement(peerid, stream);            
    });

    peer.on('track', function (track, stream) {
        console.log('new track arrived .. ', this.peerid);  
        
        createRemoteVideoTrackElement(peerid, track, stream);            
    });

    peer.on('removestream', function (stream) {
        //removeRemoteVideoElement(peerid); 
        console.log("stream removed .. ", peerid); // hardly called
    });

    peer.on('close', function () {
        console.log("connection closed .. ", peerid);
        removeRemoteVideoElement(peerid);    
    });   
}

但是,要开始进行操作,我们需要获取摄像头和麦克风访问权限(用户媒体流),并将其存储在 localStream 变量中

// get video/voice stream
navigator.getUserMedia({ video: true, audio: true }, gotMedia, function () {})

// This method starts of proceedings
function gotMedia (ownstream) {

    localStream = ownstream;
        
    connectToSocket(); // once socket connects we receive "app.peer" on hub

    var video = document.getElementById('me');
        video.srcObject = ownstream;
        video.play();
}

 

历史

  • 2018 年 4 月 23 日:文章上传
© . All rights reserved.