Home Manual Reference Source

src/core/webrtc/PeerConnectionManager.js

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

const getShortStackId = (id1, id2) => (
  id1.localeCompare(id2, 'en-us') > 0 ? `${id1}-${id2}` : `${id2}-${id1}`
);

/* eslint-disable no-bitwise */
const hashCode = str => (
  str
    .split('')
    .reduce((prevHash, currVal) => (((prevHash << 5) - prevHash) + currVal.charCodeAt(0)) | 0, 0)
);
/* eslint-enable no-bitwise */

const getStackId = (deviceId1, deviceId2) => {
  const shortstackId = getShortStackId(deviceId1, deviceId2);

  const hash = `${hashCode(shortstackId)}`;
  const length = hash.length; // eslint-disable-line
  const id1 = hash.substring(length - 3, length);
  const id2 = hash.substring(length - 6, length - 3);

  return `${id1}/${id2}/${shortstackId}`;
};

/**
 * @access protected
 */
export default class PeerConnectionManager {
  /**
   * Constructor
   * @access protected
   */
  constructor() {
    /**
     * WebRTC stacks
     * @type {{*: {*: PeerConnection}}}
     */
    this.stacks = {};
  }

  /**
   * Get a PeerConnection object for a specific stream
   * @param {Local|Remote} stream
   * @param {Remote|{to: string, device:string}} remote
   * @param {boolean} publish
   * @return {*}
   */
  getPeerConnection(stream, remote, publish) {
    if (!cache.user) {
      return Promise.reject(
        new Error('Only an authenticated user can get a PeerConnection\'s object.')
      );
    }
    const stackId = getStackId(remote.device, cache.device);

    if (this.stacks[stackId] && this.stacks[stackId][stream.uid]) {
      return Promise.resolve(this.stacks[stackId][stream.uid]);
    }

    if (!this.stacks[stackId]) {
      this.stacks[stackId] = {};
    }

    const userId = cache.user.uid.split('/');
    const shortUserId = userId[2];
    let shortRemoteTo;
    let shortRemoteFrom;
    if (remote.to) {
      const remoteTo = remote.to.split('/');
      shortRemoteTo = remoteTo[2]; // eslint-disable-line prefer-destructuring
    } else {
      const remoteFrom = remote.from.split('/');
      shortRemoteFrom = remoteFrom[2]; // eslint-disable-line prefer-destructuring
    }

    const users = {};
    // users[cache.user.uid] = true;
    users[shortUserId] = true;
    // users[remote.from || remote.to] = true;
    users[shortRemoteFrom || shortRemoteTo] = true;

    return DataSync.update(`_/webrtc/${stackId}`, users)
      .then(() => new PeerConnection(stackId, stream.uid, remote, publish))
      .then((pc) => {
        Log.d('PeerConnectionManager~getPeerConnection', { stackId, streamId: stream.uid, pc });
        this.stacks[stackId][stream.uid] = pc;
        return pc;
      })
      .catch(Log.r('PeerConnectionManager~getPeerConnection'));
  }

  /**
   * Create offer for a stream to a subscriber
   * @param {Local} localStream
   * @param {object} subscriber
   * @param {array} errorCallbacks
   * @return {Promise.<PeerConnection>}
   */
  offer(localStream, subscriber, errorCallbacks) {
    Log.d('PeerConnectionManager~offer', { localStream, subscriber });
    return this.getPeerConnection(localStream, subscriber, true)
      .then(pc => pc.offer(localStream.media, errorCallbacks));
  }

  /**
   * Answer to the offer from the publisher
   * @param {Remote} remoteStream
   * @param {Element} htmlElement
   * @param {array} errorCallbacks
   * @return {*|Promise.<PeerConnection>}
   */
  answer(remoteStream, htmlElement, errorCallbacks = []) {
    Log.d('PeerConnectionManager~answer', { remoteStream, htmlElement });
    return this.getPeerConnection(remoteStream, remoteStream, false)
      .then(pc => pc.answer(htmlElement, errorCallbacks));
  }

  /**
   * Close a PeerConnection
   * @param streamId
   * @param remoteDevice
   * @return {*}
   */
  close(streamId, remoteDevice) {
    const stackId = getStackId(remoteDevice, cache.device);


    const pc = this.stacks[stackId] ? this.stacks[stackId][streamId] : null;
    if (pc) {
      pc.close();
      DataSync.remove(`_/webrtc/${stackId}`);
      this.stacks[stackId][streamId] = null;
      delete this.stacks[stackId][streamId];
      return pc;
    }
    return false;
  }
}