1
import { Component, OnInit, ElementRef, ViewChild } from '@angular/core';
import { Socket } from 'ngx-socket-io';
import { ToastController } from '@ionic/angular';
import { AppointmentService } from 'src/app/services/appointment.service';

@Component({
  selector: 'app-videocall',
  templateUrl: './videocall.page.html',
  styleUrls: ['./videocall.page.scss'],
})
export class VideocallPage implements OnInit {

  @ViewChild('localVideo', { static: true }) localVideo: ElementRef;
  @ViewChild('remoteVideo', { static: true }) remoteVideo: ElementRef;
  consultationRoomId: any;
  callBtn: any;
  muteBtn: any;
  localStream: MediaStream;
  remoteStream: MediaStream;
  rtcPeerConnection: RTCPeerConnection;



  constructor(public socket: Socket, private toastCtrl: ToastController, private appoint: AppointmentService,
    private elRef: ElementRef) { }


  ngOnInit() {
    this.callBtn = document.getElementById("call");
    this.muteBtn = document.getElementById("mute");
    var endBtn = document.getElementById("end");
    var divConsultingRoom = document.getElementById("consultingRoom");

    var iceServers = {
      'iceServers': [
        { urls: 'stun:stun.services.mozilla.com' },
        { urls: 'stun:stun.l.google.com:19302' },
        { urls: 'stun:stun01.sipphone.com' },
        { urls: 'stun:stun.ekiga.net' },
        { urls: 'stun:stun.fwdnet.net' },
        { urls: 'stun:stun.ideasip.com' },
        { urls: 'stun:stun.iptel.org' },
        { urls: 'stun:stun.rixtelecom.se' },
        { urls: 'stun:stun.schlund.de' },
        { urls: 'stun:stun.l.google.com:19302' },
        { urls: 'stun:stun1.l.google.com:19302' },
        { urls: 'stun:stun2.l.google.com:19302' },
        { urls: 'stun:stun3.l.google.com:19302' },
        { urls: 'stun:stun4.l.google.com:19302' },
        { urls: 'stun:stunserver.org' },
        { urls: 'stun:stun.softjoys.com' },
        { urls: 'stun:stun.voiparound.com' },
        { urls: 'stun:stun.voipbuster.com' },
        { urls: 'stun:stun.voipstunt.com' },
        { urls: 'stun:stun.voxgratia.org' },
        { urls: 'stun:stun.xten.com' },
        {
          urls: 'turn:192.158.29.39:3478?transport=udp',
          credential: 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
          username: '28224511:1379330808'
        },
        {
          urls: 'turn:192.158.29.39:3478?transport=tcp',
          credential: 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
          username: '28224511:1379330808'
        }
      ]
    }
    var streamConstraints = { audio: true, video: true };
    var isCaller;

    //this.consultationRoomId = this.appoint.getConsultationId();
    this.consultationRoomId = '1234';
    this.socket.connect();

    this.socket.on('created', (room) => {
      //debugger;
      alert('Starting consultation');
      navigator.mediaDevices.getUserMedia(streamConstraints).then((stream) => {
        this.localStream = stream;
        this.localVideo.nativeElement.srcObject = stream;
        isCaller = true;
      }).catch(function (err) {
        console.log('An error ocurred when accessing media devices', err);
      });
    });

    this.socket.on('joined', (room) => {
      alert('Joining consultation');
      navigator.mediaDevices.getUserMedia(streamConstraints).then((stream) => {
        this.localStream = stream;
        this.localVideo.nativeElement.srcObject = stream;
        this.socket.emit('ready', this.consultationRoomId);
      }).catch(function (err) {
        console.log('An error ocurred when accessing media devices', err);
      });
    });

    this.socket.on('disconnect', (room) => {
      console.log('Doctor has ended the consultation>>>' + room);
    });

    this.socket.on('candidate', (event) => {
      var candidate = new RTCIceCandidate({
        sdpMLineIndex: event.label,
        candidate: event.candidate
      });
      this.rtcPeerConnection.addIceCandidate(candidate);
    });

    this.socket.on('ready', () => {
      if (isCaller) {
        this.rtcPeerConnection = new RTCPeerConnection(iceServers);
        this.rtcPeerConnection.onicecandidate = this.onIceCandidate;
        this.rtcPeerConnection.ontrack = this.onAddStream;
        this.rtcPeerConnection.addTrack(this.localStream.getTracks()[0], this.localStream);
        this.rtcPeerConnection.addTrack(this.localStream.getTracks()[1], this.localStream);
        this.rtcPeerConnection.createOffer()
          .then(sessionDescription => {
            this.rtcPeerConnection.setLocalDescription(sessionDescription);
            this.socket.emit('offer', {
              type: 'offer',
              sdp: sessionDescription,
              room: this.consultationRoomId
            });
          })
          .catch(error => {
            console.log(error)
          })
      }
    });

    this.socket.on('offer', (event) => {
      if (!isCaller) {
        this.rtcPeerConnection = new RTCPeerConnection(iceServers);
        this.rtcPeerConnection.onicecandidate = this.onIceCandidate;
        this.rtcPeerConnection.ontrack = this.onAddStream;
        this.rtcPeerConnection.addTrack(this.localStream.getTracks()[0], this.localStream);
        this.rtcPeerConnection.addTrack(this.localStream.getTracks()[1], this.localStream);
        this.rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event));
        this.rtcPeerConnection.createAnswer()
          .then(sessionDescription => {
            this.rtcPeerConnection.setLocalDescription(sessionDescription);
            this.socket.emit('answer', {
              type: 'answer',
              sdp: sessionDescription,
              room: this.consultationRoomId
            });
          })
          .catch(error => {
            console.log(error)
          })
      }
    });

    this.socket.on('answer', (event) => {
      console.log("on answer>>>>" + event);
      this.rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event));
    })

  }

  call() {
    //this.divConsultingRoom.style = "display: block;";
    this.socket.emit('create or join', this.consultationRoomId);
  }

  mute() {
    if (this.muteBtn.innerHTML == "Mute") {
      this.localStream.getAudioTracks()[0].enabled = false;
      this.muteBtn.innerHTML = "Unmute";
    }
    else {
      this.localStream.getAudioTracks()[0].enabled = true;
      this.muteBtn.innerHTML = "Mute";
    }
  }

  end() {
    this.socket.emit('disconnect', this.consultationRoomId);
  }

  onIceCandidate = (event) => {
    if (event.candidate) {
      console.log('sending ice candidate');
      this.socket.emit('candidate', {
        type: 'candidate',
        label: event.candidate.sdpMLineIndex,
        id: event.candidate.sdpMid,
        candidate: event.candidate.candidate,
        room: this.consultationRoomId
      })
    }
  }

  onAddStream = (event) => {
    this.remoteVideo.nativeElement.srcObject = event.streams[0];
    this.remoteStream = event.streams[0];
  }

}

