ice-connection/helpers/addIceCandidate.js

import Skylink from '../../index';
import logger from '../../logger';
import { candidateProcessingState } from '../../skylink-events';
import { dispatchEvent } from '../../utils/skylinkEventManager';
import * as constants from '../../constants';
import messages from '../../messages';
import HandleIceCandidateStats from '../../skylink-stats/handleIceCandidateStats';

const handleIceCandidateStats = new HandleIceCandidateStats();

/**
 * Success callback for adding an IceCandidate
 * @param {SkylinkRoom} room - The current room
 * @param {String} targetMid - The mid of the target peer
 * @param {String} candidateId - The id of the ICE Candidate
 * @param {String} candidateType - Type of the ICE Candidate
 * @param {RTCIceCandidate} candidate - An RTCIceCandidate Object
 * @fires candidateProcessingState
 * @memberOf IceConnectionHelpers
 * @private
 */
const addIceCandidateSuccess = (room, targetMid, candidateId, candidateType, candidate) => {
  const { STATS_MODULE, ICE_CANDIDATE } = messages;
  const { CANDIDATE_PROCESSING_STATE, TAGS } = constants;

  logger.log.INFO([targetMid, TAGS.CANDIDATE_HANDLER, `${candidateId}:${candidateType}`, ICE_CANDIDATE.CANDIDATE_HANDLER.CANDIDATE_ADDED]);
  dispatchEvent(candidateProcessingState({
    room,
    state: CANDIDATE_PROCESSING_STATE.PROCESS_SUCCESS,
    peerId: targetMid,
    candidateId,
    candidateType,
    candidate,
    error: null,
  }));
  handleIceCandidateStats.send(room.id, STATS_MODULE.HANDLE_ICE_GATHERING_STATS.PROCESS_SUCCESS, targetMid, candidateId, candidate);
};

/**
 * Failure callback for adding an IceCandidate
 * @param {SkylinkRoom} room - The current room
 * @param {String} targetMid - The mid of the target peer
 * @param {String} candidateId - The id of the ICE Candidate
 * @param {String} candidateType - Type of the ICE Candidate
 * @param {RTCIceCandidate} candidate - An RTCIceCandidate Object
 * @param {Error} error - Error
 * @fires candidateProcessingState
 * @memberOf IceConnectionHelpers
 * @private
 */
const addIceCandidateFailure = (room, targetMid, candidateId, candidateType, candidate, error) => {
  const { STATS_MODULE, ICE_CANDIDATE } = messages;
  const { CANDIDATE_PROCESSING_STATE, TAGS } = constants;

  logger.log.ERROR([targetMid, TAGS.CANDIDATE_HANDLER, `${candidateId}:${candidateType}`, ICE_CANDIDATE.CANDIDATE_HANDLER.FAILED_ADDING_CANDIDATE], error);
  dispatchEvent(candidateProcessingState({
    room,
    state: CANDIDATE_PROCESSING_STATE.PROCESS_ERROR,
    peerId: targetMid,
    candidateId,
    candidateType,
    candidate,
    error,
  }));
  handleIceCandidateStats.send(room.id, STATS_MODULE.HANDLE_ICE_GATHERING_STATS.PROCESS_FAILED, targetMid, candidateId, candidate, error);
};

/**
 * @param {String} targetMid - The mid of the target peer
 * @param {String} candidateId - The id of the ICE Candidate
 * @param {String} candidateType - Type of the ICE Candidate
 * @param {RTCIceCandidate} nativeCandidate - An RTCIceCandidate Object
 * @param {SkylinkState} roomState - Skylink State
 * @fires candidateProcessingState
 * @memberOf IceConnectionHelpers
 * @private
 */
const addIceCandidate = (targetMid, candidateId, candidateType, nativeCandidate, roomState) => {
  const state = Skylink.getSkylinkState(roomState.room.id);
  const { peerConnections, room } = state;
  const peerConnection = peerConnections[targetMid];
  const candidate = {
    candidate: nativeCandidate.candidate,
    sdpMid: nativeCandidate.sdpMid,
    sdpMLineIndex: nativeCandidate.sdpMLineIndex,
  };
  const { STATS_MODULE, ICE_CANDIDATE, PEER_CONNECTION } = messages;
  const { CANDIDATE_PROCESSING_STATE, PEER_CONNECTION_STATE, TAGS } = constants;

  logger.log.DEBUG([targetMid, TAGS.CANDIDATE_HANDLER, `${candidateId}:${candidateType}`, ICE_CANDIDATE.CANDIDATE_HANDLER.ADDING_CANDIDATE]);
  dispatchEvent(candidateProcessingState({
    peerId: targetMid,
    room,
    candidateType,
    candidate,
    candidateId,
    state: CANDIDATE_PROCESSING_STATE.PROCESSING,
    error: null,
  }));
  handleIceCandidateStats.send(room.id, STATS_MODULE.HANDLE_ICE_GATHERING_STATS.PROCESSING, targetMid, candidateId, candidate);

  if (!(peerConnection
    && peerConnection.signalingState !== PEER_CONNECTION_STATE.CLOSED
    && peerConnection.remoteDescription
    && peerConnection.remoteDescription.sdp
    && peerConnection.remoteDescription.sdp.indexOf(`\r\na=mid:${candidate.sdpMid}\r\n`) > -1)) {
    logger.log.WARN([targetMid, TAGS.CANDIDATE_HANDLER, `${candidateId}:${candidateType}`, `${ICE_CANDIDATE.CANDIDATE_HANDLER.DROPPING_CANDIDATE} - ${PEER_CONNECTION.NO_PEER_CONNECTION}`]);

    dispatchEvent(candidateProcessingState({
      peerId: targetMid,
      room: roomState.room,
      candidateType,
      candidate,
      candidateId,
      state: constants.CANDIDATE_PROCESSING_STATE.DROPPED,
      error: new Error(PEER_CONNECTION.NO_PEER_CONNECTION),
    }));
    handleIceCandidateStats.send(room.id, STATS_MODULE.HANDLE_ICE_GATHERING_STATS.PROCESS_FAILED, targetMid, candidateId, candidate, PEER_CONNECTION.NO_PEER_CONNECTION);
  }

  try {
    peerConnection.addIceCandidate(candidate)
      .then(() => { addIceCandidateSuccess(room, targetMid, candidateId, candidateType, candidate); })
      .catch((error) => { addIceCandidateFailure(room, targetMid, candidateId, candidateType, candidate, error); });
  } catch (error) {
    addIceCandidateFailure.bind(peerConnection, room, targetMid, candidateId, candidateType, candidate, error);
  }
};

export default addIceCandidate;