265 lines
8.7 KiB
JavaScript
265 lines
8.7 KiB
JavaScript
/**
|
|
* An interface for an Audio/Video client which is extended to provide broadcasting functionality.
|
|
* @interface
|
|
* @param {AVMaster} master The master orchestration instance
|
|
* @param {AVSettings} settings The audio/video settings being used
|
|
*/
|
|
class AVClient {
|
|
constructor(master, settings) {
|
|
|
|
/**
|
|
* The master orchestration instance
|
|
* @type {AVMaster}
|
|
*/
|
|
this.master = master;
|
|
|
|
/**
|
|
* The active audio/video settings being used
|
|
* @type {AVSettings}
|
|
*/
|
|
this.settings = settings;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Is audio broadcasting push-to-talk enabled?
|
|
* @returns {boolean}
|
|
*/
|
|
get isVoicePTT() {
|
|
return this.settings.client.voice.mode === "ptt";
|
|
}
|
|
|
|
/**
|
|
* Is audio broadcasting always enabled?
|
|
* @returns {boolean}
|
|
*/
|
|
get isVoiceAlways() {
|
|
return this.settings.client.voice.mode === "always";
|
|
}
|
|
|
|
/**
|
|
* Is audio broadcasting voice-activation enabled?
|
|
* @returns {boolean}
|
|
*/
|
|
get isVoiceActivated() {
|
|
return this.settings.client.voice.mode === "activity";
|
|
}
|
|
|
|
/**
|
|
* Is the current user muted?
|
|
* @returns {boolean}
|
|
*/
|
|
get isMuted() {
|
|
return this.settings.client.users[game.user.id]?.muted;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/* Connection */
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* One-time initialization actions that should be performed for this client implementation.
|
|
* This will be called only once when the Game object is first set-up.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async initialize() {
|
|
throw Error("The initialize() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Connect to any servers or services needed in order to provide audio/video functionality.
|
|
* Any parameters needed in order to establish the connection should be drawn from the settings object.
|
|
* This function should return a boolean for whether the connection attempt was successful.
|
|
* @returns {Promise<boolean>} Was the connection attempt successful?
|
|
*/
|
|
async connect() {
|
|
throw Error("The connect() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Disconnect from any servers or services which are used to provide audio/video functionality.
|
|
* This function should return a boolean for whether a valid disconnection occurred.
|
|
* @returns {Promise<boolean>} Did a disconnection occur?
|
|
*/
|
|
async disconnect() {
|
|
throw Error("The disconnect() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/* Device Discovery */
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Provide an Object of available audio sources which can be used by this implementation.
|
|
* Each object key should be a device id and the key should be a human-readable label.
|
|
* @returns {Promise<{object}>}
|
|
*/
|
|
async getAudioSinks() {
|
|
return this._getSourcesOfType("audiooutput");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Provide an Object of available audio sources which can be used by this implementation.
|
|
* Each object key should be a device id and the key should be a human-readable label.
|
|
* @returns {Promise<{object}>}
|
|
*/
|
|
async getAudioSources() {
|
|
return this._getSourcesOfType("audioinput");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Provide an Object of available video sources which can be used by this implementation.
|
|
* Each object key should be a device id and the key should be a human-readable label.
|
|
* @returns {Promise<{object}>}
|
|
*/
|
|
async getVideoSources() {
|
|
return this._getSourcesOfType("videoinput");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Obtain a mapping of available device sources for a given type.
|
|
* @param {string} kind The type of device source being requested
|
|
* @returns {Promise<{object}>}
|
|
* @private
|
|
*/
|
|
async _getSourcesOfType(kind) {
|
|
if ( !("mediaDevices" in navigator) ) return {};
|
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
return devices.reduce((obj, device) => {
|
|
if ( device.kind === kind ) {
|
|
obj[device.deviceId] = device.label || game.i18n.localize("WEBRTC.UnknownDevice");
|
|
}
|
|
return obj;
|
|
}, {});
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/* Track Manipulation */
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Return an array of Foundry User IDs which are currently connected to A/V.
|
|
* The current user should also be included as a connected user in addition to all peers.
|
|
* @returns {string[]} The connected User IDs
|
|
*/
|
|
getConnectedUsers() {
|
|
throw Error("The getConnectedUsers() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Provide a MediaStream instance for a given user ID
|
|
* @param {string} userId The User id
|
|
* @returns {MediaStream|null} The MediaStream for the user, or null if the user does not have one
|
|
*/
|
|
getMediaStreamForUser(userId) {
|
|
throw Error("The getMediaStreamForUser() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Provide a MediaStream for monitoring a given user's voice volume levels.
|
|
* @param {string} userId The User ID.
|
|
* @returns {MediaStream|null} The MediaStream for the user, or null if the user does not have one.
|
|
*/
|
|
getLevelsStreamForUser(userId) {
|
|
throw new Error("An AVClient subclass must define the getLevelsStreamForUser method");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Is outbound audio enabled for the current user?
|
|
* @returns {boolean}
|
|
*/
|
|
isAudioEnabled() {
|
|
throw Error("The isAudioEnabled() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Is outbound video enabled for the current user?
|
|
* @returns {boolean}
|
|
*/
|
|
isVideoEnabled() {
|
|
throw Error("The isVideoEnabled() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Set whether the outbound audio feed for the current game user is enabled.
|
|
* This method should be used when the user marks themselves as muted or if the gamemaster globally mutes them.
|
|
* @param {boolean} enable Whether the outbound audio track should be enabled (true) or disabled (false)
|
|
*/
|
|
toggleAudio(enable) {
|
|
throw Error("The toggleAudio() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Set whether the outbound audio feed for the current game user is actively broadcasting.
|
|
* This can only be true if audio is enabled, but may be false if using push-to-talk or voice activation modes.
|
|
* @param {boolean} broadcast Whether outbound audio should be sent to connected peers or not?
|
|
*/
|
|
toggleBroadcast(broadcast) {
|
|
throw Error("The toggleBroadcast() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Set whether the outbound video feed for the current game user is enabled.
|
|
* This method should be used when the user marks themselves as hidden or if the gamemaster globally hides them.
|
|
* @param {boolean} enable Whether the outbound video track should be enabled (true) or disabled (false)
|
|
*/
|
|
toggleVideo(enable) {
|
|
throw Error("The toggleVideo() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Set the Video Track for a given User ID to a provided VideoElement
|
|
* @param {string} userId The User ID to set to the element
|
|
* @param {HTMLVideoElement} videoElement The HTMLVideoElement to which the video should be set
|
|
*/
|
|
async setUserVideo(userId, videoElement) {
|
|
throw Error("The setUserVideo() method must be defined by an AVClient subclass.");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/* Settings and Configuration */
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Handle changes to A/V configuration settings.
|
|
* @param {object} changed The settings which have changed
|
|
*/
|
|
onSettingsChanged(changed) {}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Replace the local stream for each connected peer with a re-generated MediaStream.
|
|
*/
|
|
async updateLocalStream() {
|
|
throw Error("The updateLocalStream() method must be defined by an AVClient subclass.");
|
|
}
|
|
}
|