下面也是HTML页面

<ion-content>
    <div id="consultingRoom">
        <video id="localVideo" #localVideo inline autoplay></video>
        <video id="remoteVideo" #remoteVideo inline autoplay></video>
    </div>

    <ion-button id="call" color="primary" (click)="call()">Call</ion-button>
    <ion-button id="mute" color="secondary" (click)="mute()">Mute</ion-button>
    <ion-button id="end" color="tertiary" (click)="end()">End</ion-button>
</ion-content>

addStreamMethod 中的 event.streams[0] 对象显示带有接收者 id 的远程 MediaStream,但前端的 remoteVideo 元素仍然是空白的。信令服务器是在 AWS EC2 实例上运行的节点套接字服务器。

下面是远程媒体流的调试

RemoteMediaStream 控制台调试

以下是来自 Chrome 的 webrtc 内部日志

9/3/2020, 4:39:00 PM    
transceiverAdded
9/3/2020, 4:39:00 PM    
transceiverAdded
9/3/2020, 4:39:00 PM    
createOffer
options: {offerToReceiveVideo: -1, offerToReceiveAudio: -1, voiceActivityDetection: true, iceRestart: false}
9/3/2020, 4:39:00 PM    negotiationneeded
9/3/2020, 4:39:00 PM    
createOfferOnSuccess
9/3/2020, 4:39:00 PM    
setLocalDescription
9/3/2020, 4:39:00 PM    
transceiverModified
9/3/2020, 4:39:00 PM    
transceiverModified
9/3/2020, 4:39:00 PM    
signalingstatechange
9/3/2020, 4:39:00 PM    setLocalDescriptionOnSuccess
9/3/2020, 4:39:00 PM    
icegatheringstatechange
9/3/2020, 4:39:00 PM    
icecandidate (host)
9/3/2020, 4:39:00 PM    
icecandidate (host)
9/3/2020, 4:39:00 PM    
icecandidate (host)
9/3/2020, 4:39:00 PM    
icecandidate (host)
9/3/2020, 4:39:00 PM    
icecandidate (host)
9/3/2020, 4:39:00 PM    
icecandidate (host)
9/3/2020, 4:39:00 PM    
icecandidate (host)
9/3/2020, 4:39:00 PM    
icecandidate (host)
9/3/2020, 4:39:00 PM    
icecandidateerror
9/3/2020, 4:39:00 PM    
icecandidateerror
9/3/2020, 4:39:00 PM    
icecandidateerror
9/3/2020, 4:39:00 PM    
icecandidateerror
9/3/2020, 4:39:00 PM    
icecandidate (srflx)
9/3/2020, 4:39:00 PM    
icecandidate (srflx)
9/3/2020, 4:39:00 PM    
setRemoteDescription
9/3/2020, 4:39:00 PM    
icecandidate (srflx)
9/3/2020, 4:39:00 PM    
transceiverModified
9/3/2020, 4:39:00 PM    
transceiverModified
9/3/2020, 4:41:38 PM    
signalingstatechange
9/3/2020, 4:41:38 PM    setRemoteDescriptionOnSuccess
9/3/2020, 4:41:39 PM    
icecandidate (srflx)
9/3/2020, 4:41:39 PM    
icecandidate (srflx)
9/3/2020, 4:41:39 PM    
icecandidate (srflx)
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidate (srflx)
9/3/2020, 4:41:39 PM    
icecandidate (srflx)
9/3/2020, 4:41:39 PM    
icecandidate (srflx)
9/3/2020, 4:41:39 PM    
icecandidate (srflx)
9/3/2020, 4:41:39 PM    
icecandidate (srflx)
9/3/2020, 4:41:39 PM    
icecandidate (srflx)
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icecandidateerror
9/3/2020, 4:41:39 PM    
icegatheringstatechange
complete
9/3/2020, 4:41:39 PM    
addIceCandidate (host)
9/3/2020, 4:41:39 PM    
addIceCandidate (host)
9/3/2020, 4:41:39 PM    
iceconnectionstatechange
checking
9/3/2020, 4:41:39 PM    
addIceCandidate (host)
9/3/2020, 4:41:39 PM    
iceconnectionstatechange (legacy)
checking
9/3/2020, 4:41:39 PM    
connectionstatechange
connecting
9/3/2020, 4:41:39 PM    
addIceCandidate (host)
9/3/2020, 4:41:39 PM    
addIceCandidate (host)
9/3/2020, 4:41:39 PM    
addIceCandidate (host)
9/3/2020, 4:41:39 PM    
addIceCandidate (srflx)
9/3/2020, 4:41:39 PM    
addIceCandidate (srflx)
9/3/2020, 4:41:54 PM    
iceconnectionstatechange
disconnected
9/3/2020, 4:41:54 PM    
iceconnectionstatechange (legacy)
failed
9/3/2020, 4:41:54 PM    
connectionstatechange
failed

目前我只在浏览器上进行测试,尚未在 Android 和 IOS 上进行测试请让我知道我错过了什么?

4

0 回答 0