美文网首页
WebRTC学习-本地使用RTCPeerConnection

WebRTC学习-本地使用RTCPeerConnection

作者: BugMaker | 来源:发表于2021-05-07 10:41 被阅读0次

什么是RTCPeerConnection

/** A WebRTC connection between the local computer and a remote peer. It provides methods to connect to a remote peer, maintain and monitor the connection, and close the connection once it's no longer needed. */
interface RTCPeerConnection extends EventTarget {
    readonly canTrickleIceCandidates: boolean | null;
    readonly connectionState: RTCPeerConnectionState;
    readonly currentLocalDescription: RTCSessionDescription | null;
    readonly currentRemoteDescription: RTCSessionDescription | null;
    readonly iceConnectionState: RTCIceConnectionState;
    readonly iceGatheringState: RTCIceGatheringState;
    readonly idpErrorInfo: string | null;
    readonly idpLoginUrl: string | null;
    readonly localDescription: RTCSessionDescription | null;
    onconnectionstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
    ondatachannel: ((this: RTCPeerConnection, ev: RTCDataChannelEvent) => any) | null;
    onicecandidate: ((this: RTCPeerConnection, ev: RTCPeerConnectionIceEvent) => any) | null;
    onicecandidateerror: ((this: RTCPeerConnection, ev: RTCPeerConnectionIceErrorEvent) => any) | null;
    oniceconnectionstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
    onicegatheringstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
    onnegotiationneeded: ((this: RTCPeerConnection, ev: Event) => any) | null;
    onsignalingstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
    onstatsended: ((this: RTCPeerConnection, ev: RTCStatsEvent) => any) | null;
    ontrack: ((this: RTCPeerConnection, ev: RTCTrackEvent) => any) | null;
    readonly peerIdentity: Promise<RTCIdentityAssertion>;
    readonly pendingLocalDescription: RTCSessionDescription | null;
    readonly pendingRemoteDescription: RTCSessionDescription | null;
    readonly remoteDescription: RTCSessionDescription | null;
    readonly sctp: RTCSctpTransport | null;
    readonly signalingState: RTCSignalingState;
    addIceCandidate(candidate: RTCIceCandidateInit | RTCIceCandidate): Promise<void>;
    addTrack(track: MediaStreamTrack, ...streams: MediaStream[]): RTCRtpSender;
    addTransceiver(trackOrKind: MediaStreamTrack | string, init?: RTCRtpTransceiverInit): RTCRtpTransceiver;
    close(): void;
    createAnswer(options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit>;
    createDataChannel(label: string, dataChannelDict?: RTCDataChannelInit): RTCDataChannel;
    createOffer(options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit>;
    getConfiguration(): RTCConfiguration;
    getIdentityAssertion(): Promise<string>;
    getReceivers(): RTCRtpReceiver[];
    getSenders(): RTCRtpSender[];
    getStats(selector?: MediaStreamTrack | null): Promise<RTCStatsReport>;
    getTransceivers(): RTCRtpTransceiver[];
    removeTrack(sender: RTCRtpSender): void;
    setConfiguration(configuration: RTCConfiguration): void;
    setIdentityProvider(provider: string, options?: RTCIdentityProviderOptions): void;
    setLocalDescription(description: RTCSessionDescriptionInit): Promise<void>;
    setRemoteDescription(description: RTCSessionDescriptionInit): Promise<void>;
    addEventListener<K extends keyof RTCPeerConnectionEventMap>(type: K, listener: (this: RTCPeerConnection, ev: RTCPeerConnectionEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
    addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
    removeEventListener<K extends keyof RTCPeerConnectionEventMap>(type: K, listener: (this: RTCPeerConnection, ev: RTCPeerConnectionEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
    removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}

Steps:

  • localPC(PC,RTCPeerConnection,下同) 绑定 ice candidate事件(来确定连接方式:直连 or STUN or TURN)
  • remotePC绑定ice candidate事件
  • remotePC绑定media事件(onaddstream已经废弃,使用ontrack),这里是为了当本地发送的media到达remote端的时候,remote的video开始接受media并播放
  • localPC添加media stream,这里要使用track
  • 这里说明一下:连接两端local <==> remote, 在remote端看来 local端 是 remote peer, local 的 local description 就相当于是 remote 的 remote description(description,描述了支持的格式,编解码方式等,目的是为了协商出两端都支持的格式)
  • localPC 来createOffer发起请求,生成的description设置为local的localDescription,根据上面的等价原则设置remote端的RemoteDescription
  • remotePC生成回答,生成的description同样的设置方法

Code: API时间-2021年5月7日

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <table border="1">
    <tr>
      <th>Local</th>
      <th>Remote</th>
    </tr>
    <tr>
      <td><video id="local" autoplay></video> </td>
      <td><video id="remote" autoplay></video> </td>
    </tr>

    <tr>
      <td align="center">
        <div>
          <button id="StartBtn" onclick="start()">Start</button>
          <button id="CallBtn" onclick="call()">Call</button>
          <button id="StopBtn" onclick="hangup()">Stop</button>
        </div>
      </td>
    </tr>
  </table>

  <script type="text/javascript">
    var localStream, localPeerConnection, remotePeerConnection;
    var localvideo = document.getElementById('local');
    var remotevideo = document.getElementById('remote');

    var startBtn = document.getElementById('StartBtn');
    var callBtn = document.getElementById('CallBtn');
    var stopBtn = document.getElementById('StopBtn');

    startBtn.disabled = false;
    callBtn.disabled = true;
    stopBtn.disabled = true;

    function log(text) {
      console.log("At time: " + (performance.now()/1000).toFixed(3) + " --> " + text);
    }

    function successCallback(stream) {
      console.log('Recv local stream');
      localvideo.srcObject = stream;
      localStream = stream; // URL.createObjectURL is no longer use in chrome
      callBtn.disabled = false;
    }

    function start() {
      log("Requesting local stream");
      startBtn.disabled = false;
      navigator.getUserMedia({audio:true, video:true}, successCallback, function (error) {
        log("getUserMedia error:", error);
      });
    }

    function call() {
      callBtn.disabled = true;
      stopBtn.disabled = false;
      log('Starting call');

      if (localStream.getVideoTracks().length > 0){
        log('Using video device ...' + localStream.getVideoTracks()[0].label);
      }
      if (localStream.getAudioTracks().length > 0){
        log('Using audio device ...' + localStream.getAudioTracks()[0].label);
      }

      RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //chrome
      log('RTCPeerConnection object: ' + RTCPeerConnection);

      var servers = null;
      localPeerConnection = new RTCPeerConnection(servers);
      log('Created local peer connection');

      //gotLocalIceCandidate 是 onicecandidate 的 handler, 这里将candidate添加到 remote peer connection
      localPeerConnection.onicecandidate = gotLocalIceCandidate;

      remotePeerConnection = new RTCPeerConnection(servers);
      log('Create remote peer connection');

      remotePeerConnection.onicecandidate = gotRemoteIceCandidate;

      //recv remote stream
      // remotePeerConnection.onaddstream = gotRemoteStream; //Deprecated
      remotePeerConnection.ontrack = gotRemoteStream;

      //see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream
      localStream.getTracks().forEach(function(track){
        localPeerConnection.addTrack(track, localStream);
      });
      log('Added local stream to local peer connection');

      // see https://developer.mozilla.org/zh-CN/docs/Web/API/RTCPeerConnection/createOffer
      localPeerConnection.createOffer().then(function (desc) {
        localPeerConnection.setLocalDescription(desc);
        remotePeerConnection.setRemoteDescription(desc);
        log('Offer from local peer connection: \n' + desc.sdp);

        remotePeerConnection.createAnswer().then(function (desc) {
          remotePeerConnection.setLocalDescription(desc);
          localPeerConnection.setRemoteDescription(desc);
        });
      });
      // 本地实验可以通过setDesc 简单实现sendToServer
      // .then(function() {
      //   sendToServer({
      //     type: 'video-offer',
      //     sdp: localPeerConnection.localDescription
      //   });
      // });
    }

    function hangup() {
      log('Ending call');

      localPeerConnection.close();
      remotePeerConnection.close();

      localPeerConnection = null;
      remotePeerConnection = null;

      stopBtn.disabled = true;
      callBtn.disabled = false;
    }

    // 本地代理ICE需要通过信令服务器传递信息给其他对等端时触发此EventHandler
    function gotLocalIceCandidate(event) {
      if (event.candidate){
        // add candidate to remote peer connection
        remotePeerConnection.addIceCandidate(event.candidate);
        log('local ICE candidate: \n' + event.candidate.candidate + '\n add to remote peer connection');
      }
    }


    function gotRemoteIceCandidate(event) {
      if (event.candidate){
        // add candidate to local peer connection
        localPeerConnection.addIceCandidate(event.candidate);
        log('local ICE candidate: \n' + event.candidate.candidate+ '\n add to local peer connection');
      }
    }

    function gotRemoteStream(event) {
      // see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
      remotevideo.srcObject = event.streams[0]; // chrome
      log('Recv remote stream');
    }

  </script>
</body>
</html>

如果不工作,Chrome右击属性-目标-添加" --allow-file-access-from-files"

相关文章

网友评论

      本文标题:WebRTC学习-本地使用RTCPeerConnection

      本文链接:https://www.haomeiwen.com/subject/iljsdltx.html