前言:最近项目遇到了一个推送视频对话的需求,本身项目就用到了websocket推送,所以直接使用webRCT就行
封装了一个socket.js
export default class MSocket {
constructor(config) {
if (!config.url) {
throw new Error('websocket url is invalid');
}
this.reconnectTimer = null;
this.heartbeatTimer = null;
this.isAlive = false; // 是否已经链接
this.config = {
url: '', // 链接路径
// retry: Infinity, // 重连次数
retry: 10, // 重连次数
reconnectWait: 5 * 1000, // 重连间隔时间
heartBeatWait: 10 * 1000, // 心跳包发送间隔时间
heartMsg: 'test', // 心跳包发送数据
isHeartBeat: true // 是否开启心跳包
};
Object.keys(config).forEach(key => {
this.config[key] = config[key]
});
this.init();
}
init() {
this.socket = new WebSocket(this.config.url);
this.socket.onerror = (e) => {
this.reconnect();
this.isAlive = false;
clearInterval(this.config.heartbeatTimer);
typeof this.config.onerror === 'function' && this.config.onerror(e);
};
this.socket.onmessage = ({ data }) => {
const res = data.indexOf('{') > -1 ? JSON.parse(data) : data;
typeof this.config.onmessage === 'function' && this.config.onmessage(res);
}
this.socket.onclose = (e) => {
this.isAlive = false;
clearInterval(this.config.heartbeatTimer);
console.info('websocket was closed');
typeof this.config.onclose === 'function' && this.config.onclose(e);
}
this.socket.onopen = (e) => {
console.info('websocket was opened');
this.isAlive = true;
if (this.config.isHeartBeat) {
this.startHeart();
}
typeof this.config.onopen === 'function' && this.config.onopen(e);
}
}
// 发送数据
send(data) {
if (!this.isAlive) return
const text = typeof data === 'string' ? data : JSON.stringify(data)
this.socket.send(text)
}
// 关闭链接
close() {
this.isAlive = false;
this.socket && this.socket.close();
}
// 自动重连
reconnect() {
this.reconnectTimer = setTimeout(() => {
if (this.config.retry > 0) {
this.config.retry--
this.init()
}
}, this.config.reconnectWait)
}
// 自动心跳包
startHeart() {
this.heartbeatTimer = setInterval(() => {
this.send(this.config.heartMsg)
}, this.config.heartBeatWait)
}
}
<!-- <video id="localVideo" autoplay></video> --> //本地视频
<video id="remoteVideo" autoplay></video> // 远程视频
<el-button type="success" class="button-box" @click="cutStation">切换工位</el-button>
import MSocket from '@/utils/socket.js';
data() {
return {
mediaConstraints: {
video: false,
audio: true, // true,//由于没有麦克风,所有如果请求音频,会报错,不过不会影响视频流播放
},
TYPE_COMMAND_READY: "ready",
TYPE_COMMAND_OFFER: "offer",
TYPE_COMMAND_ANSWER: "answer",
TYPE_COMMAND_CANDIDATE: "candidate",
TYPE_COMMAND_CALLFROMCENTER: "call_from_center",
localMediaStream: "",
rtcPeerConnection: "",
offerOptions: {
iceRestart: true,
offerToReceiveAudio: true, //true,由于没有麦克风,所有如果请求音频,会报错,不过不会影响视频流播放
offerToReceiveVideo: true,
},
userId: '',
stationVlaue: 'up'
};
},
connection() {
//初始化weosocket
const _this = this
const id= this.sessionData.id|| ""
const userId = _this.userId
_this.socketed = new MSocket({
url: `ws://XXXXXXXX/ws`, // ws 链接地址
onerror: (e) => {
console.log(e, '错误信息');
},
onmessage: (event) => {
let message = event || {};
if (message.type == "peers") {
let data = message.data;
for (var item of data) {
if (item.id == id) {
_this.video(id); //是id
}
}
}
if (message.type == "offer") {
let sdpMessage = JSON.stringify(message.data.description);
sdpMessage.replace(/\n/g, "\\n").replace(/\r/g, "\\r");
let sdp = JSON.parse(sdpMessage);
//初始化一个webrtc端点 .
_this.rtcPeerConnection = new RTCPeerConnection({
bundlePolicy: "max-compat",
});
//添加事件监听函数
_this.rtcPeerConnection.onicecandidate = _this.onIceCandidate;
_this.rtcPeerConnection.ontrack = _this.onTrack;
try {
_this.rtcPeerConnection
.setRemoteDescription(new RTCSessionDescription(sdp))
.then(console.log("setRemoteDescription 完毕"));
_this.rtcPeerConnection
.createAnswer(_this.offerOptions)
.then(_this.setLocalAndAnswer)
.catch(() => {
console.log("setLocalAndAnswer error");
});
} catch (error) {
console.log(error);
}
}
if (message.type == "answer") {
let sdpMessage = JSON.stringify(message.data.description);
sdpMessage.replace(/\n/g, "\\n").replace(/\r/g, "\\r");
let sdp = JSON.parse(sdpMessage);
console.log(sdpMessage);
_this.rtcPeerConnection
.setRemoteDescription(new RTCSessionDescription(sdp))
.then(() => {
console.log("setRemoteDescription完毕");
});
// rtcPeerConnection.createAnswer(offerOptions).then(setLocalAndAnswer);
}
if (message.type == "candidate") {
let candidateMessage = JSON.stringify(message.data);
let candidate = JSON.parse(candidateMessage).candidate;
let rtcIceCandidate = new RTCIceCandidate({
sdpMid: candidate.sdpMid,
sdpMLineIndex: candidate.sdpMLineIndex,
candidate: candidate.candidate,
});
_this.rtcPeerConnection
.addIceCandidate(rtcIceCandidate)
.then(() => {
console.log("addIceCandidate完毕");
})
.catch((error) => {
console.log(error);
});
}
if (message.type == "bye") {
let remoteVideo = document.getElementById("remoteVideo");
remoteVideo.srcObject = null;
}
},
onopen: () => {
console.log("连接服务器成功");
_this.socketed.send(
JSON.stringify({
type: "new",
data: {
name: "Flutter Web",
//操作员工号
id: userId,
user_agent:
"flutter-webrtc/web-plugin 0.0.1 ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) flutter-webrtc-demo/1.0.0 Chrome/94.0.4606.71 Electron/15.1.2 Safari/537.36 )",
},
})
);
_this.openLocalMedia();
}
});
},
openLocalMedia() {
console.log(navigator, 'navigator')
window.navigator.mediaDevices
.getUserMedia(this.mediaConstraints)
.then(res => {
this.openLocalMediaSuccess(res)
})
.catch(err => {
this.openLocalMediaError(err)
});
},
openLocalMediaError(error) {
console.error("getUserMedia error: " + error);
},
openLocalMediaSuccess(mediaStream) {
// const video = document.getElementById("localVideo");
// video.srcObject = mediaStream; // 我这边不需要本地视频 需要可以放开注释
this.localMediaStream = mediaStream;
console.log(mediaStream);
this.socketed.send(
JSON.stringify({ command: this.TYPE_COMMAND_READY, userId: this.userId })
);
},
//远程视频
video(id) {
//初始化一个webrtc端点 .
const _this = this
_this.rtcPeerConnection = new RTCPeerConnection({ bundlePolicy: "max-compat" });
//添加事件监听函数
_this.rtcPeerConnection.onicecandidate = _this.onIceCandidate;
_this.rtcPeerConnection.ontrack = _this.onTrack;
try {
if (!_this.localMediaStream) {
setTimeout(() => {
for (const track of _this.localMediaStream.getTracks()) {
_this.rtcPeerConnection.addTrack(track, _this.localMediaStream);
}
_this.rtcPeerConnection
.createOffer(_this.offerOptions)
.then(_this.setLocalAndOffer)
.catch(() => {
console.log("异常211");
});
}, 1000);
} else {
for (const track of _this.localMediaStream.getTracks()) {
_this.rtcPeerConnection.addTrack(track, _this.localMediaStream);
}
_this.rtcPeerConnection
.createOffer(_this.offerOptions)
.then(_this.setLocalAndOffer)
.catch((error) => {
console.log(error);
});
}
} catch (error) {
console.log(error);
}
},
setLocalAndAnswer(sessionDescription) {
this.rtcPeerConnection.setLocalDescription(sessionDescription).then(() => {
console.log("setLocalDescription完毕");
});
const id= this.sessionData.id|| ""
this.socketed.send(
JSON.stringify({
type: this.TYPE_COMMAND_ANSWER,
data: {
//操作员工号
from: this.userId,
//车道号
to: id,
session_id: this.userId + "-" + id,
//roomId: roomId,
description: sessionDescription,
},
})
);
},
setLocalAndOffer(sessionDescription) {
try {
this.rtcPeerConnection.setLocalDescription(sessionDescription).then(() => {
console.log("setLocalDescription完毕");
});
} catch (error) {
console.log(error);
}
const id= this.sessionData.id|| ""
this.socketed.send(
JSON.stringify({
type: this.TYPE_COMMAND_OFFER,
data: {
//操作员工号
from: this.userId,
//车道号
to: id,
media: "video",
session_id: this.userId + "-" + id,
//roomId: roomId,
description: {
type: sessionDescription.type,
sdp: sessionDescription.sdp,
},
},
})
)
},
onIceCandidate(event) {
const _this = this
const id= this.sessionData.id|| ""
if (event.candidate) {
setTimeout(() => {
_this.socketed.send(
JSON.stringify({
type: _this.TYPE_COMMAND_CANDIDATE,
data: {
from: _this.userId,
to: id,
session_id: _this.userId + "-" + id,
candidate: {
sdpMid: event.candidate.sdpMid,
sdpMLineIndex: event.candidate.sdpMLineIndex,
candidate: event.candidate.candidate,
},
},
})
);
}, 1000);
}
},
onTrack(event) {
let remoteMediaStream = event.streams[0];
let remoteVideo = document.getElementById("remoteVideo");
try {
remoteVideo.srcObject = remoteMediaStream;
console.log(remoteMediaStream);
setTimeout(function () {
remoteVideo.play();
}, 200);
} catch (error) {
console.log(error);
}
},
cutStation() { //切换上下工位
if (this.socketed) {
const id= this.sessionData.id|| ""
this.socketed.send(JSON.stringify({
type: 'gala_message',
data: {
from: this.userId,
to: id,
type: "position",
value: this.stationVlaue == 'up' ? 'up' : 'down'
}
}))
this.stationVlaue == 'up' ? this.stationVlaue = 'up' : this.stationVlaue = 'down'
}
}
mounted的中调用connection 就可以了