首先了解一下peerJS, 个人简单的理解为使用peerJS可以绕过nat拦截,实现两个客户端之间直接通信,可以先看一下Peerjs文档 ,下面就开始吧。
既然是音视频通话,首先需要检查有没有音视频设备,代码如下:
refreshMediaDevices () {
window.mediaDevices = {
'videoinput': [],
'audioinput': [],
};
navigator.mediaDevices.enumerateDevices().then((devices) => {
devices.forEach((device) => {
switch (device.kind) {
case 'videoinput':
window.mediaDevices.videoinput.push(device);
break;
case 'audioinput':
window.mediaDevices.audioinput.push(device);
break;
}
});
this.$Bus.$emit('on.refreshMediaDevices.finished');
}).catch((e) => {
console.info('获取音视频设备信息失败:', e);
});
},
提醒一下,这里检查设备时需要定时刷新,不然可能识别不到设备
(window.refreshMediaDevices = this.refreshMediaDevices)();
this.refreshMediaDevicesTimer = window.setInterval(window.refreshMediaDevices, 2000);
接下来就是注册Peer, 注册之前先断开之前的连接防止重复注册,刚开始我使用的peerID是固定的,后来改为自定义的随机数,
因为如果不断刷新页面的话难免会出现相同的id注册多次,每次都是随机数,客户端B在接收的之前也是先获取(通过socket)客户端A的peerID:
crtPeer () {
this.desPeer();
// peer
// 随机生成peerId
let peerId = 'YX_' + Date.now() + Math.floor(Math.random() * (100 - 1)) + 1;
// 放入socket
this.parent.setPeerId(peerId);
let peer = this.peer = window.peer = new Peer(peerId, {
config: {
iceServers: pkg.iceServers.map((url) => {
return { url };
}),
},
});
// peer.on.open
peer.on('open', (id) => {
console.info(`Peer注册成功!`, id);
});
peer.on('error', (error) => {
this.$message.error({
message: `Peer错误: [${error.type}],请刷新页面重试`,
});
});
// ##
// peer.on.connection
peer.on('connection', (conn) => {
console.info(`peer.on.connection`, conn);
this.$message.info(`peer.on.connection:${conn.peer}`);
});
// ##
// peer.on.disconnected
peer.on('disconnected', () => {
console.info('peer.on.disconnected');
console.info('peer.reconnect():begin...');
peer.reconnect();
console.info('peer.reconnect():finish...');
});
},
双方注册成功以后,就要开始处理呼叫和被呼叫,首先呼叫,呼叫前先检查peer是否注册,是否有音视频设备等等。
let localStream = this.localStream = window.localStream = await this.getUserMedia();
this.mediaConnection && this.mediaConnection.close && this.mediaConnection.close();
let call = this.mediaConnection = window.peer.call(this.peerIdRemote, localStream);
// remoteStream
call.on('stream', (remoteStream) => {
this.remoteStream = window.remoteStream = remoteStream;
this.showLocalVideo(localStream);
this.showRemoteVideo(remoteStream);
// 开始录制音频
this.recordStart();
this.$message.success('远程通话已接通!');
});
call.on('close', () => {
this.clearStream();
});
call.on('error', () => {
this.clearStream();
});
// 获取用户媒体流
async getUserMedia () {
// ##
// localStream
this.localStream = window.localStream = await navigator.mediaDevices.getUserMedia({
video: this.callType === 1 ? this.videoDeviceEnabledCaller : false,
audio: this.audioDeviceEnabledCaller,
});
return this.localStream;
},
这里先拿到了本地视频流,用于连接显示本地视频,peerIdRemote就是获取的客户端B注册的peerId,到这里基本呼叫基本完成了,然后在监听一个被呼叫的方法,直接上代码:
setTimeout(() => {
window.peer.on('call', async (call) => {
console.info('on.call', call);
this.mediaConnection && this.mediaConnection.close && this.mediaConnection.close();
this.mediaConnection = call;
try {
// ##
// localStream
let localStream = await this.getUserMedia();
this.showLocalVideo(localStream);
// ##
// remoteStream
call.on('stream', (remoteStream) => {
if (this.callType === 1) {
this.remoteStream = window.remoteStream = remoteStream;
this.showRemoteVideo(remoteStream);
// 开始录制音频
this.recordStart();
this.$message.success('远程通话已接通!');
});
call.on('close', () => {
this.clearStream();
});
call.on('error', () => {
this.clearStream();
});
let msg = this.callType === 2 ? '音频' : '视频';
// 确认请求
this.$confirm(`xxx请求与您进行` + msg + `通话!`, '通话请求', {
confirmButtonText: '接通',
cancelButtonText: '拒绝',
type: 'warning',
}).then(() => {
this.parent.isCalling = true;
this.peerIdRemote = call.peer;
call.answer(localStream); // Answer the call with an A/V stream.
}).catch(() => {
this.clearStream();
});
} catch (e) {
console.log('Failed to get local stream', e);
}
});
}, 1000);
这样基本的通话功能就可以实现了,除了视频通话,还可以实现语音,聊天等功能,再说一点就是呼叫时网络不好的话,对方会有延迟,或者挂断以后需要提示等等,可以结合socket使用,体验会更好一些,本人研究的不是太多,只是基本的使用,如果感兴趣的话可以多看看这方面的资料,如果有什么不对的欢迎指出,有问题也欢迎提问(*^▽^*)。