引言
前面幾篇文章講了使用webrtc
實現本地模擬通話視頻聊天,現實網絡1對1視頻聊天以及屏幕分享和聊天隨時切換的文章,接下來就下來實戰怎么利用webrtc實現多人群視頻通話,會議
思路
- 因為
webrtc
是點對點的,通過前面文章我們也實現了相應的效果,但是多對多的實現思路也是基於此的,條件是:一對一維護一個PeerConnection
,多對多維護n
個PeerConnection
- 簡單的講就是每個客戶端維護會議室中自己和其他會議室所有人的鏈接信息
PeerConnection
, - 當會議室中有人加入進來之后,加入的一方通知服務器自己加入,服務器發送通知給其他已經在會議室的人
- 等到初始化完成每個客戶端的
PeerConnection
后,最后加入的用戶向每個成員發送offer - 每個成員監聽到offer后響應answer
- 通過onicecandidate 實現多端ICE候選,最后檢測每個客戶端視頻加入到本地video標簽並以各個用戶名稱命名元素ID
- 流程基本不變,和點對點的一樣,只不過在web端多了一些操作,以前是一個發送一個接受響應,現在是一個發送,多個監聽響應
演示
源碼地址
- 后端:
https://github.com/wangsrGit119/suc-chat-bandend
- 前段:
https://github.com/wangsrGit119/suc-love-chat
- 演示地址就不放在文章了,想要的可以直接留言,部署自己的或者遇到問題都可以問我哦
具體代碼實現
- 初始化各個客戶端的peerConnect,這個操作是每個用戶主動去創建會議或者接受邀請的時候才會去觸發的
//初始化 PeerConnection
initPeer(peerName,e){
const that = this;
console.log(peerName+"初始化PeerConnection")
let peer_tep = new PeerConnection(this.iceServers);
peer_tep.addStream(that.localStream);
peer_tep.onicecandidate = function(event) {
console.log("監聽ice候選信息",event.candidate)
if (event.candidate) {
let candidate_data = {userId:that.userInfo.userId,username:that.userInfo.username,candidate:event.candidate}
let params = {userId:that.userInfo.userId,targetId:e.userId,targetName:e.username,targetType:2,data:candidate_data}
that.socket.emit("candidate",params)
}else{
console.log("ICE收集已經完成")
}
};
peer_tep.onaddstream = (event) => {
console.log("監聽到視頻加入 加入用戶 ",e.username)
that.createEleVideo(event.stream,e.username)
};
that.userPeerList[peerName] = peer_tep;
},
- 每個剛加進來的用戶進行創建offer並發送到其他客戶端
//創建連接
async onCreateOffer() {
const that = this;
console.log("開始創建offer")
for(const ele of that.groupUserList){
let peerName = that.userInfo.username+"-"+ele.username;
if(that.userInfo.username !== ele.username){
//創建offer
let offer = await that.userPeerList[peerName].createOffer(that.offerOption);
//設置本地描述
await that.userPeerList[peerName].setLocalDescription(offer)
//遠程發送到服務器 並轉發到其他的客戶端
let data = {offer:offer,userId:that.userInfo.userId,username:that.userInfo.username,info:"發送offer"}
let params = {userId:that.userInfo.userId,targetId:ele.userId,targetName:ele.username,targetType:2,data:data}
that.socket.emit("offer",params)
}
}
},
- 監聽別的客戶端的響應
//監聽 Ice 候選
async onIceCandidate(data) {
const that = this;
let peerName = that.userInfo.username+"-"+data.data.username;
await that.userPeerList[peerName].addIceCandidate(data.data.candidate)
},
//監聽遠端offer
async onOffer(data) {
const that = this;
let peerName = that.userInfo.username+"-"+data.data.username;
await that.userPeerList[peerName].setRemoteDescription(data.data.offer)
// 接收端創建 answer
let answer = await that.userPeerList[peerName].createAnswer();
// 接收端設置本地 answer 描述
await that.userPeerList[peerName].setLocalDescription(answer);
//發送到呼叫端 answer
let answer_data = {userId:that.userInfo.userId,username:that.userInfo.username,answer:answer}
let params = {userId:that.userInfo.userId,targetId:data.data.userId,targetName:data.data.username,targetType:data.targetType,data:answer_data}
that.socket.emit("answer",params)
},
//監聽遠程響應
async onAnswer(data) {
const that = this;
let peerName = that.userInfo.username+"-"+data.data.username;
// 發送端 設置遠程 answer 描述
await that.userPeerList[peerName].setRemoteDescription(data.data.answer);
},
- 監聽到媒體流的時候創建媒體顯示,注意,監聽到的媒體以用戶名作為dom元素id,這樣移除或者禁言都可以對應到具體用戶
//追加視頻
createEleVideo(stream,id){
console.log("createEleVideo",stream);
let ele = document.getElementById("ManyToManyVideo");
let old = document.getElementById(id);
if(old){
old.srcObject = stream;
}else{
let video = document.createElement('video');
video.controls = true;
video.autoplay = true;
video.width = 230;
video.height = 200;
video.volume = 0.1;
video.id = id;
video.srcObject = stream;
ele.append(video);
}
},
最后
- 求個贊贊,有問題請留言
- 文章來源公眾號
蘇克分享