websocked主要实现文本信息、二进制图片录音等消息通讯web
webRtc主要实现视频音频流的通讯浏览器
getUserMedia主要实现获取浏览器摄像头麦克风以及获取视频音频流socket
首先得创建一个webScoked对象,用webSocked几个经常使用的事件去监听webSocked的工做状态,在onopen事件中监听到webSocked链接成功,在须要的地方使用send方法发送消息,就能够在onmessage事件中监听通讯消息并处理,当发生意外错误或者须要关闭webSocked的时候就会调用oncloseide
部分代码
init() {
if (this.brid) {
let url = window.config.baseConfig.socketUrl + this.brid
this.webSock = new WebSocket(url)
this.webSock.onopen = this.webSockOpen
this.webSock.onerror = this.webSocketError
this.webSock.onmessage = this.webSocketMsg
this.webSock.onclose = this.webSocketClose
}
},
webSockOpen() {
console.log('链接成功!')
},
webSocketMsg(evt) {
try {
let data = JSON.parse(evt.data), id = data.id, i = this.tempMsg[id]
if (data.code === 1) {
this.msgs[i].code = 1
} else if (data.code === 2 && !i) {
this.tempMsg[id] = true
this.msgs.push(data)
this.webSock.send(JSON.stringify({code: '3', id: id}))
}
} catch (e) {}
},
webSocketError(e) {
console.error(e)
},
webSocketClose() {
console.warn('关闭webSock')
},
send(val) {
let id = this.brid + new Date().getTime(), data = {
code: '0',
id: id,
from: this.brid,
to: this.to,
msg: this.value || val
}
this.webSock.send(JSON.stringify(data))
this.$refs.input.innerHTML = ''
this.value = ''
this.tempMsg[id] = this.msgs.length
this.msgs.push(data)
}
复制代码
接下来获取浏览器摄像头、麦克风以及获取数据流,须要注意的是浏览器兼容性this
navigator.mediaDevices.getUserMedia({
//constraints
video: true,
audio: true
}).then(stream => {
const video = document.querySelector('video');
video.srcObject = stream;
}).catch(error => {
alert('error: ', error)
})
复制代码
其中constraints 约束对象,咱们能够用来指定一些和媒体流有关的属性。好比指定是否获取某种流,指定视频流的宽高、帧率以及理想值,对于移动设备来讲,还能够指定获取前摄像头,或者后置摄像头。url
最后三者结合实现一个数据流的通讯,通讯大体流程以下:spa
一、发送方启动本地视频(获取浏览器摄像头、麦克风)并添加至通道中,发送视频邀请 {event: 'invite', to: '接收方id', type: '1'}code
二、如发送方在接收方以前取消电话,需通知对方已取消{event: 'cancle', to: '接收方id', type: '1'}视频
三、如接收方挂断了电话,需通知对方已经挂断电话{event: 'close', to: '接收方id', type: '1'}对象
四、若是接收方接听了电话,须要启动本地视频并添加至通道中(获取浏览器摄像头、麦克风)需回复发起方{event: 'reply', to: '接收方id', type: '1'}
5.当发起方收到reply后,发送iceCandidate,officeSdp
六、接收方会执行addIceCandidate(new RTCIceCandidate(JSON.parse(data.msg))),setRemoteDescription(new RTCSessionDescription(JSON.parse(data.msg))),而后在回复发起方answerSdp
七、发起方接收到answerSdp,执行setRemoteDescription(new RTCSessionDescription(JSON.parse(data.msg)))
部分实现代码
creatRTCPreeConnection() {
// let iceServer = {iceServer: {'iceServers': [{'url': 'turn:172.16.113.146:3478', 'username': 'daiyao', 'credential': 'daiyao'}]}}
let iceServer = {'iceServers': [{'url': 'stun:stun.voiparound.com'}]}
this.cn = new RTCPeerConnection(iceServer)
console.log(this.cn)
this.cn.onaddstream = (event) => {
if (this.isVideo) {
document.getElementById('remoteVideo').srcObject = event.stream
} else {
document.getElementById('remoteAudio').srcObject = event.stream
}
}
},
video() {
this.showTh = true
let thObj = {
event: 'invite',
to: '2815718',
// to: '4223',
type: '1',
tips: '发送视频邀请'
}
this.sendth(thObj)
},
sendth(val) {
console.log(val)
this.webSock.send(JSON.stringify(val))
},
dealVideoMsg(data) {
if (data.event === 'invite') {
this.isShow.invite = data.event
this.showTh = true
if (data.kind === 'audio') {
this.isShow.media = 'video'
this.isVideo = false
}
if (this.isOpen) {
this.creatRTCPreeConnection()
this.GetUserMedia(data.from, false)
let thObj = {
type: '1',
event: 'reply',
to: data.from,
tips: '接收到' + data.from + '视频邀约,回复能够通话'
}
this.sendth(thObj)
this.isShow.invite = null
this.isShow.all = 'all'
}
} else if (data.event === 'reply') {
this.isShow.reply = data.event
this.creatRTCPreeConnection()
this.sendCandidate(data.from)
this.GetUserMedia(data.from, true)
} else if (data.event === 'candidate') {
this.cn.addIceCandidate(new RTCIceCandidate(JSON.parse(data.msg)))
} else if (data.event === 'offerSdp') {
this.cn.setRemoteDescription(new RTCSessionDescription(JSON.parse(data.msg)))
this.createAnswer(data.from)
} else if (data.event === 'answerSdp') {
this.cn.setRemoteDescription(new RTCSessionDescription(JSON.parse(data.msg)))
}
},
GetUserMedia(toUserId, isCaller) {
navigator.webkitGetUserMedia({'audio': this.isAudio, 'video': this.isVideo},
(stream) => {
console.log(this.isVideo)
if (this.isVideo) {
console.log(this.isVideo)
document.getElementById('localVideo').srcObject = stream
} else {
document.getElementById('localAudio').srcObject = stream
}
this.cn.addStream(stream)
if (isCaller) {
this.creatOf(toUserId)
}
}, (error) => {
console.log('getUserMedia error: ' + error)
}
)
},
sendCandidate(toUserId) {
console.log(this.cn)
console.log(this.cn.onicecandidate)
this.cn.onicecandidate = (event) => {
console.log(event.candidate)
if (event.candidate !== null) {
var thObj = {
type: '1',
event: 'candidate',
msg: event.candidate,
to: toUserId,
tips: '发送rtc候选人信息,进入可连线视频列表'
}
this.sendth(thObj)
}
}
},
creatOf(toUserId) {
this.cn.createOffer((offerSdp) => {
console.log('1')
this.cn.setLocalDescription(offerSdp)
var thObj = {
type: '1',
msg: offerSdp,
event: 'offerSdp',
to: toUserId,
tips: 'offerSdp'
}
console.log('111')
this.sendth(thObj)
}, (error) => {
console.log('Failure callback:' + error)
})
},
createAnswer(toUserId) {
this.cn.createAnswer((answerSdp) => {
this.cn.setLocalDescription(answerSdp)
var thObj = {
type: '1',
msg: answerSdp,
event: 'answerSdp',
to: toUserId,
tips: 'answerSdp'
}
this.sendth(thObj)
}, (error) => {
console.log('Failure callback:' + error)
})
}
复制代码