Source: RTCSession/RTCMediaHandler.js

module.exports = RTCMediaHandler;

/**
 * Dependencies.
 */
var debug = require('debug')('JsSIP:RTCSession:RTCMediaHandler');
var JsSIP_C = require('../Constants');
var WebRTC = require('../WebRTC');


/* RTCMediaHandler
 * -class PeerConnection helper Class.
 * -param {RTCSession} session
 * -param {Object} [contraints]
 */
function RTCMediaHandler(session, constraints) {
  constraints = constraints || {};

  this.session = session;
  this.localMedia = null;
  this.peerConnection = null;
  this.ready = true;

  this.init(constraints);
}


RTCMediaHandler.prototype = {
  isReady: function() {
    return this.ready;
  },

  createOffer: function(onSuccess, onFailure, constraints) {
    var self = this;

    function onSetLocalDescriptionSuccess() {
      if (self.peerConnection.iceGatheringState === 'complete' && (self.peerConnection.iceConnectionState === 'connected' || self.peerConnection.iceConnectionState === 'completed')) {
        self.ready = true;
        onSuccess(self.peerConnection.localDescription.sdp);
      } else {
        self.onIceCompleted = function() {
          self.onIceCompleted = undefined;
          self.ready = true;
          onSuccess(self.peerConnection.localDescription.sdp);
        };
      }
    }

    this.ready = false;

    this.peerConnection.createOffer(
      function(sessionDescription){
        self.setLocalDescription(
          sessionDescription,
          onSetLocalDescriptionSuccess,
          function(e) {
            self.ready = true;
            onFailure(e);
          }
        );
      },
      function(e) {
        self.ready = true;
        debug('unable to create offer');
        debug(e);
        onFailure(e);
      },
      constraints
    );
  },

  createAnswer: function(onSuccess, onFailure, constraints) {
    var self = this;

    function onSetLocalDescriptionSuccess() {
      if (self.peerConnection.iceGatheringState === 'complete' && (self.peerConnection.iceConnectionState === 'connected' || self.peerConnection.iceConnectionState === 'completed')) {
        self.ready = true;
        onSuccess(self.peerConnection.localDescription.sdp);
      } else {
        self.onIceCompleted = function() {
          self.onIceCompleted = undefined;
          self.ready = true;
          onSuccess(self.peerConnection.localDescription.sdp);
        };
      }
    }

    this.ready = false;

    this.peerConnection.createAnswer(
      function(sessionDescription){
        self.setLocalDescription(
          sessionDescription,
          onSetLocalDescriptionSuccess,
          function(e) {
            self.ready = true;
            onFailure(e);
          }
        );
      },
      function(e) {
        self.ready = true;
        debug('unable to create answer');
        debug(e);
        onFailure(e);
      },
      constraints
    );
  },

  setLocalDescription: function(sessionDescription, onSuccess, onFailure) {
    this.peerConnection.setLocalDescription(
      sessionDescription,
      onSuccess,
      function(e) {
        debug('unable to set local description');
        debug(e);
        onFailure(e);
      }
    );
  },

  addStream: function(stream, onSuccess, onFailure, constraints) {
    try {
      this.peerConnection.addStream(stream, constraints);
    } catch(e) {
      debug('error adding stream');
      debug(e);
      onFailure();
      return;
    }

    onSuccess();
  },

  /**
  * peerConnection creation.
  */
  init: function(options) {
    options = options || {};

    var idx, length, server,
      self = this,
      servers = [],
      constraints = options.constraints || {},
      stun_servers = options.stun_servers  || null,
      turn_servers = options.turn_servers || null,
      config = this.session.ua.configuration;

    if (!stun_servers) {
      stun_servers = config.stun_servers;
    }

    if (!turn_servers) {
      turn_servers = config.turn_servers;
    }

    /* Change 'url' to 'urls' whenever this issue is solved:
     * https://code.google.com/p/webrtc/issues/detail?id=2096
     */

    if (stun_servers.length > 0) {
      servers.push({'url': stun_servers});
    }

    length = turn_servers.length;
    for (idx = 0; idx < length; idx++) {
      server = turn_servers[idx];
      servers.push({
        'url': server.urls,
        'username': server.username,
        'credential': server.credential
      });
    }

    this.peerConnection = new WebRTC.RTCPeerConnection({'iceServers': servers}, constraints);

    this.peerConnection.onaddstream = function(e) {
      debug('stream added: '+ e.stream.id);
    };

    this.peerConnection.onremovestream = function(e) {
      debug('stream removed: '+ e.stream.id);
    };

    this.peerConnection.onicecandidate = function(e) {
      if (e.candidate) {
        debug('ICE candidate received: '+ e.candidate.candidate);
      } else if (self.onIceCompleted !== undefined) {
        setTimeout(function() {
          self.onIceCompleted();
        });
      }
    };

    this.peerConnection.oniceconnectionstatechange = function() {
      debug('ICE connection state changed to "'+ this.iceConnectionState +'"');

      if (this.iceConnectionState === 'failed') {
        self.session.terminate({
            cause: JsSIP_C.causes.RTP_TIMEOUT,
            status_code: 200,
            reason_phrase: JsSIP_C.causes.RTP_TIMEOUT
          });
      }
    };


    this.peerConnection.onstatechange = function() {
      debug('PeerConnection state changed to "'+ this.readyState +'"');
    };
  },

  close: function() {
    debug('closing PeerConnection');
    if(this.peerConnection) {
      this.peerConnection.close();

      if(this.localMedia) {
        this.localMedia.stop();
      }
    }
  },

  /**
  * -param {Object} mediaConstraints
  * -param {Function} onSuccess
  * -param {Function} onFailure
  */
  getUserMedia: function(onSuccess, onFailure, constraints) {
    var self = this;

    debug('requesting access to local media');

    WebRTC.getUserMedia(constraints,
      function(stream) {
        debug('got local media stream');
        self.localMedia = stream;
        onSuccess(stream);
      },
      function(e) {
        debug('unable to get user media');
        debug(e);
        onFailure();
      }
    );
  },

  /**
  * Message reception.
  * -param {String} type
  * -param {String} sdp
  * -param {Function} onSuccess
  * -param {Function} onFailure
  */
  onMessage: function(type, body, onSuccess, onFailure) {
    this.peerConnection.setRemoteDescription(
      new WebRTC.RTCSessionDescription({type: type, sdp:body}),
      onSuccess,
      onFailure
    );
  }
};