Home Manual Reference Source

src/core/Config.js

import media from './util/Media';
import * as DataSync from './util/DataSync';
import * as Log from './util/Log';
import cache from './util/cache';

const _flattenServers = (servers) => {
  const _expand = servers.map((server) => {
    const {
      username, credential, urls, url
    } = server;
    const uris = urls || url;
    if (typeof uris !== 'string') {
      return uris.map(uri => ({ username, credential, urls: uri }));
    }
    return [server];
  });
  return [].concat(..._expand);
};

/**
 * The Reach configuration object
 * @class Config
 */
export default class Config {
  /**
   * Create configuration
   * @access protected
   * @param obj
   */
  constructor(obj) {
    /**
     * The default media constraints. These can be overridden when subscribing to a stream.
     * @type {MediaStreamConstraints}
     */
    this.constraints = null;

    /**
     * The id/element dom element that will hold the local video/audio element
     * @type {string|Element}
     */
    this.localStreamContainer = null;
    /**
     * The id/element dom element that will hold the remote video/audio element
     * @type {string|Element}
     */
    this.remoteStreamContainer = null;

    /**
     * The preferred video Codec. Takes a RegExp matching the codec name and sample rate.
     * Predefined values can be found in {@link Codec/video}
     * @type {RegExp}
     * @example <caption>Prefer VP9</caption>
     * var myReach = new Reach('https://io.datasync.orange.com/base/<my_namespace>', {
     *  preferredVideoCodec: Reach.codecs.video.VP9
     * });
     */
    this.preferredVideoCodec = null;

    /**
     * The preferred audio Codec. Takes a RegExp matching the codec name and sample rate.
     * Predefined values can be found in {@link Codec/audio}
     * @type {RegExp}
     * @example <caption>Prefer opus</caption>
     * var myReach = new Reach('https://io.datasync.orange.com/base/<my_namespace>', {
     *  preferredAudioCodec: Reach.codecs.audio.OPUS
     * });
     */
    this.preferredAudioCodec = null;

    /**
   * Set the communication quality (BAD, LOW, HIGH)
   * @type {Element}
   * @example communicationQuality: Reach.communicationQuality.LOW
   */
    this.communicationQuality = null;

    /**
     * The bitrate max for the video
     * @type {string|Element}
     */
    this.videoBitrateMax = null;

    /**
     * The bitrate max for the audio
     * @type {string|Element}
     */
    this.audioBitrateMax = null;

    // Populate with default values
    this.reset();

    // Populate with data
    this.assign(obj);

    // Read ICE servers from server
    DataSync.get('_/ice').then((snapData) => {
      if (snapData) {
        this.iceServers = snapData.val();
        Log.i('ICEServers', this.iceServers.length > 0 ? this.iceServers : 'None');
      }
    }, e => Log.d('ICEServers', e));

    // TODO #Feat: Add boolean prop to request permission on start,
    // sdpEditor (for user defined SDP modifications)
  }

  /**
   * Assign new conf values
   * @access protected
   * @param {object} obj the new conf values
   */
  assign(obj) {
    Object.keys(obj || {}).forEach((key) => {
      this[key] = obj[key];
    });
  }

  /**
   * The log level (DEBUG, INFO, WARN, ERROR)
   * @type {string}
   */
  set logLevel(level) {
    cache.logLevel = level;
  }

  /**
   * The log level (DEBUG, INFO, WARN, ERROR)
   * @returns {string}
   */
  get logLevel() {
    return cache.logLevel;
  }

  /**
   * List of TURN/STUN servers to use for ICE.
   * This list will be merged with the ICE servers declared in the namespace (**_/ice**).
   * @type {ICEServer[]}
   */
  set iceServers(servers) {
    Log.d('Config~set~iceServers', servers);
    if (servers) {
      if (!this._iceServers) {
        /**
         * @ignore
         */
        this._iceServers = [].concat(servers || []);
      } else {
        // flatten existing
        const _currentServers = _flattenServers(this._iceServers);
        // flatten new
        const _newServers = _flattenServers(servers);
        // Add only the missing servers
        _newServers.forEach((newServer) => {
          if (!_currentServers.some(server => server.urls === newServer.urls
            && server.username === newServer.username
            && server.credential === newServer.credential)) {
            _currentServers.push(newServer);
          }
        });
        // Re-group by username/credential
        this._iceServers = _currentServers.reduce((previous, current) => {
          const { username, credential, urls } = current;
          const idx = previous.findIndex(s => s.username === username
            && s.credential === credential);
          if (idx >= 0) {
            previous[idx].urls.push(urls);
          } else {
            previous.push({ username, credential, urls: [urls] });
          }
          return previous;
        }, []);
      }
    }
  }

  /**
   * List of TURN/STUN servers to use for ICE.
   * This list will be merged with the ICE servers declared in the namespace (**_/ice**).
   * @type {ICEServer[]}
   */
  get iceServers() {
    return this._iceServers || [
      {
        username: 'admin',
        credential: 'webcom1234',
        urls: [
          'turns:turn1.webcom.orange.com:443',
          'turn:turn1.webcom.orange.com:443?transport=tcp',
          'turn:turn1.webcom.orange.com:3478?transport=tcp'
        ]
      }
    ];
  }

  set stats(stats) {
    this._stats = Object.assign({}, stats);
  }

  get stats() {
    return this._stats || {};
  }

  /**
   * Resets configuration to default values
   * @protected
   */
  reset() {
    this.assign({
      constraints: media.constraints(),
      logLevel: 'ERROR'
    });
  }
}