peer-connection/helpers/signalingEndOfCandidates.js

import Skylink from '../../index';
import * as constants from '../../constants';
import messages from '../../messages';
import logger from '../../logger';
import IceCandidate from '../../ice-connection';
import { isLowerThanVersion } from '../../utils/helpers';
import { dispatchEvent } from '../../utils/skylinkEventManager';
import { candidatesGathered } from '../../skylink-events';

/**
 * @param {String} targetMid
 * @param {SkylinkState} roomState
 * @return {null}
 * @memberOf PeerConnection.PeerConnectionHelpers
 * @fires candidatesGathered
 */
const signalingEndOfCandidates = (targetMid, roomState) => {
  const state = Skylink.getSkylinkState(roomState.room.id);
  const peerEndOfCandidatesCounter = state.peerEndOfCandidatesCounter[targetMid];
  const peerConnection = state.peerConnections[targetMid];
  const peerCandidatesQueue = state.peerCandidatesQueue[targetMid];
  const peerConnectionConfig = state.peerConnectionConfig[targetMid];
  const gatheredCandidates = state.gatheredCandidates[targetMid];
  const { AdapterJS, RTCIceCandidate } = window;
  const { TAGS } = constants;
  const { PEER_CONNECTION } = messages;

  if (!peerEndOfCandidatesCounter) {
    return null;
  }

  if (
    // If peer connection exists first and state is not closed.
    peerConnection && peerConnection.signalingState !== constants.PEER_CONNECTION_STATE.CLOSED
    // If remote description is set
    && peerConnection.remoteDescription && peerConnection.remoteDescription.sdp
    // If end-of-candidates signal is received
    && typeof peerEndOfCandidatesCounter.expectedLen === 'number'
    // If all ICE candidates are received
    && peerEndOfCandidatesCounter.len >= peerEndOfCandidatesCounter.expectedLen
    // If there is no ICE candidates queue
    && (peerCandidatesQueue ? peerCandidatesQueue.length === 0 : true)
    // If it has not been set yet
    && !peerEndOfCandidatesCounter.hasSet) {
    logger.log.DEBUG([targetMid, TAGS.PEER_CONNECTION, null, PEER_CONNECTION.end_of_candidates]);

    peerEndOfCandidatesCounter.hasSet = true;

    try {
      if (AdapterJS.webrtcDetectedBrowser === 'edge') {
        let mLineCounter = -1;
        const addedMids = [];
        const sdpLines = peerConnection.remoteDescription.sdp.split('\r\n');
        let rejected = false;

        for (let i = 0; i < sdpLines.length; i += 1) {
          if (sdpLines[i].indexOf('m=') === 0) {
            rejected = sdpLines[i].split(' ')[1] === '0';
            mLineCounter += 1;
          } else if (sdpLines[i].indexOf('a=mid:') === 0 && !rejected) {
            const mid = sdpLines[i].split('a=mid:')[1] || '';
            if (mid && addedMids.indexOf(mid) === -1) {
              addedMids.push(mid);
              IceCandidate.addIceCandidate(targetMid, `endofcan-${(new Date()).getTime()}`, 'endOfCandidates', new RTCIceCandidate({
                sdpMid: mid,
                sdpMLineIndex: mLineCounter,
                candidate: 'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates',
              }), state);
              // Start breaking after the first add because of max-bundle option
              if (peerConnectionConfig.bundlePolicy === constants.BUNDLE_POLICY.MAX_BUNDLE) {
                break;
              }
            }
          }
        }
      } else if (AdapterJS && !isLowerThanVersion(AdapterJS.VERSION, '0.14.0')) {
        peerConnection.addIceCandidate(null);
      }

      if (gatheredCandidates) {
        const candidatesLength = {
          expected: peerEndOfCandidatesCounter.expectedLen || 0,
          received: peerEndOfCandidatesCounter.len || 0,
          processed: gatheredCandidates.receiving.srflx.length + gatheredCandidates.receiving.relay.length + gatheredCandidates.receiving.host.length,
        };
        dispatchEvent(candidatesGathered({
          room: state.room,
          peerId: targetMid,
          candidatesLength,
        }));
      }

      state.peerEndOfCandidatesCounter[targetMid] = peerEndOfCandidatesCounter;
      Skylink.setSkylinkState(state, roomState.room.id);
    } catch (error) {
      logger.log.ERROR([targetMid, TAGS.PEER_CONNECTION, null, PEER_CONNECTION.end_of_candidate_failure], error);
    }
  }
  return null;
};

export default signalingEndOfCandidates;