UNPKG

media-utils

Version:

Opinionated collection of user media utils by @dapotatoman

219 lines (209 loc) 7.09 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function destroyStream(stream, emitEvent) { if (!stream) return; stream.getTracks().forEach((track) => { track.stop(); stream.removeTrack(track); if (emitEvent) { track.dispatchEvent(new Event("ended")); stream.dispatchEvent(new Event("removeTrack")); } }); } function destroyStreams(streams, emitEvent) { if (!(streams == null ? void 0 : streams.length)) throw new Error("No stream was passed"); streams.forEach((stream) => stream.getTracks().forEach((track) => { track.stop(); stream.removeTrack(track); if (emitEvent) { track.dispatchEvent(new Event("ended")); stream.dispatchEvent(new Event("removetrack")); } })); } function getStream(constraints) { return navigator.mediaDevices.getUserMedia(constraints); } function amplifyStream(stream, level) { const [audioTrack] = stream.getAudioTracks(); const streamClone = new MediaStream([audioTrack]); const ctx = new AudioContext(); const source = ctx.createMediaStreamSource(streamClone); const destination = ctx.createMediaStreamDestination(); const gainNode = ctx.createGain(); gainNode.gain.value = level; [source, gainNode, destination].reduce((a, b) => a && a.connect(b)); const [newTrack] = destination.stream.getAudioTracks(); stream.removeTrack(audioTrack); stream.addTrack(newTrack); function destroy() { destroyStream(stream); destroyStream(streamClone); destroyStream(destination.stream); destination.disconnect(); source.disconnect(); if (ctx.state !== "closed") ctx.close(); } function setLevel(value) { gainNode.gain.value = value; } return { destroy, setLevel }; } function muxStreams(videoStream, ...audioStream) { audioStream.forEach((stream) => stream.getAudioTracks().forEach((track) => videoStream.addTrack(track))); return videoStream; } function onTracksEnded(stream, callback) { const tracks = stream.getTracks(); const tracksRunning = tracks.map((i) => i.enabled && i.readyState === "live"); tracks.forEach((track, index) => track.addEventListener("ended", () => { tracksRunning[index] = false; if (!tracksRunning.find((i) => i === true)) callback(); })); } function onAnyTrackEnded(stream, callback, destroy) { const tracks = stream.getTracks(); function unregister(i) { tracks.forEach((e) => e.removeEventListener("ended", i)); if (destroy) destroyStream(stream); } function listener() { callback(); unregister(listener); } tracks.forEach((i) => i.addEventListener("ended", listener)); return unregister.bind(unregister, listener); } function getDevices(kind) { return navigator.mediaDevices.enumerateDevices().then((devices) => { if (kind) return devices.filter((i) => i.kind === kind); return devices; }); } function getCategorizedDevices() { return getDevices().then((devices) => { const list = {}; devices.forEach((i) => list[i.kind].push(i)); return list; }); } function getFilteredDevices(key, value) { return getDevices().then((devices) => devices.filter((i) => (i == null ? void 0 : i[key]) === value)); } async function getDevice(key, value, devices) { const entries = devices || await getDevices(); return entries.filter((i) => (i == null ? void 0 : i[key]) === value); } class EventBus { constructor() { this.bus = new EventTarget(); } on(type, listener) { this.bus.addEventListener(type, listener); } once(type, listener) { this.bus.addEventListener(type, listener, { once: true }); } off(type, listener) { this.bus.removeEventListener(type, listener); } emit(type, data) { const evt = new CustomEvent(type, { detail: data }); this.bus.dispatchEvent(evt); } } function onDeviceChange(listener, options) { navigator.mediaDevices.addEventListener("devicechange", listener, options); return () => navigator.mediaDevices.removeEventListener("devicechange", listener, options); } function watchDevice(key, value) { const events = new EventBus(); let deviceExists = false; const updateDevice = () => getDevice(key, value).then(([i]) => deviceExists = !!i); (async () => { await updateDevice(); onDeviceChange(async () => { const device = await getDevice(key, value); if (deviceExists && !device) events.emit("removed"); else if (!deviceExists && device) events.emit("added"); deviceExists = !!device; }); })(); return events; } function watchDevicesList(onUpdate, immediate) { const update = () => getDevices().then(onUpdate); if (immediate) update(); return onDeviceChange(update, { passive: true }); } function requestMediaPermissions() { return Promise.all([ navigator.mediaDevices.getUserMedia({ audio: true }), navigator.mediaDevices.getUserMedia({ video: true }), navigator.mediaDevices.getUserMedia({ audio: true, video: true }) ]).then((streams) => { streams.forEach((i) => destroyStream(i)); return true; }).catch(() => false); } function requestPermission(name) { const key = { camera: "video", microphone: "audio" }[name]; return navigator.mediaDevices.getUserMedia({ [key]: true }).then(() => true).catch(() => false); } function watchPermission(name, onChange, immediate) { const destroy = navigator.permissions.query({ name }).then((status) => { if (immediate) onChange(status); status.addEventListener("change", () => onChange(status)); return () => status.removeEventListener("change", () => onChange(status)); }); return () => { destroy.then((fn) => fn == null ? void 0 : fn()); }; } function watchPermissions(names, onChange, immediate) { const destroyFns = []; names.forEach((name) => destroyFns.push(watchPermission(name, (i) => onChange(name, i), immediate))); return () => destroyFns.forEach((fn) => fn == null ? void 0 : fn()); } function hasPermission(name) { return navigator.permissions.query({ name }).then((status) => { return status.state === "granted"; }); } function hasMediaPermissions(name) { return Promise.all([ hasPermission("camera"), hasPermission("microphone") ]).then((i) => i[0] && i[1]); } exports.amplifyStream = amplifyStream; exports.destroyStream = destroyStream; exports.destroyStreams = destroyStreams; exports.getCategorizedDevices = getCategorizedDevices; exports.getDevice = getDevice; exports.getDevices = getDevices; exports.getFilteredDevices = getFilteredDevices; exports.getStream = getStream; exports.hasMediaPermissions = hasMediaPermissions; exports.hasPermission = hasPermission; exports.muxStreams = muxStreams; exports.onAnyTrackEnded = onAnyTrackEnded; exports.onDeviceChange = onDeviceChange; exports.onTracksEnded = onTracksEnded; exports.requestMediaPermissions = requestMediaPermissions; exports.requestPermission = requestPermission; exports.watchDevice = watchDevice; exports.watchDevicesList = watchDevicesList; exports.watchPermission = watchPermission; exports.watchPermissions = watchPermissions;