UNPKG

@skylineos/clsp-player

Version:

Skyline Technology Solutions' CLSP Video Player. Stream video in near-real-time in modern browsers.

218 lines (178 loc) 5.6 kB
import Conduit from './Conduit'; import Paho from './Paho'; import Destroyable from '../../utils/Destroyable'; let collection; export default class ConduitCollection extends Destroyable { static asSingleton () { if (!collection) { collection = ConduitCollection.factory('1'); } return collection; } static factory (logId) { return new ConduitCollection(logId); } /** * @private */ constructor (logId) { super(logId); this.totalConduitCount = 0; this.conduits = {}; this.deletedConduitClientIds = []; Paho.register(); // This is the Window Message listener for EVERY Conduit / Router on the // page (assuming you are using the singleton pattern) window.addEventListener('message', this._routeWindowMessageToTargetConduit); } /** * @private * * The listener for the "message" event on the window. Its job is to * identify messages that are intended for a specific Conduit / stream and * route them to the correct one. The most common example of this is when a * Router receives a moof/segment from a server, and posts a message to the * window. This listener will route that moof/segment to the proper Conduit. * * @param {Object} event * The window message event * * @returns {void} */ _routeWindowMessageToTargetConduit = (event) => { const clientId = event.data.clientId; if (!clientId) { // A window message was received that is not related to CLSP return; } this.logger.debug(`Received Window Message event for ${clientId}`); const eventType = event.data.event; if (!this.has(clientId)) { // When the CLSP connection is interupted due to a listener being removed, // a fail event is always sent. It is not necessary to log this as an error // in the console, because it is not an error. // @todo - the fail event no longer exists (or is it from Paho or // something?) - what is the name of the new corresponding event? if (eventType === 'fail') { return; } // @todo - use this to detect an externally destroyed iframe // if (eventType === 'iframe-onunload') { // console.log(event); // return; // } // Don't show an error for iovs that have been deleted if (this.deletedConduitClientIds.includes(clientId)) { this.logger.warn(`Received a message for deleted conduit ${clientId}`); return; } throw new Error(`Unable to route message of type ${eventType} for Conduit with clientId "${clientId}". A Conduit with that clientId does not exist.`); } // Pass the Window Message event to the proper Conduit instance. All we've // done at this point is pass the event to the Conduit that it was intended // for. At this point, the Conduit is responsible for taking the necessary // action based on the eventType this.get(clientId).onRouterEvent(eventType, event); }; /** * Create a Conduit for a specific stream, and add it to this collection. * * @returns {Conduit} */ create ( logId, clientId, streamConfiguration, containerElement, ) { this.logger.debug(`creating a conduit with logId ${logId} and clientId ${clientId}`); const conduit = Conduit.factory( logId, clientId, streamConfiguration, containerElement, ); this._add(conduit); this.totalConduitCount++; return conduit; } /** * Add a Conduit instance to this collection. * * @private * * @param {Conduit} conduit * The conduit instance to add * * @returns {this} */ _add (conduit) { const clientId = conduit.clientId; this.conduits[clientId] = conduit; return this; } /** * Determine whether or not a Conduit with the passed clientId exists in this * collection. * * @param {String} clientId * The clientId of the conduit to find * * @returns {Boolean} * True if the conduit with the given clientId exists * False if the conduit with the given clientId does not exist */ has (clientId) { return Object.prototype.hasOwnProperty.call(this.conduits, clientId); } /** * Get a Conduit with the passed clientId from this collection. * * @param {String} clientId * The clientId of the conduit instance to get * * @returns {Conduit|undefined} * If a Conduit with this clientId doest not exist, undefined is returned. */ get (clientId) { return this.conduits[clientId]; } /** * Remove a conduit instance from this collection and destroy it. * * @param {String} clientId * The clientId of the conduit to remove and destroy * * @returns {this} */ async remove (clientId) { const conduit = this.get(clientId); if (!conduit) { return; } await conduit.destroy(); delete this.conduits[clientId]; this.deletedConduitClientIds.push(clientId); return this; } /** * Destroy this collection and destroy all conduit instances in it. * * @returns {void} */ async _destroy () { window.removeEventListener('message', this._routeWindowMessageToTargetConduit); for (const clientId in this.conduits) { try { await this.remove(clientId); } catch (error) { this.logger.error(`Error while removing conduit ${clientId} while destroying`); this.logger.error(error); } } this.conduits = null; this.deletedConduitClientIds = null; await super._destroy(); } }