Webrtc 视频通讯 入门体验
WebRTC,名称源自网页即时通信,它于2011年6月1日开源并在Google、Mozilla、Opera支持下被纳入万维网联盟的W3C推荐标准, 解决了网页浏览器进行实时语音对话或视频对话的问题。
转载请注明出处:https://blog.csdn.net/a1151879477/article/details/97363772
开发环境
- 最新版本的谷歌浏览器, 现在为:75.0.3770.142
- 安卓手机一部, 安装最新版本的谷歌浏览器
- 外网服务器一台,
- websocket部分使用了swoft php框架
参考链接
https://www.cnblogs.com/fangkm/p/4364553.html 这篇文章里, 讲述了浏览器在实现net穿透中的具体流程。
https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection 这个是webrtc的API文档
第一篇文章里最重要的一张流程图:
- 第一步: 创建Connection对象, 这个是webrtc Api中重要的对象之一
/*
* 创建 connection对象
*/
function createPeerConnection() {
var mediaConstraints = {};
return new RTCPeerConnection({
iceServers: [
{"urls":["stun:stun.l.google.com:19302"]}
]
}, mediaConstraints);
}
var localClient = createPeerConnection();
var answerClient = createPeerConnection();
第二步: 获取摄像头,麦克风流,获取后, 回显, 并且像Connection对象添加流
navigator.mediaDevices.getUserMedia(mediaConstraints).then(localStream => {
let localVideo = document.getElementById("local_video");
//设置回显
localVideo.srcObject = localStream;
localVideo.onloadedmetadata = function (e) {
localVideo.play();
};
localVideo.volume = 0.0;
console.log(localStream.getTracks());
localStream.getTracks().forEach(track => localClient.addTrack(track, localStream));
});
第三步: 创建Session, 并将offer发送给远程用户
localClient.createOffer()
.then(offer => {
localClient.setLocalDescription(offer)
.then(() => {
// 将offer 发送给被叫
ws.send('user.offer:' + JSON.stringify({
user_id: getUserId(),
connectUserId: $this.data('id'),
offer: offer
}))
})
})
第四步(被叫端):将受到的offer 设置进 remoteDescription, 生成被叫的answer,将被叫生成的answer 发送给主叫
const sessionDescription = new RTCSessionDescription(offer);
//由于受到的格式为json 需要转变为 RTCSessionDescription后 才能设置成功
answerClient.setRemoteDescription(sessionDescription);
answerClient.createAnswer()
.then(answer => {
answerClient.setLocalDescription(answer)
.then(() => {
//发送给主叫 answer
ws.send('user.answer:' + JSON.stringify({
user_id: getUserId(),
answer: answer,
connectUserId: msg.connectUserId
}))
})
});
第五步(主叫):受到被叫的offer 以同样的操作设置 remoteDescription
localClient.setRemoteDescription(new RTCSessionDescription(answer));
第六步(主叫):这时主叫会触发 onicecandidate
事件, 这里会自动获取自己的服务器ip端口等信息, 需要将这些信息发送给被叫
answerClient.onicecandidate = function (e) {
if (e.candidate) {
ws.send('user.candidate:', JSON.stringify({
user_id: getUserId(),
candidateType: 'answerClient',
connectUserId: remoteUserId,
candidate: e.candidate
}))
}
console.log('answerClient is on icecandidate');
};
第七步(被叫):被叫收到candidate, 调用 addIceCandidate()
将 candidate记录下, 也会触发被叫的 onicecandidate
事件, 需要进行同样的动作发送给主叫, 主叫同样调用 addIceCandidate
方法记录信息, 这时如果双方网络环境能够穿透的时候就会调用ontrack
方法, 在接受到的事件event
中, 将steam流添加到video标签里就可了。
// 调用addIceCandidate
if (candidate.candidateType === 'officeClient') {
answerClient.addIceCandidate(candidate)
} else {
localClient.addIceCandidate(candidate)
}
answerClient.onicecandidate = function (e) {
if (e.candidate) {
ws.send('user.candidate:', JSON.stringify({
user_id: getUserId(),
candidateType: 'answerClient',
connectUserId: remoteUserId,
candidate: e.candidate
}))
}
console.log('answerClient is on icecandidate');
};
localClient.onicecandidate = function (e) {
if (e.candidate) {
ws.send('user.candidate:', JSON.stringify({
user_id: getUserId(),
candidateType: 'offerClient',
connectUserId: remoteUserId,
candidate: e.candidate
}))
}
console.log('answerClient is on icecandidate');
};
answerClient.ontrack = function (e) {
remoteVideo.srcObject = e.streams[0];
};
localClient.ontrack = function(e){
remoteVideo.srcObject = e.streams[0];
};
发现的问题
有些时候会穿透失败
需要点两下对方才会显示画面, 自己这边没有对方的画面,目前没找到原因还需要继续完善
github地址: https://github.com/meiyoufengzhengdexian/webrtc-demo