webRCT实时语音视频通话 结合 vue使用

前言:最近项目遇到了一个推送视频对话的需求,本身项目就用到了websocket推送,所以直接使用webRCT就行

使用websocket

封装了一个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)
  }
}

template

  <!-- <video id="localVideo" autoplay></video> --> //本地视频
        <video id="remoteVideo" autoplay></video> // 远程视频
        <el-button type="success" class="button-box" @click="cutStation">切换工位</el-button>

引入和data数据

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 就可以了

你可能感兴趣的:(vue.js,音视频,javascript)