ice-connection/helpers/onIceCandidate.js

import Skylink from '../../index';
import logger from '../../logger';
import messages from '../../messages';
import { dispatchEvent } from '../../utils/skylinkEventManager';
import { candidateGenerationState } from '../../skylink-events';
import SignalingServer from '../../server-communication/signaling-server';
import handleIceGatheringStats from '../../skylink-stats/handleIceGatheringStats';
import * as constants from '../../constants';

/**
 * @param targetMid - The mid of the target peer
 * @param {RTCIceCandidate} candidate - {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate}
 * @param {SkylinkRoom} currentRoom - Current room
 * @memberOf IceConnectionHelpers
 * @fires candidateGenerationState
 * @private
 * @return {null}
 */
const onIceCandidate = (targetMid, candidate, currentRoom) => {
  const state = Skylink.getSkylinkState(currentRoom.id);
  const initOptions = Skylink.getInitOptions();
  const peerConnection = state.peerConnections[targetMid];
  const signalingServer = new SignalingServer();
  let gatheredCandidates = state.gatheredCandidates[targetMid];
  const { CANDIDATE_GENERATION_STATE, TAGS } = constants;

  if (!peerConnection) {
    logger.log.WARN([targetMid, TAGS.CANDIDATE_HANDLER, null, messages.ICE_CANDIDATE.CANDIDATE_HANDLER.no_peer_connection], candidate);
    return null;
  }

  if (candidate.candidate) {
    if (!peerConnection.gathering) {
      logger.log.WARN([targetMid, TAGS.CANDIDATE_HANDLER, null, messages.ICE_CANDIDATE.CANDIDATE_HANDLER.ICE_GATHERING_STARTED], candidate);
      peerConnection.gathering = true;
      peerConnection.gathered = false;
      dispatchEvent(candidateGenerationState({
        room: currentRoom,
        peerId: targetMid,
        state: constants.CANDIDATE_GENERATION_STATE.GATHERING,
      }));
      handleIceGatheringStats.send(currentRoom.id, CANDIDATE_GENERATION_STATE.GATHERING, targetMid, false);
    }

    const candidateType = candidate.candidate.split(' ')[7];
    logger.log.DEBUG([targetMid, TAGS.CANDIDATE_HANDLER, candidateType, messages.ICE_CANDIDATE.CANDIDATE_HANDLER.CANDIDATE_GENERATED], candidate);

    if (candidateType === 'endOfCandidates' || !(peerConnection
      && peerConnection.localDescription && peerConnection.localDescription.sdp
      && peerConnection.localDescription.sdp.indexOf(`\r\na=mid:${candidate.sdpMid}\r\n`) > -1)) {
      logger.log.WARN([targetMid, TAGS.CANDIDATE_HANDLER, candidateType, messages.ICE_CANDIDATE.CANDIDATE_HANDLER.DROP_EOC], candidate);
      return null;
    }

    if (initOptions.filterCandidatesType[candidateType]) {
      if (!(state.hasMCU && initOptions.forceTURN)) {
        logger.log.WARN([targetMid, TAGS.CANDIDATE_HANDLER, candidateType, messages.ICE_CANDIDATE.CANDIDATE_HANDLER.FILTERED_CANDIDATE], candidate);
        return null;
      }

      logger.log.WARN([targetMid, TAGS.CANDIDATE_HANDLER, candidateType, messages.ICE_CANDIDATE.CANDIDATE_HANDLER.FILTERING_FLAG_NOT_HONOURED], candidate);
    }

    if (!gatheredCandidates) {
      gatheredCandidates = {
        sending: { host: [], srflx: [], relay: [] },
        receiving: { host: [], srflx: [], relay: [] },
      };
    }

    gatheredCandidates.sending[candidateType].push({
      sdpMid: candidate.sdpMid,
      sdpMLineIndex: candidate.sdpMLineIndex,
      candidate: candidate.candidate,
    });

    state.gatheredCandidates[targetMid] = gatheredCandidates;
    Skylink.setSkylinkState(state, currentRoom.id);

    logger.log.DEBUG([targetMid, TAGS.CANDIDATE_HANDLER, candidateType, messages.ICE_CANDIDATE.CANDIDATE_HANDLER.SENDING_CANDIDATE], candidate);

    signalingServer.sendCandidate(targetMid, state, candidate);
  } else {
    logger.log.INFO([targetMid, TAGS.CANDIDATE_HANDLER, null, messages.ICE_CANDIDATE.CANDIDATE_HANDLER.ICE_GATHERING_COMPLETED]);

    if (peerConnection.gathered) {
      return null;
    }

    peerConnection.gathering = false;
    peerConnection.gathered = true;

    dispatchEvent(candidateGenerationState({
      peerId: targetMid,
      state: constants.CANDIDATE_GENERATION_STATE.COMPLETED,
      room: currentRoom,
    }));
    handleIceGatheringStats.send(currentRoom.id, CANDIDATE_GENERATION_STATE.COMPLETED, targetMid, false);

    if (state.gatheredCandidates[targetMid]) {
      const sendEndOfCandidates = () => {
        if (!state.gatheredCandidates[targetMid]) return;

        signalingServer.sendMessage({
          type: constants.SIG_MESSAGE_TYPE.END_OF_CANDIDATES,
          noOfExpectedCandidates: state.gatheredCandidates[targetMid].sending.srflx.length + state.gatheredCandidates[targetMid].sending.host.length + state.gatheredCandidates[targetMid].sending.relay.length,
          mid: state.user.sid,
          target: targetMid,
          rid: currentRoom.id,
        });
      };
      setTimeout(sendEndOfCandidates, 6000);
    }
  }
  return null;
};

export default onIceCandidate;