luma/wtf-sdp

View on GitHub
src/media.js

Summary

Maintainability
A
0 mins
Test Coverage
import memoize from 'lodash.memoize';
// import { memoize } from 'decko';
import Attributes from './attributes.js';
import { collatePayloads } from './payloads.js';
import collateSSRCs from './ssrcs.js';
import filterUndefinedValues from './util/filter_undefined_values.js';
import mapToObject from './util/map_to_object.js';

export default class Media {
  /**
   * Represents a SDP media section
   *
   * @constructor
   * @param  {Object} raw A raw JSON representation of the media section
   */
  constructor(raw) {
    const self = this;
    // @TODO this stuff could all go away if I could get the memoize decorator
    // working with Babel 6
    const memoizePayloads = memoize(() => collatePayloads(self.formats, self.attrs));
    const memoizeSSRCs = memoize(() => collateSSRCs(self.attrs.get('ssrc')));
    const memoizeExtensions = memoize(() => {
      return self.attrs.get('extmap').reduce((exts, ext) => {
        exts.set(ext.value, ext.extension);
        return exts;
      }, new Map());
    });

    Object.defineProperties(this, {
      /**
       * Get the raw JSON version of this media section. This will be the original
       * version from the parser. If you want a more friendly version you should use
       * `media.toJson()` instead.`
       *
       * @property {Object} raw - returns the raw JSON version of this media section
       * @memberof Media#
       */
      raw: { value: raw },

      /**
       * Returns a collection of all attributes for this media section. Many types
       * of attributes have custom getters (e.g. candidates, ssrcs, payloads, etc)
       * so you would only use this property when you are doing something less common.
       *
       * @property {Attributes} attrs - returns a collection of all attributes
       * @memberof Media#
       */
      attrs: { value: new Attributes(raw.attrs) },

      /**
       * Returns all extensions (extmap) as map of value => extension.
       *
       *  ```
       *    const extensions = media.extensions.get('1')
       *    // extensions == 'urn:ietf:params:rtp-hdrext:ssrc-audio-level'
       * ```
       *
       * @property {Map} extensions - returns a map of value => extension
       * @memberof Media#
       */
      extensions: { get: memoizeExtensions },

      /**
       * Returns all payloads as map of id => paylaod.
       *
       *  ```
       *    const opus = media.payloads.get('111');
       *    opus.encodingName === 'opus';
       *    opus.feedback === [{ type: 'transport-cc' }];
       *    opus.params === ['minptime=10']
       * ```
       *
       * @property {Map} payloads - returns a map of id => payload
       * @memberof Media#
       */
      payloads: { get: memoizePayloads },

      /**
       * Returns all ssrcs as map of id => ssrc.
       *
       *  ```
       *    const ssrc = media.ssrcs.get('3339118420');
       *    ssrc.cname === 'qNn0vt04pMSU';
       *    ssrc.msid === 'y2Pkbz4KgqrQz9Rx1WM91xAOsAOYAYZNcucu 85c70629-98cd...';
       *    ssrc.label === '85c70629-98cd-4ce4-8508-b9374c7baa6d';
       * ```
       *
       * @property {Map} payloads - returns a map of id => payload
       * @memberof Media#
       */
      ssrcs: { get: memoizeSSRCs },
    });
  }

  /**
   * Get the mid
   * @property {String} The mid
   */
  get id() {
    return this.attrs.first('mid');
  }

  /**
   * Get the media type.
   * @property {String} The media type. Usually video, audio, or data.
   */
  get type() {
    return this.raw.type;
  }

  /**
   * Get the media port.
   * @property {Number} The port.
   */
  get port() {
    return this.raw.port;
  }

  /**
   * Get the media protocol
   * @property {String} The media protocol, i.e. 'RTP/SAVPF'
   */
  get protocol() {
    return this.raw.protocol;
  }

  /**
   * Return the ids of the supported payloads. Use `media.payloads.get(payloadid)`
   * to retrieve payload info.
   *
   * @property {Array} Array of payload ids (strings)
   */
  get formats() {
    return this.raw.formats;
  }

  /**
   * Get the details of all connections for this media
   *
   *  ```
   *    const conn = media.connections[0];
   *    conn.netType === 'IN';
   *    conn.addressType === 'IP4';
   *    conn.address === '0.0.0.0'
   * ```
   *
   * @property {Array} Array of connection data
   */
  get connections() {
    return this.raw.connections;
  }

  /**
   * Return all ICE relates properties for this media.
   *
   * @property {Object} An object that will contain properties for pwd, ufrag, options. Properties
   * will only exist if the media defines them so ice may just return an empty object.
   */
  get ice() {
    return filterUndefinedValues({
      pwd: this.attrs.first('ice-pwd'),
      ufrag: this.attrs.first('ice-ufrag'),
      options: this.attrs.first('ice-options'),
    });
  }

  /**
   * Indicates the 'direction' of the media. The direction can be 'sendonly', 'recvonly',
   * 'sendrecv', or 'inactive'. 'inactive' is also a direction :-/
   *
   * @property {String} A string representing the direction
   */
  get direction() {
    return this.attrs.first('direction');
  }

  /**
   * Returns details about the fingerprint. This includes the actual fingering string as well as
   * the hash function that was used to generate it.
   *
   * @property {Object} An object containing  `hashFunction` and `fingerprint` properties.
   */
  get fingerprint() {
    return this.attrs.first('fingerprint');
  }

  // @memoize
  // get payloads() {
  //   return collatePayloads(this.raw.formats, this.raw.attrs);
  // }

  // @memoize
  // get extensions() {
  //   return this.attrs.get('extmap').reduce((exts, ext) => {
  //     exts[ext.value] = ext.extension;
  //   }, {});
  // }

  // @memoize
  // get ssrcs() {
  //   return self.attrs.get('ssrc').reduce((ssrcs, { id, attribute } = {}) => {
  //     if (!ssrcs[id]) {
  //       ssrcs[id] = [];
  //     }
  //
  //     ssrcs[id].push(attribute);
  //     return ssrcs;
  //   }, {});
  // }

  /**
   * Get all ssrc groups for this media.
   * @property {Array} Array of ssrc groups.
   */
  get ssrcGroups() {
    // @TODO map the ssrc ids to specific objects in `this.ssrcs()``?
    return this.attrs.get('ssrc-group');
  }

  /**
   * Get all candidates for this media.
   *
   *  ```
   *    const cand = media.candidates[0];
   *    cand.foundation === '2';
   *    cand.componentId === 1;
   *    cand.transport === 'UDP'
   *    cand.address === '192.0.2.3'
   *    cand.type === 'srflx'
   *    cand.relAddr === '10.0.1.1'
   *    cand.relPort === 8998
   * ```
   *
   * @return {Array} Array of candidate objects
   */
  get candidates() {
    return this.attrs.get('candidate');
  }

  /**
   * Returns a simple JSON representation of the media.
   *
   * @return {String} A JSON version of this object
   */
  toJson() {
    return filterUndefinedValues({
      id: this.id,
      type: this.type,
      port: this.port,
      protocol: this.protocol,
      connections: this.connections,
      ice: this.ice,
      direction: this.direction,
      fingerprint: this.fingerprint,
      extensions: mapToObject(this.extensions),
      ssrcs: mapToObject(this.ssrcs),
      ssrcGroups: this.ssrcGroups,
      candidates: this.candidates,
      payloads: mapToObject(this.payloads),
      attrs: this.attrs,
    });
  }
}