twilio-conferencing-core-engine
Version:
twilio-conferencing-core-engine
118 lines (103 loc) • 3.45 kB
JavaScript
;
const { createLocalTracks } = require("twilio-video");
const localTracks = {
audio: null,
video: null,
};
/**
* Start capturing media from the given input device.
* @param kind - 'audio' or 'video'
* @param deviceId - the input device ID
* @param render - the render callback
* @returns {Promise<void>} Promise that is resolved if successful
*/
async function applyInputDevice(kind, deviceId, render) {
// Create a new LocalTrack from the given Device ID.
const [track] = await createLocalTracks({ [kind]: { deviceId } });
// Stop the previous LocalTrack, if present.
if (localTracks[kind]) {
localTracks[kind].stop();
}
// Render the current LocalTrack.
localTracks[kind] = track;
render(new MediaStream([track.mediaStreamTrack]));
}
//safari requires getUserMedia to be called before devices are enumerated with deviceId and label
//otherwise it only returns the default
async function ensureGetMediaCalled() {
return await navigator.mediaDevices.getUserMedia({
audio: true,
video: true,
});
}
/**
* Get the list of input devices of a given kind.
* @param kind - 'audio' | 'video'
* @returns {Promise<MediaDeviceInfo[]>} the list of media devices
*/
async function getInputDevices(kind) {
return ensureGetMediaCalled()
.then(async () => {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter((device) => device.kind === `${kind}input`);
})
.catch(() => null);
}
/**
* Get the list of input devices of a given kind.
* @param kind - 'audio' | 'video'
* @returns {Promise<MediaDeviceInfo[]>} the list of media devices
*/
async function getOutputDevices(kind) {
return ensureGetMediaCalled()
.then(async () => {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter((device) => device.kind === `${kind}output`);
})
.catch(() => null);
}
/**
*
* @param {function} callback the function to invoke with the list of new array of media devices
*/
async function registerOnMediaDevicesListChangeHandler(callback) {
ensureGetMediaCalled()
.then(async () => {
navigator.mediaDevices.ondevicechange = () => {
navigator.mediaDevices
.enumerateDevices()
.then((devices) => callback(devices))
.catch(() => callback(null));
};
})
.catch(() => callback(null));
}
/**
* Select the default input for the given media kind.
* @param kind - 'audio' or 'video'
* @param render - the media render function
* @returns {Promise<string>} the device ID of the selected media input
*/
async function selectDefaultMedia(kind, render) {
// Get the list of available media input devices.
let devices = await getInputDevices(kind);
// Apply the default media input device.
await applyInputDevice(kind, devices[0].deviceId, render);
// If all device IDs and/or labels are empty, that means they were
// enumerated before the user granted media permissions. So, enumerate
// the devices again.
if (devices.every(({ deviceId, label }) => !deviceId || !label)) {
devices = await getInputDevices(kind);
}
return new Promise((resolve) => {
localStorage.setItem(`${kind}DeviceId`, devices[0].deviceId);
resolve(devices[0].deviceId);
});
}
module.exports = {
getInputDevices,
getOutputDevices,
registerOnMediaDevicesListChangeHandler,
selectDefaultMedia,
applyInputDevice,
};