jssip (通話、応答、再ネゴシエーションおよびその他の機能)

jssip (通話、応答、再ネゴシエーションおよびその他の機能)

<template>
  <div class="head">
    <a-button type="primary" @click="call">call</a-button>
    <a-button type="primary">answer</a-button>
    <a-button type="primary" @click="renegotiate">renegotiate</a-button>
    <a-button type="primary" @click="terminate">terminate</a-button>
    <a-button type="primary" @click="peerBtn">对等连接</a-button>
  </div>
  <div class="main">
    <div>
      <video src="" id="selfVideo" controls></video>
      <video src="" id="remoteVideo" controls></video>
      <audio src="" id="audioElement" controls></audio>
    </div>
    <div class="message"></div>
  </div>
</template>

<script setup>
import JsSIP from "jssip";
import { onMounted } from "vue";
let currentSession;
let userAgent;
let peer;
onMounted(() => {
  sip_init();
});
var msg_log;
function sip_init() {
  msg_log = {
    el: document.querySelector(".message"),
    log(msg) {
      console.log("msg", msg);
      this.el.innerHTML += `<span class="success">${new Date().toLocaleTimeString()}:${msg}</span></br>`;
    },
    error(msg) {
      console.log("error", msg);
      this.el.innerHTML += `<span class="error">${new Date().toLocaleTimeString()}:${msg}</span></br>`;
    },
  };
  const name = location.search.slice(6);
  if (!name) {
    return msg_log.error("location.search获取不到信息");
  }
  const selfVideo = document.querySelector("#selfVideo");
  const remoteVideo = document.querySelector("#remoteVideo");
  // 本地加载完成 对端加载完成
  const socket = new JsSIP.WebSocketInterface("ws://101.200.183.204:5062");
  const configuration = {
    sockets: [socket],
    uri: `sip:${name === "offer" ? 2001 : 2002}@172.24.66.100;transport=ws`,
    password: "67975111",
    register: true,
    session_timers: false,
  };
  var ua = new JsSIP.UA(configuration);
  ua.on("connected", () => msg_log.log("连线中"));
  ua.on("connecting", () => msg_log.log("接线中"));
  ua.on("disconnected", () => msg_log.error("取消连线"));
  ua.on("registered", () =>
    msg_log.log(`--${name === "offer" ? 2001 : 2002}注册成功`)
  );
  ua.on("registrationExpiring", () => msg_log.log("注册即将到期,重新注册"));
  ua.on("registrationFailed", () => msg_log.error("注册失败"));
  ua.on("unregistered", () => msg_log.log("取消注册"));
  ua.on("sipEvent", () => msg_log.log("sipEvent"));
  ua.on("newRTCSession", function (data) {
    const { session, request, originator } = data;
    if (originator === "remote") {
      msg_log.log("对方打电话过来了~~~");
    } else {
      msg_log.log("拨打电话中~~~");
    }
    currentSession = session;
    session.on("accepted", () => msg_log.log("通话接受时候触发"));
    session.on("connecting", () => msg_log.log("通话连线时候触发"));
    session.on("sdp", () => msg_log.log("交换sdp信令事件触发"));
    session.on("failed", () => msg_log.log("通话失败事件触发"));
    session.on("reinvite", () => {
      openLocalCamera();
      msg_log.log("重新协商事件触发");
      audioElement.srcObject = null;
      if (session._connection.getLocalStreams().length > 0) {
        // 接听后,判断localStream
        selfVideo.srcObject = session?._connection.getLocalStreams()[0];
        selfVideo.play();
      }
      if (session?._connection.getRemoteStreams().length > 0) {
        remoteVideo.srcObject = session?._connection.getRemoteStreams()[0];
        remoteVideo.play();
      }
    });
    session.on("progress", () => {
      if (originator === "remote") {
        msg_log.log("电话过来拉~~~~~~~~~··");
        // var flag = confirm("是否接听?");
        // if (!flag) {
        //   ua?.terminateSessions();
        //   return;
        // }
        session.answer({
          mediaConstraints: { audio: true, video: true },
          // mediaStream: localStream,
        });
        msg_log.log("我接听了");
      }
      msg_log.log("接听事件在progress中触发");
    });
    session.on("confirmed", () => {
      msg_log.log("呼叫确认--设置媒体流到音视频中");
      selfVideo.srcObject = null;
      remoteVideo.srcObject = null;
      const stream = new MediaStream();
      const receivers = currentSession.connection?.getReceivers();
      if (receivers)
        receivers.forEach((receiver) => stream.addTrack(receiver.track));
      audioElement.srcObject = stream;
      // 最后都要播放
      audioElement.oncanplay = () => {
        audioElement.play();
      };
    });
    session.on("peerconnection", (data) => {
      msg_log.log("对等连接事件触发");
    });
    session.on("connecting", (data) => {
      peer = session._connection;
      console.log(peer, data, "wewewewewewewew");
      msg_log.log("对等连接建立,connecting");
    });
    session.on("ended", () => msg_log.log("通话结束"));
  });
  userAgent = ua;
  ua.start();
}
const eventHandlers = {
  progress: function (e) {
    console.log("call is in progress");
  },
  failed: function (e) {
    console.log("call failed: ", e);
  },
  ended: function (e) {
    console.log("call ended : ", e);
  },
  confirmed: function (e) {
    console.log("call confirmed");
  },
};
function call() {
  const opt = {
    mediaConstraints: {
      audio: true,
      video: false,
    },
    eventHandlers,
  };
  msg_log.log("1001 呼叫");
  userAgent.call("sip:[email protected]", opt);
}
// 重新协商
function renegotiate() {
  // openLocalCamera();
  var options = {
    useUpdate: false,
    rtcOfferConstraints: {
      mandatory: {
        OfferToReceiveAudio: true,
        OfferToReceiveVideo: true,
      },
    },
    pcConfig: {
      rtcpMuxPolicy: "negotiate",
      iceServers: [{ urls: ["stun:stun.l.google.com:19302"] }],
    },
  };
  currentSession.renegotiate(options, () => {
    audioElement.srcObject = null;
    if (currentSession._connection.getLocalStreams().length > 0) {
      // 接听后,判断localStream
      selfVideo.srcObject = currentSession?._connection.getLocalStreams()[0];
      selfVideo.play();
    }
    if (currentSession?._connection.getRemoteStreams().length > 0) {
      remoteVideo.srcObject = currentSession?._connection.getRemoteStreams()[0];
      remoteVideo.play();
    }
    console.log("我重新协商成功了~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
  });
}
function terminate() {
  currentSession.terminate();
}
//开启本地摄像头,并把当前的媒体流添加到peerConnection中
function openLocalCamera() {
  // 1.获取本地音视频流
  // 调用 getUserMedia API 获取音视频流
  let constraints = {
    video: true,
    // audio: true,
    // audio: {
    //     // 设置回音消除
    //     noiseSuppression: true,
    //     // 设置降噪
    //     echoCancellation: true,
    // }
  };
  navigator.mediaDevices
    .getUserMedia(constraints)
    .then(gotLocalMediaStream)
    .catch((err) => {
      console.log("getUserMedia 错误", err);
    });
}
async function gotLocalMediaStream(mediaStream) {
  selfVideo.srcObject = mediaStream;
  selfVideo.play();
  const mediaStreamTrack = mediaStream.getTracks()[0];
  const localStream = mediaStream;
  localStream.getTracks().forEach((track) => {
    peer.addTrack(track, mediaStream);
  });
  // 这里每次开启本地视频流都要重新发送offer到对端
  // await peer.setLocalDescription(offer);
}
//关闭本地的视频流
function closeLocalMedia() {
  peerConnection.removeTrack(this.rtpSender);
}
async function peerBtn() {
  console.log(peer, "peer");
  // const await navigator.
  const stream = await navigator.mediaDevices.getUserMedia({ video: true });
  stream.getTracks().forEach((track) => {
    peer.addTrack(track, stream);
  });
  selfVideo.srcObject = stream;
  selfVideo.play();
  // openLocalCamera();
}
// 在关闭视频流之后,video标签会显示最后一帧的画面,如果想使video标签展示初始状态,可以调用video.load()
// video.load(); //视频回到初始状态
</script>

<style lang="less">
.head {
  height: 50px;
  background-color: #ccc;
  display: flex;
  align-items: center;
  margin-bottom: 20px;
  button {
    margin: 0 10px;
  }
}
.main {
  display: flex;
  .message {
    margin-left: 100px;
    background-color: rgba(0, 0, 0, 0.729);
    width: 500px;
    border: 2px solid #000;
    span.success {
      color: green;
    }
    span.error {
      color: red;
    }
  }
}
</style>

http://localhost:8080/?type=オファー送信者

http://localhost:8080/?type=answer リスナー

おすすめ

転載: blog.csdn.net/m0_51531365/article/details/127597728