<script>
export default {
components: {},
data() {
return {
fromIm: Math.floor(Math.random()*1000),
toIm: '',
ws: '',
rtcPeer: '',
localVideo: '',
remoteVideo: '',
offerIces: [],
answerIces: [],
iceFlag: false
}
},
mounted() {
this.initWs()
},
methods:{
sender(){
if(!this.toIm){
alert("toIm不能为空")
return;
}
this.initMedia('offer')
},
initWs(){
let _this = this
//修改成你自己websocket服务端的ip和端口
_this.ws = new WebSocket("wss://www.xxxxxx.com/ws?username="+this.fromIm);
_this.ws.onopen = function(){
// Web Socket 已连接上,使用 send() 方法发送数据
alert("WebSocket连接成功");
//心跳检测
setInterval(() => {
_this.ws.send("{msgType: 'PING'}")
},5000)
};
//我自己定义的格式,你们可以根据自己修改
//ret = {msgType: 'RTC',msg: '消息体',toIm: '接收人', fromIm: '发送人'}
_this.ws.onmessage = function (ret){
var data = JSON.parse(ret.data)
console.log("收到消息",data)
if(data.msgType!=='RTC'){
return;
}
const { type, sdp, iceCandidate } = JSON.parse(data.msg)
console.log("收到消息",type,iceCandidate)
if (type === 'answer') {
_this.rtcPeer.setRemoteDescription(new RTCSessionDescription({ type, sdp }));
} else if (type === 'answer_ice') {
_this.answerIces.push(iceCandidate)
} else if (type === 'offer') {
_this.toIm = data.fromIm
_this.initMedia("answer",new RTCSessionDescription({ type, sdp }));
} else if (type === 'offer_ice') {
_this.offerIces.push(iceCandidate)
}
};
_this.ws.onclose = function(){
// 关闭 websocket
alert("WebSocket连接已关闭...");
};
},
//接收拨打方的消息,判断后添加候选人(因为addIceCandidate必须在设置描述remoteDescription之后,如果还没设置描述,我们先把它存起来,添加描述后再添加候选人)
intervalAddIce(){
let _this = this
setInterval(() => {
if(_this.rtcPeer && _this.rtcPeer.remoteDescription && _this.rtcPeer.remoteDescription.type){
if(!_this.iceFlag){
_this.iceFlag = true;
while(_this.offerIces.length>0){
let iceCandidate = _this.offerIces.shift();
_this.rtcPeer.addIceCandidate(iceCandidate).then(_=>{
console.log("success addIceCandidate()");
}).catch(e=>{
console.log("Error: Failure during addIceCandidate()",e);
});
}
while(_this.answerIces.length>0){
let iceCandidate = _this.answerIces.shift();
_this.rtcPeer.addIceCandidate(iceCandidate).then(_=>{
console.log("success addIceCandidate()");
}).catch(e=>{
console.log("Error: Failure during addIceCandidate()",e);
});
}
_this.iceFlag = false;
}
}
}, 1000);
},
//初始化媒体源
async initMedia(iceType,offerSdp){
var _this = this
//ios浏览器不判断这部分会提示不支持 (navigator.mediaDevices && navigator.mediaDevices.getUserMedia)
const UserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || (navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
if(!UserMedia){
alert("media,您的浏览器不支持访问用户媒体设备,请换一个浏览器")
return;
}
//RTCPeerConnection 接口代表一个由本地计算机到远端的WebRTC连接。该接口提供了创建,保持,监控,关闭连接的方法的实现。
const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
if(!PeerConnection){
alert("peer,您的浏览器不支持访问用户媒体设备,请换一个浏览器")
return;
}
_this.localVideo = document.getElementById('localVideo');
navigator.mediaDevices.getUserMedia({
audio: {
channelCount: {ideal: 2,min: 1}, //双声道
echoCancellation: true, //回声消除
autoGainControl: true, //修改麦克风输入音量,自动增益
noiseSuppression: true //消除背景噪声
}
// video: { //可以指定获取传入摄像头影像的宽高
// width: 400,
// height: 500
// }
,video: true
}).then(async stream => {
await _this.initPeer(iceType,PeerConnection)
_this.intervalAddIce()
//成功打开音视频流
try {
_this.localVideo.srcObject = stream;
} catch (error) {
_this.localVideo.src = await window.URL.createObjectURL(stream);
}
stream.getTracks().forEach( async track => {
await _this.rtcPeer.addTrack(track, stream);
});
if (!offerSdp) {
console.log('创建本地SDP');
const offer = await _this.rtcPeer.createOffer();
await _this.rtcPeer.setLocalDescription(offer);
console.log(`传输发起方本地SDP`,offer);
await _this.ws.send(_this.getMsgObj(offer));
} else {
console.log('接收到发送方SDP');
await _this.rtcPeer.setRemoteDescription(offerSdp);
console.log('创建接收方(应答)SDP');
const answer = await _this.rtcPeer.createAnswer();
console.log(`传输接收方(应答)SDP`);
await _this.ws.send(_this.getMsgObj(answer));
await _this.rtcPeer.setLocalDescription(answer);
}
}).catch(error => {
alert("无法开启本地媒体源:"+error);
})
},
//初始化webRtc连接
async initPeer(iceType,PeerConnection){
let _this = this
_this.remoteVideo = document.getElementById('remoteVideo');
if(!_this.rtcPeer){
var stun = "stun:120.24.202.127:3478"
var turn = "turn:120.24.202.127:3478"
var peerConfig = {
"iceServers": [{
"urls": stun
}, {
"urls": turn,
"username": "admin",
"credential": "123456"
}]
};
//如果是局域网,不需要配置穿透服务,直接传null即可
_this.rtcPeer = new PeerConnection(peerConfig);
_this.rtcPeer.onicecandidate = async (e) => {
if (e.candidate) {
console.log("搜集并发送候选人")
await _this.ws.send(_this.getMsgObj({
type: iceType+'_ice',
iceCandidate: e.candidate
}));
}else{
console.log("候选人收集完成")
}
};
_this.rtcPeer.ontrack = async(e) => {
console.log("ontrack",e)
if (e && e.streams) {
_this.remoteVideo.srcObject = await e.streams[0];
}
};
}
},
getMsgObj(msg){
var msgObj = {
fromIm: this.fromIm,
toIm: this.toIm,
msg: msg,
msgType: "RTC",
}
console.log(msgObj)
return JSON.stringify(msgObj);
}
}
}
</script>
<style scoped>
.video {
transform: rotateY(180deg);
}
.remote{
float: left;
}
.local{
}
</style>