opentok
Version:
OpenTok server-side SDK
1,300 lines (1,250 loc) • 76.4 kB
JavaScript
/*
* OpenTok server-side SDK
*/
// Dependencies
var net = require('net');
var _ = require('lodash');
var encodeToken = require('opentok-token');
var Client = require('./client');
var Session = require('./session');
var Stream = require('./stream');
var archiving = require('./archiving');
var Broadcast = require('./broadcast');
var SipInterconnect = require('./sipInterconnect');
var moderation = require('./moderation');
var signaling = require('./signaling');
var errors = require('./errors');
var callbacks = require('./callbacks');
var generateJwt = require('./generateJwt');
var render = require('./render.js');
var OpenTok;
var key;
/*
* decodes a sessionId into the metadata that it contains
* @param {string} sessionId
* @returns {?SessionInfo} sessionInfo
*/
function decodeSessionId(sessionId) {
var fields;
// remove sentinal (e.g. '1_', '2_')
sessionId = sessionId.substring(2);
// replace invalid base64 chars
sessionId = sessionId.replace(/-/g, '+').replace(/_/g, '/');
// base64 decode
if (typeof Buffer.from === 'function') {
sessionId = Buffer.from(sessionId, 'base64').toString('ascii');
}
else {
sessionId = Buffer.from(sessionId, 'base64').toString('ascii');
}
// separate fields
fields = sessionId.split('~');
return {
apiKey: fields[1],
location: fields[2],
create_time: new Date(fields[3])
};
}
/**
* Contains methods for creating OpenTok sessions, generating tokens, and working with archives.
* <p>
* To create a new OpenTok object, call the OpenTok constructor with your OpenTok API key
* and the API secret for your <a href="https://tokbox.com/account">TokBox account</a>.
* Do not publicly share your API secret. You will use it with the OpenTok constructor
* (only on your web server) to create OpenTok sessions.
* <p>
* Be sure to include the entire OpenTok Node.js SDK on your web server.
*
* @class OpenTok
*
* @param apiKey {String} Your OpenTok API key. (See your
* <a href="https://tokbox.com/account">TokBox account page</a>.)
* @param apiSecret {String} Your OpenTok API secret. (See your
* <a href="https://tokbox.com/account">TokBox account page</a>.)
*/
// eslint-disable-next-line consistent-return
OpenTok = function (apiKey, apiSecret, env) {
var apiConfig;
var clientConfig;
var config;
// we're loose about calling this constructor with `new`, we got your back
if (!(this instanceof OpenTok)) return new OpenTok(apiKey, apiSecret, env);
// validate arguments: apiKey := Number|String, apiSecret := String
if (!(_.isNumber(apiKey) || _.isString(apiKey)) || !_.isString(apiSecret)) {
throw new Error('Invalid arguments when initializing OpenTok: apiKey=' +
apiKey +
', apiSecret=' +
apiSecret);
}
// apiKey argument can be a Number, but we will internally store it as a String
if (_.isNumber(apiKey)) apiKey = apiKey.toString();
this.client = new Client({ apiKey: apiKey, apiSecret: apiSecret });
this.apiKey = apiKey;
this.apiSecret = apiSecret;
// TODO: this is a pretty obvious seam, the integration could be more smooth
apiConfig = {
apiEndpoint: 'https://api.opentok.com',
apiKey: apiKey,
apiSecret: apiSecret,
auth: {
expire: 300
}
};
// env can be either an object with a bunch of DI options, or a simple string for the apiUrl
clientConfig = {
request: {}
};
if (_.isString(env)) {
clientConfig.apiUrl = env;
apiConfig.apiEndpoint = env;
}
else if (_.isObject(env) && !_.isFunction(env) && !_.isArray(env)) {
if (_.isString(env.apiUrl)) {
clientConfig.apiUrl = env.apiUrl;
apiConfig.apiEndpoint = env.apiUrl;
}
if (_.isString(env.proxy)) {
clientConfig.request.proxy = env.proxy;
apiConfig.proxy = env.proxy;
}
if (_.isString(env.uaAddendum)) {
clientConfig.uaAddendum = env.uaAddendum;
apiConfig.uaAddendum = env.uaAddendum;
}
if (parseInt(env.timeout, 10)) {
clientConfig.request.timeout = parseInt(env.timeout, 10);
}
}
config = this.client.config(clientConfig);
this.apiUrl = config.apiUrl;
/**
* Starts archiving an OpenTok session.
* <p>
* Clients must be actively connected to the OpenTok session for you to successfully start
* recording an archive.
* <p>
* You can only record one archive at a time for a given session. You can only record archives
* of sessions that uses the OpenTok Media Router (sessions with the media mode set to routed);
* you cannot archive sessions with the media mode set to relayed.
*
* @param sessionId The session ID of the OpenTok session to archive.
*
* @param options {Object} An optional options object with the following properties (each
* of which is optional):
* <p>
* <ul>
* <li>
* <code>name</code> (String) — the name of the archive, which you can use to identify
* the archive. The name is set as a property of the Archive object, and it is a property of
* archive-related events in the OpenTok client libraries.
* </li>
* <li>
* <code>hasAudio</code> (Boolean) — Whether the archive will include an audio track
* (<code>true</code>) or not (<code>false</code>). The default value is <code>true</code>
* (an audio track is included). If you set both <code>hasAudio</code> and
* <code>hasVideo</code> to <code>false</code>, the call to the <code>startArchive()</code>
* method results in an error.
* </li>
* <li>
* <code>hasVideo</code> (Boolean) — Whether the archive will include a video track
* (<code>true</code>) or (not <code>false</code>). The default value is <code>true</code>
* (a video track is included). If you set both <code>hasAudio</code> and
* <code>hasVideo</code> to <code>false</code>, the call to the <code>startArchive()</code>
* method results in an error.
* </li>
* <li>
* <code>outputMode</code> (String) — Whether all streams in the archive are recorded
* to a single file ("composed", the default) or to individual files ("individual").
* </li>
* <li>
* <code>layout</code> (Object) — An object defining the initial layout options
* for a composed archive. This object has three properties: <code>type</code>,
* <code>stylesheet</code>, and <code>screenshareType</code>, which are each strings.
* Set <code>type</code> to "bestFit",
* "pip", "verticalPresentation", "horizontalPresentation", or "custom". Set the
* <code>stylesheet</code> property if <code>type</code> is set to "custom", and
* set it to the stylesheet defining the custom layout. For example, set the
* <code>layout</code> object to <code>{ type: "pip" }</code> to set the initial layout
* of the archive to picture-in-picture. Set the <code>screenshareType</code> property
* to the layout type to use when there is a screen-sharing stream in the session
* (This property is optional.) Note if you set the <code>screenshareType</code> property,
* you must set the <code>type</code> property to "bestFit" and leave
* the <code>stylesheet</code> property unset. For details, see
* <a href="https://tokbox.com/developer/guides/archiving/layout-control.html">Customizing
* the video layout for composed archives</a>.
* </li>
* <li>
* <code>resolution</code> (String) — For a composed archive, set this to the
* resolution of the archive. Valid values are "1280x720" or "640x480" (the default).
* </li>
* <li>
* <code>streamMode</code> (optional) — The stream mode for the archive. This can be
* set to one of the the following:
*
* <ul>
* <li> "auto" — Streams included in the archive are selected automatically
* (the default).</li>
*
* <li> "manual" — Specify streams to be included based on calls to the
* {@link OpenTok#addArchivetStream OpenTok.addArchiveStream()} and
* {@link OpenTok#removeArchiveStream OpenTok.removeArchiveStream()} methods.</li>
* </ul>
* </li>
* <li>
* <code>multiArchiveTag</code> (String) — Set this to support recording
* multiple archives for the same session simultaneously. Set this to a unique string
* for each simultaneous archive of an ongoing session. You must also set this option
* when manually starting an archive that is automatically archived. Note that the
* <code>multiArchiveTag</code> value is not included in the response for
* the methods to list archives and retrieve archive information. If you do not specify a
* unique <code>multiArchiveTag</code> you can only record one archive at a time for a
* given session.
* </li>
* </ul>
*
* For more information on archiving and the archive file formats, see the
* <a href="https://tokbox.com/opentok/tutorials/archiving/">OpenTok archiving</a>
* programming guide.
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
*
* <ul>
*
* <li>
* <code>error</code> — An error object (if the call to the method fails).
* </li>
*
* <li>
* <code>archive</code> — The {@link Archive} object. This object includes properties
* defining the archive, including the archive ID.
* </li>
*
* </ul>
*
* @method #startArchive
* @memberof OpenTok
*/
this.startArchive = archiving.startArchive.bind(null, this, apiConfig);
/**
* Stops an OpenTok archive that is being recorded.
* <p>
* Archives automatically stop recording after 120 minutes or when all clients have disconnected
* from the session being archived.
* <p>
* You cannot stop an archive that is not being recorded.
*
* @param archiveId {String} The archive ID of the archive you want to stop recording.
* @return The {@link Archive} object corresponding to the archive being STOPPED.
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
*
* <ul>
*
* <li>
* <code>error</code> — An error object (if the call to the method fails).
* </li>
*
* <li>
* <code>archive</code> — The {@link Archive} object.
* </li>
*
* </ul>
*
* @method #stopArchive
* @memberof OpenTok
*/
this.stopArchive = archiving.stopArchive.bind(null, apiConfig);
/**
* Gets an {@link Archive} object for the given archive ID.
*
* @param archiveId {String} The archive ID.
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
* <ul>
* <li><code>error</code> — An error object (if the call to the method fails). </li>
* <li><code>archive</code> — The {@link Archive} object.</li>
* </ul>
*
* @method #getArchive
* @memberof OpenTok
*/
this.getArchive = archiving.getArchive.bind(null, apiConfig);
/**
* Deletes an OpenTok archive.
* <p>
* You can only delete an archive which has a status of "available" or "uploaded". Deleting an
* archive removes its record from the list of archives. For an "available" archive, it also
* removes the archive file, making it unavailable for download.
*
* @param {String} archiveId The archive ID of the archive you want to delete.
*
* @param callback {Function} The function to call upon completing the operation. On successfully
* deleting the archive, the function is called with no arguments passed in. On failure, an error
* object is passed into the function.
*
* @method #deleteArchive
* @memberof OpenTok
*/
this.deleteArchive = archiving.deleteArchive.bind(null, apiConfig);
/**
* Retrieves a List of {@link Archive} objects, representing archives that are both
* completed and in-progress, for your API key.
*
* @param options {Object} An options parameter with three properties:
*
* <ul>
*
* <li>
* <code>count</code> — The maximum number of archives to return. The default number of
* archives returned is 50 (or fewer, if there are fewer than 50 archives). The method returns
* a maximum of 1000 archives.
* </li>
*
* <li>
* <code>offset</code> — The offset for the first archive to list (starting with the
* first archive recorded as offset 0). 1 is the offset of the archive that started prior
* to the most recent archive. This property is optional; the default is 0.
* </li>
*
* <li>
* <code>sessionId</code> — Specify the ID of a session in order to retrieve archives
* specifically for that session. This property is optional. When no session ID is specified,
* then the method will return archives from any session created with your API key.
* </li>
*
* </ul>
*
* <p>If you don't pass in an <code>options</code> argument,
* the method returns up to 1000 archives
* starting with the first archive recorded.
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
*
* <ul>
*
* <li>
* <code>error</code> — An error object (if the call to the method fails).
* </li>
*
* <li>
* <code>archives</code> — An array of {@link Archive} objects.
* </li>
*
* </ul>
*
* @method #listArchives
* @memberof OpenTok
*/
this.listArchives = archiving.listArchives.bind(null, apiConfig);
/**
* Adds a stream to an archive that has the streamMode set to manual.
* You can call the method repeatedly with the same stream ID, to toggle
* the stream's audio or video in the archive.
*
* @param archiveId {String} The archive ID.
*
* @param streamId {String} The stream ID to add to archive.
*
* @param archiveOptions {Object} An object that has these properties:
*
* <ul>
*
* <li>
* <code>hasAudio</code> — Whether the composed archive should include the stream's audio
* (true, the default) or not (false).
* </li>
*
* <li>
* <code>hasVideo</code> — Whether the composed archive should include the stream's video
* (true, the default) or not (false).
* </li>
*
* </ul>
*
* @param callback {Function} The function to call upon completing the operation. One argument is
* passed to the function
*
* <ul>
*
* <li>
* <code>error</code> — An error object (if the call to the method fails).
* </li>
*
* </ul>
*
* @method #addArchiveStream
* @memberof OpenTok
*/
this.addArchiveStream = archiving.addArchiveStream.bind(null, apiConfig);
/**
* Removes a stream from a composed archive that has the streamMode set to manual.
*
* @param archiveId {String} The archive ID.
*
* @param streamId {String} The stream ID to remove from the archive.
*
* @param callback {Function} The function to call upon completing the operation. An error is
* passed into the function if the call fails.
*
* @method #removeArchiveStream
* @memberof OpenTok
*/
this.removeArchiveStream = archiving.removeArchiveStream.bind(null, apiConfig);
/**
* Sets the layout type for a composed archive. For a description of layout types, see
* <a href="https://tokbox.com/developer/guides/archiving/layout-control.html">Customizing
* the video layout for composed archives</a>.
*
* @param archiveId {String} The archive ID.
*
* @param type {String} The layout type. Set this to "bestFit", "pip", "verticalPresentation",
* "horizontalPresentation", "focus", or "custom". For a description of these layout types, see
* <a href="https://tokbox.com/developer/guides/archiving/layout-control.html">Customizing
* the video layout for composed archives</a>.
*
* @param stylesheet {String} (Optional) The stylesheet for a custom layout. Set this parameter
* if you set <code>type</code> to <code>"custom"</code>. Otherwise, leave it undefined or set
* to null.
*
* @param screenshareType {String} (Optional) The layout type to use when
* there is a screen-sharing
* stream in the session. Note that to use this parameter, you must set the <code>type</code>
* parameter to "bestFit" and set the <code>stylesheet</code> parameter to <code>null</code>.
*
* @param callback {Function} The function to call upon completing the operation. Upon error,
* an <code>error</code> object is passed into the function. Upon success, the function is called
* with no error object passed in.
*
* @method #setArchiveLayout
* @memberof OpenTok
*/
this.setArchiveLayout = function setArchiveLayout(
archiveId,
type,
stylesheet,
screenshareType,
callback
) {
if (typeof archiveId !== 'string') {
return callback(new Error('Invalid arguments -- must provide an archiveId string.'));
}
if (typeof type !== 'string') {
return callback(new Error('Invalid arguments -- must provide a type string.'));
}
if (typeof stylesheet === 'function') {
if (callback) {
return callback(new Error('Invalid arguments -- stylesheet cannot be a function.'));
}
callback = stylesheet; // eslint-disable-line no-param-reassign
}
else if (stylesheet && typeof stylesheet !== 'string') {
return callback(new Error('Invalid arguments -- stylesheet must be a string.'));
}
if (typeof screenshareType === 'function') {
if (callback) {
return callback(new Error('Invalid arguments -- screenshareType cannot be a function.'));
}
callback = screenshareType; // eslint-disable-line no-param-reassign
}
else if (screenshareType && typeof screenshareType !== 'string') {
return callback(new Error('Invalid arguments -- screenshareType must be a string.'));
}
else if (screenshareType && type !== 'bestFit') {
return callback(new Error('Invalid arguments -- type must be set to "bestFit" if you set screenshareType.'));
}
if (typeof callback !== 'function') {
return callback(new Error('Invalid arguments -- must provide a callback function.'));
}
return this.client.setArchiveLayout(
{
archiveId: archiveId,
type: type,
stylesheet: stylesheet,
screenshareType: screenshareType
},
callback
);
};
/**
* Retrieves a List of {@link Render} objects, representing any renders in the starting,
* started, stopped or failed state, for your API key.
*
* @param options {Object} An options parameter with three properties:
*
* <ul>
*
* <li>
* <code>count</code> — The maximum number of renders to return. The default number of
* renders returned is 50 (or fewer, if there are fewer than 50 renders). The method returns
* a maximum of 1000 renders.
* </li>
*
* <li>
* <code>offset</code> — The offset for the first render to list (starting with the
* first render recorded as offset 0). 1 is the offset of the render that started prior
* to the most recent render. This property is optional; the default is 0.
* </li>
*
* <li>
* <code>sessionId</code> — Specify the ID of a session in order to retrieve renders
* specifically for that session. This property is optional. When no session ID is specified,
* then the method will return renders from any session created with your API key.
* </li>
*
* </ul>
*
* <p>If you don't pass in an <code>options</code> argument,
* the method returns up to 1000 renders, starting with the first render recorded.
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
*
* <ul>
*
* <li>
* <code>error</code> — An error object (if the call to the method fails).
* </li>
*
* <li>
* <code>renders</code> — An array of {@link Render} objects.
* </li>
*
* </ul>
*
* @method #listRenders
* @memberof OpenTok
*/
this.listRenders = render.listRenders.bind(null, apiConfig);
/**
* Gets an {@link Render} object for the given render ID.
*
* @param renderId {String} The render ID.
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
* <ul>
* <li><code>error</code> — An error object (if the call to the method fails). </li>
* <li><code>render</code> — The {@link Render} object.</li>
* </ul>
*
* @method #getArchive
* @memberof OpenTok
*/
this.getRender = render.getRender.bind(null, apiConfig);
/**
* Starts an Experience Composer for an OpenTok session. The Experience Composer
* instance is represented as a Render object.
* For more information, see the
* <a href="https://tokbox.com/developer/guides/experience-composer/">Experience Composer developer guide</a>.
* <p>
* Clients must be actively connected to the OpenTok session for you to successfully start
* rendering a session.
* <p>
*
* @param options {Object} An optional options object with the following properties (each
* of which is optional):
* <p>
* <ul>
* <li>
* <code>sessionId</code> (String) — The ID of a session (generated with the same
* `APIKEY`as specified in the URL) which you wish to start rendering into
* </li>
* <li>
* <code>token</code> (String) — A valid OpenTok token with a Publisher role and
* (optionally) connection data to be associated with the output stream.
* </li>
*
* <li>
* <code>url</code> (String) — A publically reachable URL controlled by the
* customer and capable of generating the content to be rendered without user intervention.
* </li>
*
* <li>
* <code>properties</code> (Object) — Initial configuration of Publisher properties for
* the composed output stream.
* </li>
*
* <li>
* <code>maxDuration</code> (Number) — The maximum time allowed for the Render, in
* seconds. After this time, the Render will be stopped automatically, if it is still running.
* </li>
*
* <li>
* <code>resolution</code> (String) — Resolution of the display area for the composition.
* </li>
*
* </ul>
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
*
* <ul>
*
* <li>
* <code>error</code> — An error object (if the call to the method fails).
* </li>
*
* <li>
* <code>render</code> — The {@link Render} object. This object includes properties
* defining the render, including the render ID.
* </li>
*
* </ul>
*
* @method #startRender
* @memberof OpenTok
*/
this.startRender = render.startRender.bind(null, apiConfig);
/**
* Stops an Experience Composer that is being rendered.
*
* @param renderId {String} The ID of the render you want to stop rendering.
* @return The {@link Archive} object corresponding to the archive being STOPPED.
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
*
* <ul>
*
* <li>
* <code>error</code> — An error object (if the call to the method fails).
* </li>
*
* <li>
* <code>render</code> — The {@link Render} object.
* </li>
*
* </ul>
*
* @method #stopRender
* @memberof OpenTok
*/
this.stopRender = render.stopRender.bind(null, apiConfig);
/**
* Starts a <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/">live
* streaming broadcast</a>.
*
* @param {String} sessionId The ID of the session to broadcast.
*
* @param {Object} options An object with the following form:
*
* <pre><code>{
* outputs: {
* hls: {
* dvr: false,
* lowLatency: false,
* },
* rtmp: [{
* id: "foo",
* serverUrl: "rtmp://myfooserver/myfooapp",
* streamName: "myfoostream"
* },
* {
* id: "bar",
* serverUrl: "rtmp://mybarserver/mybarapp",
* streamName: "mybarstream"
* }]
* },
* maxDuration: 5400,
* resolution: "640x480",
* layout: {
* type: "custom",
* stylesheet: "the layout stylesheet (only used with type == custom)",
* screenshareType: "the layout type to use when there is a screen-sharing stream (optional)"
* },
* streamMode: "manual"
* }
* </code></pre>
*
* <p>
* The <code>options</code> object includes the following properties:
*
* <ul>
* <li>
* <p>
* <code>outputs</code> (required) — This object defines the types of
* broadcast streams you want to start. You can include HLS, RTMP, or both
* as broadcast streams. If you include RTMP streaming, you can specify up to five
* target RTMP streams (or just one).
* </p>
* <p>
* For HLS, include a single <code>hls</code> property in the outputs object. This object
* includes the following optional properties:
* <ul>
* <li>
* <code>dvr</code> (Boolean) — Whether to enable <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#dvr">DVR functionality</a> — rewinding, pausing,
* and resuming — in players that support it (<code>true</code>), or not
* (<code>false</code>, the default). With DVR enabled, the HLS URL will include a
* <code>?DVR</code> query string appended to the end.
* </li>
* <li>
* <code>lowLatency</code> (Boolean) — Whether to enable <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#low-latency">low-latency mode</a> for the HLS
* stream. Some HLS players do not support low-latency mode. This feature is incompatible
* with DVR mode HLS broadcasts.
* </li>
* </ul>
* The HLS URL is returned in the <code>broadcastUrls</code> as the <code>hls</code>
* property in the {@link Broadcast} object passed into the callback methods of the
* {@link OpenTok#getBroadcast} and {@link OpenTok#listBroadcast} methods.
* </p>
* <p>
* For each RTMP stream, specify <code>serverUrl</code> (the RTMP server URL),
* <code>streamName</code> (the stream name, such as the YouTube Live stream name or
* the Facebook stream key), and (optionally) <code>id</code> (a unique ID for the stream).
* If you specify an ID, it will be included as the <code>id</code> property of the
* {@link Broadcast} object passed into the callback methods of the
* <code>startBroadcast()</code> method and the
* {@link OpenTok#getBroadcast OpenTok.getBroadcast()} method. OpenTok streams
* the session to each RTMP URL you specify. Note that OpenTok live streaming
* supports RTMP and RTMPS.
* </p>
* </li>
* <li>
* <code>maxDuration</code> (optional) — The maximum duration for the broadcast, in
* seconds. The broadcast will automatically stop when the maximum duration is reached.
* You can set the maximum duration to a value from 60 (60 seconds) to 36000 (10 hours).
* The default maximum duration is 4 hours (14,400 seconds).
* </li>
* <li>
* <code>resolution</code> (optional) — The resolution of the broadcast: either
* <code>"640x480"</code> (SD, the default) or <code>"1280x720"</code> (HD).
* </li>
* </li>
* <code>layout</code> (optional) — Specify this to assign the initial layout type for
* the broadcast. This object has three properties: <code>type</code>,
* <code>stylesheet</code>, and <code>screenshareType</code>, which are each strings.
* Valid values for the <code>type</code> property are <code>"bestFit"</code>
* (best fit), <code>"custom"</code> (custom), <code>"horizontalPresentation"</code>
* (horizontal presentation), <code>"pip"</code> (picture-in-picture), and
* <code>"verticalPresentation"</code> (vertical presentation)). If you specify
* a <code>"custom"</code> layout type, set the <code>stylesheet</code> property of
* the <code>layout</code> object to the stylesheet. (For other layout types, do not set
* a <code>stylesheet</code> property.) If you do not specify an initial layout type,
* the broadcast stream uses the Best Fit layout type. Set the <code>screenshareType</code>
* property to the layout type to use when there is a screen-sharing stream in the session.
* (This property is optional.) Note if you set the <code>screenshareType</code> property,
* you must set the <code>type</code> property to "bestFit" and leave the
* <code>stylesheet</code> property unset. For more information, see
* <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#configuring-video-layout-for-opentok-live-streaming-broadcasts)">Configuring
* video layout for OpenTok live streaming broadcasts</a>.
* </li>
* <li>
* <code>streamMode</code> (optional) — The stream mode for the broadcast. This can be
* set to one of the the following:
*
* <ul>
* <li> "auto" — Streams included in the broadcast are selected automatically
* (the default).</li>
*
* <li> "manual" — Specify streams to be included based on calls to the
* {@link OpenTok#addBroadcastStream OpenTok.addBroadcastStream()} and
* {@link OpenTok#removeBroadcastStream OpenTok.removeBroadcastStream()} methods.</li>
* </ul>
* </li>
* <li>
* <code>multiBroadcastTag</code> (optional) — Set this to support multiple
* broadcasts for the same session simultaneously. Set this to a unique string for
* each simultaneous broadcast of an ongoing session.
* Note that the <code>multiBroadcastTag</code> value is not included in the response
* for the methods to list live streaming broadcasts and get information about a live
* streaming broadcast.
* </li>
* </ul>
*
* @param {Function} callback A callback method that takes two parameters:
* <code>error</code>, which is set to an Error object on error, and
* <code>broadcast</code>, which is set to a {@link Broadcast} object on success.
*
* @method #startBroadcast
* @memberof OpenTok
*/
OpenTok.prototype.startBroadcast = function (sessionId, options, callback) {
var client = this.client;
if (typeof callback !== 'function') {
throw new errors.ArgumentError('No callback given to startBroadcast');
}
if (sessionId == null || sessionId.length === 0) {
callback(new errors.ArgumentError('No sessionId given to startBroadcast'));
}
else if (!options || typeof options !== 'object') {
callback(new errors.ArgumentError('No options given to startBroadcast'));
}
else {
options.sessionId = sessionId;
if (!options.streamMode) {
options.streamMode = 'auto';
}
if (options.outputs && options.outputs.hls) {
if (options.outputs.hls.dvr && options.outputs.hls.lowLatency) {
callback(new errors.ArgumentError('Cannot set both dvr and lowLatency on HLS'));
return;
}
}
client.startBroadcast(options, function (err, json) {
if (err) { return callback(new Error('Failed to start broadcast. ' + err)); }
return callback(null, new Broadcast(client, json));
});
}
};
/**
* Adds a stream to a broadcast that has the streamMode set to manual.
* You can call the method repeatedly with the same stream ID, to toggle
* the stream's audio or video in the broadcast.
*
* @param broadcastId {String} The broadcast ID.
*
* @param streamId {String} The stream ID to add to broadcast.
*
* @param broadcastOptions {Object} An object that has these properties:
*
* <ul>
*
* <li>
* <code>hasAudio</code> — Whether the broadcast should include the stream's audio
* (true, the default) or not (false).
* </li>
*
* <li>
* <code>hasVideo</code> — Whether the broadcast should include the stream's video
* (true, the default) or not (false).
* </li>
*
* </ul>
*
* @method #addBroadcastStream
* @memberof OpenTok
*/
this.addBroadcastStream = function addBroadcastStream(
broadcastId,
streamId,
broadcastOptions,
callback
) {
var client = this.client;
if (typeof broadcastOptions === 'function') {
callback = broadcastOptions;
broadcastOptions = {};
}
if (typeof callback !== 'function') {
throw (new errors.ArgumentError('No callback given to addBroadcastStream'));
}
if (broadcastId == null || broadcastId.length === 0) {
callback(new errors.ArgumentError('No broadcastId given to addBroadcastStream'));
}
if (streamId == null || streamId.length === 0) {
callback(new errors.ArgumentError('No streamId given to addBroadcastStream'));
}
broadcastOptions = {
addStream: streamId,
hasAudio: broadcastOptions.hasAudio || true,
hasVideo: broadcastOptions.hasVideo || true
};
client.patchBroadcast(broadcastId, broadcastOptions, callback);
};
/**
* Removes a stream from a broadcast that has the streamMode set to manual.
*
* @param broadcastId {String} The broadcast ID.
*
* @param streamId {String} The stream ID to remove from the broadcast.
*
* @param callback {Function} The function to call upon completing the operation. An error is
* passed into the function if the call fails.
*
* @method #removeBroadcastStream
* @memberof OpenTok
*/
this.removeBroadcastStream = function removeBroadcastStream(broadcastId, streamId, callback) {
var client = this.client;
if (typeof callback !== 'function') {
throw new errors.ArgumentError('No callback given to removeBroadcastStream');
}
if (broadcastId == null || broadcastId.length === 0) {
callback(new errors.ArgumentError('No archiveId provided'));
return;
}
if (streamId == null || streamId.length === 0) {
callback(new errors.ArgumentError('No streamId provided'));
return;
}
client.patchBroadcast(broadcastId, { removeStream: streamId }, callback);
};
/**
* Stops a live streaming broadcast.
*
* @param {String} broadcastId The ID of the broadcast.
*
* @param {Function} callback A callback method that takes two parameters:
* <code>error</code>, which is set to an Error object on error, and
* <code>broadcast</code>, which is set to a {@link Broadcast} object on success.
*
* @method #stopBroadcast
* @memberof OpenTok
*/
this.stopBroadcast = function stopBroadcast(broadcastId, callback) {
var client = this.client;
if (broadcastId === null || broadcastId.length === 0) {
callback(new errors.ArgumentError('No broadcast ID given'));
return;
}
if (typeof callback !== 'function') {
throw new errors.ArgumentError('No callback given to stopBroadcast');
}
client.stopBroadcast(broadcastId, function (err, json) {
if (err) return callback(new Error('Failed to stop broadcast. ' + err));
return callback(null, new Broadcast(client, json));
});
};
/**
* Returns information about a live streaming broadcast.
*
* @param {String} broadcastId The ID of the broadcast.
*
* @param {Function} callback A callback method that takes two parameters:
* <code>error</code>, which is set to an Error object on error, and
* <code>broadcast</code>, which is set to a {@link Broadcast} object on success.
*
* @method #getBroadcast
* @memberof OpenTok
*/
this.getBroadcast = function getBroadcast(broadcastId, callback) {
var client = this.client;
if (broadcastId === null || broadcastId.length === 0) {
callback(new errors.ArgumentError('No broadcast ID given'));
return;
}
if (typeof callback !== 'function') {
throw new errors.ArgumentError('No callback given to getBroadcast');
}
client.getBroadcast(broadcastId, function (err, json) {
if (err) return callback(new Error('Failed to get broadcast. ' + err));
return callback(null, new Broadcast(client, json));
});
};
/**
* Retrieves a List of {@link Broadcast} objects, representing broadcasts that are both
* completed and in-progress, for your API key.
*
* @param options {Object} An options parameter with three properties:
*
* <ul>
*
* <li>
* <code>count</code> — The maximum number of broadcasts to return.
* The default number of
* broadcasts returned is 50 (or fewer, if there are fewer than 50 broadcasts).
* The method returns a maximum of 1000 broadcasts.
* </li>
*
* <li>
* <code>offset</code> — The offset for the first broadcast to list (starting with the
* first broadcast recorded as offset 0). 1 is the offset of the broadcast that started prior
* to the most recent broadcast. This property is optional; the default is 0.
* </li>
*
* <li>
* <code>sessionId</code> — Specify the ID of a session in order to retrieve broadcasts
* specifically for that session. This property is optional. When no session ID is specified,
* then the method will return broadcasts from any session created with your API key.
* </li>
*
* </ul>
*
* <p>If you don't pass in an <code>options</code> argument,
* the method returns up to 1000 broadcasts starting with the first broadcast recorded.
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
*
* <ul>
*
* <li>
* <code>error</code> — An error object (if the call to the method fails).
* </li>
*
* <li>
* <code>broadcasts</code> — An array of {@link Broadcast} objects.
* </li>
*
* </ul>
*
* @method #listBroadcasts
* @memberof OpenTok
*/
this.listBroadcasts = function listBroadcasts(options, callback) {
var query = [];
var queryString = null;
if (typeof options === 'function') {
callback = options;
options = {};
}
if (typeof callback !== 'function') {
throw new errors.ArgumentError('No callback given to listBroadcasts');
}
if (options.offset) {
query.push('offset=' + parseInt(options.offset, 10));
}
if (options.count) {
query.push('count=' + parseInt(options.count, 10));
}
if (options.sessionId) {
query.push('sessionId=' + options.sessionId);
}
queryString = query.join('&');
return this.client.listBroadcasts(
queryString,
function cb(err, json, totalCount) {
if (err) {
return callback(err);
}
return callback(null, json, totalCount);
}
);
};
/**
* Sets (or updates) the layout of the broadcast. See
* <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#configuring-video-layout-for-opentok-live-streaming-broadcasts">
* Configuring video layout for OpenTok live streaming broadcasts</a>.
*
* @param {String} broadcastId The ID of the broadcast.
*
* @param type {String} The layout type. Set this to "bestFit", "pip", "verticalPresentation",
* "horizontalPresentation", "focus", or "custom". For a description of these layout types, see
* <a href=https://tokbox.com/developer/guides/broadcast/live-streaming/#configuring-video-layout-for-opentok-live-streaming-broadcasts> Configuring
* layout for OpenTok live streaming broadcasts</a>.
*
* @param stylesheet {String} (Optional) The stylesheet for a custom layout. Set this
* parameter if you set <code>type</code> to "custom". Otherwise, leave it undefined or
* set to <code>null</code>.
*
* @param screenshareType {String} (Optional) The layout type to use when there is
* a screen-sharing stream in the session. Note that to use this parameter, you must set
* the <code>type</code> parameter to "bestFit" and set the <code>stylesheet</code>
* parameter to <code>null</code>.
*
* @param callback {Function} The function to call upon completing the operation. Upon error,
* an Error object is passed into the function. Upon success, the function is called
* with no Error object passed in.
*
* @method #setBroadcastLayout
* @memberof OpenTok
*/
this.setBroadcastLayout = function setBroadcastLayout(
broadcastId,
type,
stylesheet,
screenshareType,
callback
) {
if (typeof broadcastId !== 'string') {
return callback(new Error('Invalid arguments -- must provide an broadcastId string.'));
}
if (typeof type !== 'string') {
return callback(new Error('Invalid arguments -- must provide a type string.'));
}
if (typeof stylesheet === 'function') {
if (callback) {
return callback(new Error('Invalid arguments -- stylesheet cannot be a function.'));
}
callback = stylesheet; // eslint-disable-line no-param-reassign
}
else if (stylesheet && typeof stylesheet !== 'string') {
return callback(new Error('Invalid arguments -- stylesheet must be a string.'));
}
if (typeof screenshareType === 'function') {
if (callback) {
return callback(new Error('Invalid arguments -- screenshareType cannot be a function.'));
}
callback = screenshareType; // eslint-disable-line no-param-reassign
}
else if (screenshareType && typeof screenshareType !== 'string') {
return callback(new Error('Invalid arguments -- screenshareType must be a string.'));
}
else if (screenshareType && type !== 'bestFit') {
return callback(new Error('Invalid arguments -- type must be set to "bestFit" if you set screenshareType.'));
}
if (typeof callback !== 'function') {
return callback(new Error('Invalid arguments -- must provide a callback function.'));
}
return this.client.setBroadcastLayout(
{
broadcastId: broadcastId,
type: type,
stylesheet: stylesheet,
screenshareType: screenshareType
},
callback
);
};
/**
* Sets the layout class list for streams in a session. Layout classes are used in
* the layout for composed archives and live streaming broadcasts. For more information, see
* <a href="https://tokbox.com/developer/guides/archiving/layout-control.html">Customizing
* the video layout for composed archives</a> and
* <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#configuring-video-layout-for-opentok-live-streaming-broadcasts">Configuring
* video layout for OpenTok live streaming broadcasts</a>.
*
* <p>
* You can set the initial layout class list for streams published by a client when you generate
* used by the client. See the {@link OpenTok#generateToken OpenTok.generateToken()} method.
*
* @param sessionId {String} The session ID of the session the streams belong to.
*
* @param classListArray {Array} (Optional) An array defining the class lists to apply to
* streams. Each element in the array is an object with two properties: <code>id</code> and
* <code>layoutClassList</code>. The <code>id</code> property is the stream ID (a String),
* and the <code>layoutClassList</code> is an array of class names (Strings) to apply to the
* stream. Set <code>layoutClassList</code> to an empty array to clear the layout class list for
* a stream. For example, this <code>streamClassArray</code> array sets the layout class list for
* three streams:
* <p>
* <pre>
* const classListArray = [
* { id: '7b09ec3c-26f9-43d7-8197-f608f13d4fb6', layoutClassList: ['focus'] },
* { id: '567bc941-6ea0-4c69-97fc-70a740b68976', layoutClassList: ['top'] },
* { id: '307dc941-0450-4c09-975c-705740d08970', layoutClassList: ['bottom'] }
* ];
* </pre>
*
* @param callback {Function} The function to call upon completing the operation. Upon error,
* an <code>error</code> object is passed into the function. Upon success, the function is called
* with no error object passed in.
*
* @method #setStreamClassLists
* @memberof OpenTok
*/
this.setStreamClassLists = function setStreamClassLists(
sessionId,
classListArray,
callback
) {
var i;
var j;
var layoutObj;
if (typeof sessionId !== 'string') {
return callback(new Error('Invalid arguments -- must provide an sessionId string.'));
}
if (!Array.isArray(classListArray)) {
return callback(new Error('Invalid arguments -- must provide a streamClassArray array.'));
}
for (i = 0; i < classListArray.length; i += 1) {
layoutObj = classListArray[i];
if (typeof layoutObj.id !== 'string') {
return callback(new Error('Invalid arguments -- each element in the streamClassArray ' +
'must have an id string.'));
}
if (!Array.isArray(layoutObj.layoutClassList)) {
return callback(new Error('Invalid arguments -- each element in the streamClassArray ' +
'must have a layoutClassList array.'));
}
for (j = 0; j < layoutObj.layoutClassList.length; j += 1) {
if (typeof layoutObj.layoutClassList[j] !== 'string') {
return callback(new Error('Invalid arguments -- each element in the layoutClassList ' +
'array must be a string (defining class names).'));
}
}
}
if (typeof callback !== 'function') {
return callback(new Error('Invalid arguments -- must provide a callback function.'));
}
return this.client.setStreamClassLists(sessionId, classListArray, callback);
};
/**
* Sends a signal to all the connections in a session or to a specific one.
* <p>
* Clients must be actively connected to the OpenTok session for you to successfully send
* a signal to them.
* <p>
* For more information, see the
* <a href="https://www.tokbox.com/developer/guides/signaling">OpenTok signaling</a>
* programming guide.
*
* @param sessionId The session ID of the OpenTok session where you want to send the signal.
*
* @param connectionId The connection ID of a client connected to the session. Leave
* this empty if you want to send a signal to all connections in the session.
*
* @param payload An object with optional <code>data</code> and <code>type</code> properties:
*
* <p>
*
* <ul>
* <li>
* <code>data</code> — The data to send. The limit to the length of data string
* is 8kB. Do not set the data string to null or undefined.
* </li>
*
* <li>
* <code>type</code> — The type of the signal. Clients can use the type to filter
* signals. The maximum length of the type string is 128 characters.
* </li>
* </ul>
*
* @param callback {Function} The callback function invoked when the call to the method
* succeeds or fails. If the call fails, an error object is passed into the callback function.
*
* @method #signal
* @memberof OpenTok
*/
this.signal = signaling.signal.bind(null, apiConfig);
/**
* Disconnects a participant from an OpenTok session.
*
* This is the server-side equivalent to the
* <a href="https://www.tokbox.com/developer/guides/moderation/js/#force_disconnect">
* forceDisconnect() method in OpenTok.js</a>
*
* @param sessionId The session ID for the OpenTok session that the client you want
* to disconnect is connected to.
*
* @param connectionId The connection ID of the client you want to disconnect.
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
*
* <ul>
*
* <li>
* <code>error</code> — An error object (if the call to the method fails).
* </li>
*
* </ul>
*
* @method #forceDisconnect
* @memberof OpenTok
*/
this.forceDisconnect = moderation.forceDisconnect.bind(null, apiConfig);
/**
* Gets info about a stream. The stream must be an active stream in an OpenTok session.
*
* @param sessionId {String} The session ID of the OpenTok session containing the stream.
*
* @param options {String} The stream ID.
*
* @param callback {Function} The function to call upon completing the operation. Two arguments
* are passed to the function:
*
* <ul>
*
* <li>
* <code>error</code> — An error object (if the call to the method fails). This is
* set to null if there is no error. Calling this method results in an error if you pass in
* an invalid stream ID or an invalid session ID.
* </li>
*
* <li>
* <code>stream</code> — The {@link Stream} object. This object includes properties
* defining the stream. This is undefined if there is an error.
* </li>
*
* </ul>
*
* @method #getStream
* @memberof OpenTok
*/
this.getStream = function getStream(sessionId, streamId, callback) {
if (!sessionId || typeof sessionId !== 'string')