点对点视频通话

446 阅读1分钟

(1)

html部分

  <div class="container">
    <video id="localVideo" playsinline autoplay muted></video>
    <video id="remoteVideo" playsinline autoplay></video>
    <div class="box">
      <button @click="start" :disabled="startdisabled">
        开始
      </button>
      <button @click="call" :disabled="calldisabled">
        呼叫
      </button>
      <button class="hangupButton" @click="hangup" :disabled="disabled">
        离开
      </button>
    </div>
    <div class="box">
      <span>SDP Semantics:</span>
      <select id="sdpSemantics">
        <option selected value="">Default</option>
        <option value="unified-plan"> Plan</option>
        <option value="plan-b">Plan B</option>
      </select>
    </div>
  </div>

js部分

    export default{
        data(){
              calldisabled: true,
              disabled: true,
              startdisabled: false,
              startTime: null,
              pc1: null,
              pc2: null,
              localStream: null,
              sdpSemanticsSelect: null,
              offerOptions: {
                offerToReceiveAudio: 1,
                offerToReceiveVideo: 1,
            },
        },
        mounted(){
            const localVideo = document.getElementById('localVideo')
            const remoteVideo = document.getElementById('remoteVideo')
            this.sdpSemanticsSelect = document.querySelector('#sdpSemantics')
        },
        methods:{
            getName(pc) {
                  return pc === this.pc1 ? this.pc1 : this.pc2
                },
                
                
                
            getOtherPc(pc) {
                  return pc === this.pc1 ? this.pc2 : this.pc1
                },
                
                
            async start(){
                this.startdisabled = true //开始按钮禁用
                try{
                    const stream = await  navigator.mediaDevices.getUserMedia({audio: true,video: true,})
                    //点击开始打开摄像头和音频
                    this.calldisabled = false //呼叫按钮取消禁用
                    localVideo.srcObject = stream //视频流给到第一个视频对象
                    this.localStream = stream 
                }catch (e) {
                    alert(`getUserMedia() error: ${e.name}`)
                }
            },
            
            
            
            getSelectedSdpSemantics() {
                  const option = this.sdpSemanticsSelect.options[
                    this.sdpSemanticsSelect.selectedIndex
                  ]
                  return option
            },
            
            
            async call(){
                this.calldisabled = true
                this.disabled = false
                //开始按钮禁用 离开按钮取消禁用
                this.startTime = window.performance.now()//获取精确的时间
                //localStream 视频轨道的序列
                const videoTracks = this.localStream.getVideoTracks() 
                //音频轨道的对象序列
                const audioTracks = this.localStream.getAudioTracks()
                const configuration = this.getSelectedSdpSemantics() 
                //建立数据通信
                this.pc1 = new RTCPeerConnection(configuration) 
                this.pc1.addEventListener('icecandidate', (e) =>
                    this.onIceCandidate(this.pc1, e)
                  )
                //IceCandidate是一个模板类,里面主要包含着会话描述协议。
                this.pc2 = new RTCPeerConnection(configuration)
                this.pc2.addEventListener('icecandidate', (e) =>
                    this.onIceCandidate(this.pc2, e)
                )
                //iceConnectionState 是一个只读属性,用于描述连接的ICE连接状态。
               this.pc1.addEventListener('iceconnectionstatechange', (e) =>
                    this.onIceStateChange(this.pc1, e)
                )
                 
                this.pc2.addEventListener('iceconnectionstatechange', (e) =>
                    this.onIceStateChange(this.pc2, e)
                )
                //track视频轨道
                this.pc2.addEventListener('track', (e) => this.gotRemoteStream(e))
                try {
                    let offerOptions = this.offerOptions
                    const offer = await this.pc1.createOffer(offerOptions)
                    await this.onCreateOfferSuccess(offer)
                  } catch (e) {
                   console.log(`Failed to set session description: ${e.toString()}`)
                }
            },
            
            
            async onCreateOfferSuccess(desc) {
              try {
                await this.pc1.setLocalDescription(desc) //本地描述
                console.log(`${this.getName(this.pc1)} setLocalDescription complete`)
              } catch (e) {
                console.log(`Failed to set session description: ${e.toString()}`)
              }
              try {
                await this.pc2.setRemoteDescription(desc) //远程描述
                console.log(`${this.getName(this.pc2)} setLocalDescription complete`)
              } catch (e) {
                 console.log(`Failed to set session description: ${e.toString()}`)
              }
              try {
                const answer = await this.pc2.createAnswer()
                await this.onCreateAnswerSuccess(answer)
              } catch (e) {
                console.log(`Failed to set session description: ${e.toString()}`)
              }
            },
            
            
            async onCreateAnswerSuccess(desc) {
              try {
                await this.pc2.setLocalDescription(desc)
                 console.log(`${this.getName(this.pc2)} setLocalDescription complete`)
              } catch (e) {
                 console.log(`Failed to set session description: ${e.toString()}`)
              }
              try {
                await this.pc1.setRemoteDescription(desc)
                console.log(`${this.getName(pc1)} setRemoteDescription complete`)
              } catch (e) {
                console.log(`Failed to set session description: ${e.toString()}`)
              }
            },
            
            
            async onIceCandidate(pc, event) {
              try {
                await this.getOtherPc(pc).addIceCandidate(event.candidate)
                console.log(`${this.getName(pc)} addIceCandidate success`)
              } catch (e) {
               console.log(`${this.getName(pc)} failed to add ICE Candidate: ${e.toString()}`)
              }
                console.log(`${this.getName(pc)} ICE candidate:\n${
                  event.candidate ? event.candidate.candidate : '(null)'}`)
            },
            
            hangup() {
                  this.pc1.close()
                  this.pc2.close()
                  this.pc1 = null
                  this.pc2 = null
                  this.calldisabled = true
                  this.disabled = false
            },
        }
    }
    

效果大概就是这样,一边呼叫另一端加入视频聊天, 后台还在写。。。