UNPKG

opentok

Version:
1,293 lines (1,239 loc) 87.3 kB
/* * OpenTok server-side SDK */ // Dependencies const debug = require('debug'); 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 captions = require('./captions.js'); var OpenTok; var key; const { tokenGenerate } = require('@vonage/jwt'); const log = debug('@opentok'); /* * 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'); } // 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, working with archives, and more. * <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; const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; const callVonage = `${apiSecret}`.startsWith('-----BEGIN PRIVATE KEY') && uuidRegex.test(apiKey); // TODO: this is a pretty obvious seam, the integration could be more smooth apiConfig = { apiEndpoint: callVonage ? 'https://video.api.vonage.com' : 'https://api.opentok.com' , apiKey: apiKey, apiSecret: apiSecret, auth: { expire: 300 }, callVonage: callVonage, }; // env can be either an object with a bunch of DI options, or a simple string for the apiUrl clientConfig = { request: {}, apiUrl: callVonage ? 'https://video.api.vonage.com' : 'https://api.opentok.com', callVonage: callVonage, }; if (_.isString(env)) { clientConfig.apiUrl = env; apiConfig.apiEndpoint = env; } 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) &mdash; 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) &mdash; 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) &mdash; 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) &mdash; 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) &mdash; 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) &mdash; For a composed archive, set this to the * resolution of the archive. Valid values are "1920x1080", "1280x720" or "640x480" * (the default). * </li> * <li> * <code>streamMode</code> (optional) &mdash; The stream mode for the archive. This can be * set to one of the the following: * * <ul> * <li> "auto" &mdash; Streams included in the archive are selected automatically * (the default).</li> * * <li> "manual" &mdash; 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) &mdash; 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> * <li> * <code>maxBitrate</code> (Number) &mdash; The maximum video bitrate for the archive, * in bits per second. This option is only valid for composed archives. Set the maximum * video bitrate to control the size of the composed archive. This maximum bitrate * applies to the video bitrate only. If the output archive has audio, those bits will be * excluded from the limit. This value is mutually exclusive with <code>quantizationParameter</code> * </li> * <li> * <code>quantizationParameter</code> (Number) &mdash;quantization parameter (QP) is an optional video encoding * value allowed for composed archiving, smaller values generate higher quality and larger archives, larger values * generate lower quality and smaller archives, QP uses variable bitrate (VBR). This value is mutally exclusive * with <code>maxBitrate</code> * </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> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>archive</code> &mdash; 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> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>archive</code> &mdash; 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> &mdash; An error object (if the call to the method fails). </li> * <li><code>archive</code> &mdash; 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> &mdash; 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> &mdash; 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> &mdash; 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> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>archives</code> &mdash; 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> &mdash; Whether the composed archive should include the stream's audio * (true, the default) or not (false). * </li> * * <li> * <code>hasVideo</code> &mdash; 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> &mdash; 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 ); }; /** * Starts live captions for an OpenTok session. * <p> * The maximum allowed duration is 4 hours, after which the audio captioning will stop without * any effect on the ongoing OpenTok Session. An event will be posted to your callback URL if * provided when starting the captions. * <p> * Each OpenTok session supports only one audio captioning session. * * @param sessionId The session ID of the OpenTok session to archive. * * @param token A valid OpenTok token with role set to Moderator. * * @param options {Object} An optional options object with the following properties (each * of which is optional): * <p> * <ul> * <li> * <code>languageCode</code> (String) &mdash; The BCP-47 code for a spoken language used on * this call. The default value is "en-US". The following language codes are supported: * "en-AU" (English, Australia), "en-GB" (Englsh, UK), "es-US" (English, US), * "zh-CN” (Chinese, Simplified), "fr-FR" (French), "fr-CA" (French, Canadian), * "de-DE" (German), "hi-IN" (Hindi, Indian), "it-IT" (Italian), "ja-JP" (Japanese), * "ko-KR" (Korean), "pt-BR" (Portuguese, Brazilian), "th-TH" (Thai). * </li> * <li> * <code>maxDuration</code> (Integer) &mdash; The maximum duration for the audio captioning, * in seconds. The default value is 14,400 seconds (4 hours), the maximum duration allowed. * The minimum value for maxDuration is 300 (300 seconds, or 5 minutes). * </li> * <li> * <code>partialCaptions</code> (Boolean) &mdash; Whether to enable this to faster captioning * at the cost of some degree of inaccuracies. The default value is true. * </li> * </ul> * * For more information on captions, see the * <a href="https://tokbox.com/developer/rest/#starting-live-captions">OpenTok captions</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> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>captionID</code> &mdash; The id of the captions * </li> * * </ul> * * @method #startCaptions * @memberof OpenTok */ this.startCaptions = captions.startCaptions.bind(null, apiConfig); /** * Stops live captions for an OpenTok Session * * @param captionId The session ID of the OpenTok session to archive. * * </ul> * * For more information on captions, see the * <a href="https://tokbox.com/developer/rest/#starting-live-captions">OpenTok captions</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> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>success</code> &mdash; True always * </li> * * </ul> * * @method #startCaptions * @memberof OpenTok */ this.stopCaptions = captions.stopCaptions.bind(null, apiConfig); /** * 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> &mdash; 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> &mdash; 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> &mdash; 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> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>renders</code> &mdash; An array of {@link Render} objects. * </li> * * </ul> * * @method #listRenders * @memberof OpenTok */ this.listRenders = render.listRenders.bind(null, apiConfig); /** * Gets a {@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> &mdash; An error object (if the call to the method fails). </li> * <li><code>render</code> &mdash; The {@link Render} object.</li> * </ul> * * @method #getRender * @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) &mdash; 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) &mdash; 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) &mdash; 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) &mdash; Initial configuration of Publisher properties for * the composed output stream. * </li> * * <li> * <code>maxDuration</code> (Number) &mdash; 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) &mdash; 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> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>render</code> &mdash; 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> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>render</code> &mdash; The {@link Render} object. * </li> * * </ul> * * @method #stopRender * @memberof OpenTok */ this.stopRender = render.stopRender.bind(null, apiConfig); /** * Starts a live streaming broadcast. See the * <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/">live * streaming broadcast</a> developer guide. * * @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) &mdash; 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) &mdash; 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) &mdash; The resolution of the broadcast: either * <code>"640x480"</code> (SD, the default), <code>"1280x720"</code> (HD) or * <code>"1920x1080"</code> (FHD). * </li> * </li> * <code>layout</code> (optional) &mdash; 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) &mdash; The stream mode for the broadcast. This can be * set to one of the the following: * * <ul> * <li> "auto" &mdash; Streams included in the broadcast are selected automatically * (the default).</li> * * <li> "manual" &mdash; 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) &mdash; 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> &mdash; Whether the broadcast should include the stream's audio * (true, the default) or not (false). * </li> * * <li> * <code>hasVideo</code> &mdash; Whether the broadcast should include the stream's video * (true, the default) or not (false). * </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 #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> &mdash; 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> &mdash; 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> &mdash; 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> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>broadcasts</code> &mdash; 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 ); }; /** * Sends audio from a Vonage Video API session to a WebSocket. * See the <a href="https://tokbox.com/developer/guides/audio-connector/">Audio Connector</a> * developer guide. * * @param sessionId {String} (required) The OpenTok session ID that includes the OpenTok streams you want to * include in the WebSocket stream * * @param token {String} (required) The OpenTok token to be used for the Audio Streamer connection to the. * OpenTok session. You can add token data to identify that the connection is the Audio Streamer * endpoint or for other identifying data * * @param websocketUri {String} (required) A publicly reachable WebSocket URI to be used for the destination * of the audio stream (such as "wss://service.com/ws-endpoint"). * * @param options {Object} An optional options object with the following properties * (all of which are optional): * <p> * <ul> * <li> * <code>streams</code> (Array) &mdash; An array of stream IDs for the OpenTok streams * you want to include in the WebSocket stream. If you omit this property, all streams * in the session will be included. * </li> * <li> * <code>headers</code> (Object) &mdash; An object of key-value pairs of headers to be * sent to your WebSocket server with each message, with a maximum length of 512 bytes. * </li> * </ul> * * @param callback {Function} The function to call upon completing the operation. Upon error, * an <code>error</code> object is passed in as the first parameter of the function. * Upon success, the function is called with no error object passed in (as the first parameter) * and the second parameter is an object with the following properties: * * <ul> * <li> * <code>id</code> -- A unique ID identifying the Audio Streamer WebSocket connection. * </li> * <li> * <code>connectionId</code> -- The OpenTok connection ID for the Audio Streamer * WebSocket connection in