2021-06-23 15:26:33 +08:00
|
|
|
/*
|
|
|
|
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
|
|
|
|
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2021-10-23 06:23:32 +08:00
|
|
|
import EventEmitter from 'events';
|
|
|
|
import { logger } from "matrix-js-sdk/src/logger";
|
|
|
|
|
2021-06-23 15:26:33 +08:00
|
|
|
import SettingsStore from "./settings/SettingsStore";
|
|
|
|
import { SettingLevel } from "./settings/SettingLevel";
|
2021-08-29 15:39:34 +08:00
|
|
|
import { MatrixClientPeg } from "./MatrixClientPeg";
|
2021-06-23 15:26:33 +08:00
|
|
|
|
2021-07-09 19:08:39 +08:00
|
|
|
// XXX: MediaDeviceKind is a union type, so we make our own enum
|
|
|
|
export enum MediaDeviceKindEnum {
|
|
|
|
AudioOutput = "audiooutput",
|
|
|
|
AudioInput = "audioinput",
|
|
|
|
VideoInput = "videoinput",
|
2021-06-23 15:26:33 +08:00
|
|
|
}
|
|
|
|
|
2021-07-09 19:08:39 +08:00
|
|
|
export type IMediaDevices = Record<MediaDeviceKindEnum, Array<MediaDeviceInfo>>;
|
|
|
|
|
2021-06-23 15:56:37 +08:00
|
|
|
export enum MediaDeviceHandlerEvent {
|
|
|
|
AudioOutputChanged = "audio_output_changed",
|
|
|
|
}
|
|
|
|
|
|
|
|
export default class MediaDeviceHandler extends EventEmitter {
|
|
|
|
private static internalInstance;
|
|
|
|
|
|
|
|
public static get instance(): MediaDeviceHandler {
|
|
|
|
if (!MediaDeviceHandler.internalInstance) {
|
|
|
|
MediaDeviceHandler.internalInstance = new MediaDeviceHandler();
|
|
|
|
}
|
|
|
|
return MediaDeviceHandler.internalInstance;
|
|
|
|
}
|
|
|
|
|
2021-06-23 16:27:51 +08:00
|
|
|
public static async hasAnyLabeledDevices(): Promise<boolean> {
|
2021-06-23 15:26:33 +08:00
|
|
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
|
|
return devices.some(d => Boolean(d.label));
|
|
|
|
}
|
|
|
|
|
2022-10-01 00:28:53 +08:00
|
|
|
/**
|
|
|
|
* Gets the available audio input/output and video input devices
|
|
|
|
* from the browser: a thin wrapper around mediaDevices.enumerateDevices()
|
|
|
|
* that also returns results by type of devices. Note that this requires
|
|
|
|
* user media permissions and an active stream, otherwise you'll get blank
|
|
|
|
* device labels.
|
|
|
|
*
|
|
|
|
* Once the Permissions API
|
|
|
|
* (https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API)
|
|
|
|
* is ready for primetime, it might help make this simpler.
|
|
|
|
*
|
|
|
|
* @return Promise<IMediaDevices> The available media devices
|
|
|
|
*/
|
2021-06-23 16:27:51 +08:00
|
|
|
public static async getDevices(): Promise<IMediaDevices> {
|
2021-06-23 15:26:33 +08:00
|
|
|
try {
|
|
|
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
2021-07-09 19:08:39 +08:00
|
|
|
const output = {
|
|
|
|
[MediaDeviceKindEnum.AudioOutput]: [],
|
|
|
|
[MediaDeviceKindEnum.AudioInput]: [],
|
|
|
|
[MediaDeviceKindEnum.VideoInput]: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
devices.forEach((device) => output[device.kind].push(device));
|
|
|
|
return output;
|
2021-06-23 15:26:33 +08:00
|
|
|
} catch (error) {
|
2021-10-15 22:31:29 +08:00
|
|
|
logger.warn('Unable to refresh WebRTC Devices: ', error);
|
2021-06-23 15:26:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 16:37:15 +08:00
|
|
|
/**
|
|
|
|
* Retrieves devices from the SettingsStore and tells the js-sdk to use them
|
|
|
|
*/
|
2022-05-04 22:41:56 +08:00
|
|
|
public static async loadDevices(): Promise<void> {
|
2021-06-23 15:26:33 +08:00
|
|
|
const audioDeviceId = SettingsStore.getValue("webrtc_audioinput");
|
|
|
|
const videoDeviceId = SettingsStore.getValue("webrtc_videoinput");
|
|
|
|
|
2022-05-04 22:41:56 +08:00
|
|
|
await MatrixClientPeg.get().getMediaHandler().setAudioInput(audioDeviceId);
|
|
|
|
await MatrixClientPeg.get().getMediaHandler().setVideoInput(videoDeviceId);
|
2021-06-23 15:26:33 +08:00
|
|
|
}
|
|
|
|
|
2021-06-23 16:27:51 +08:00
|
|
|
public setAudioOutput(deviceId: string): void {
|
2021-06-23 15:26:33 +08:00
|
|
|
SettingsStore.setValue("webrtc_audiooutput", null, SettingLevel.DEVICE, deviceId);
|
2021-06-23 15:56:37 +08:00
|
|
|
this.emit(MediaDeviceHandlerEvent.AudioOutputChanged, deviceId);
|
2021-06-23 15:26:33 +08:00
|
|
|
}
|
|
|
|
|
2021-06-23 16:37:15 +08:00
|
|
|
/**
|
|
|
|
* This will not change the device that a potential call uses. The call will
|
|
|
|
* need to be ended and started again for this change to take effect
|
|
|
|
* @param {string} deviceId
|
|
|
|
*/
|
2022-05-04 22:41:56 +08:00
|
|
|
public async setAudioInput(deviceId: string): Promise<void> {
|
2021-06-23 15:26:33 +08:00
|
|
|
SettingsStore.setValue("webrtc_audioinput", null, SettingLevel.DEVICE, deviceId);
|
2022-05-04 22:41:56 +08:00
|
|
|
return MatrixClientPeg.get().getMediaHandler().setAudioInput(deviceId);
|
2021-06-23 15:26:33 +08:00
|
|
|
}
|
|
|
|
|
2021-06-23 16:37:15 +08:00
|
|
|
/**
|
|
|
|
* This will not change the device that a potential call uses. The call will
|
|
|
|
* need to be ended and started again for this change to take effect
|
|
|
|
* @param {string} deviceId
|
|
|
|
*/
|
2022-05-04 22:41:56 +08:00
|
|
|
public async setVideoInput(deviceId: string): Promise<void> {
|
2021-06-23 15:26:33 +08:00
|
|
|
SettingsStore.setValue("webrtc_videoinput", null, SettingLevel.DEVICE, deviceId);
|
2022-05-04 22:41:56 +08:00
|
|
|
return MatrixClientPeg.get().getMediaHandler().setVideoInput(deviceId);
|
2021-06-23 15:26:33 +08:00
|
|
|
}
|
|
|
|
|
2022-05-04 22:41:56 +08:00
|
|
|
public async setDevice(deviceId: string, kind: MediaDeviceKindEnum): Promise<void> {
|
2021-07-09 20:13:31 +08:00
|
|
|
switch (kind) {
|
|
|
|
case MediaDeviceKindEnum.AudioOutput: this.setAudioOutput(deviceId); break;
|
2022-05-04 22:41:56 +08:00
|
|
|
case MediaDeviceKindEnum.AudioInput: await this.setAudioInput(deviceId); break;
|
|
|
|
case MediaDeviceKindEnum.VideoInput: await this.setVideoInput(deviceId); break;
|
2021-07-09 20:13:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 16:27:51 +08:00
|
|
|
public static getAudioOutput(): string {
|
2021-06-23 15:26:33 +08:00
|
|
|
return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_audiooutput");
|
|
|
|
}
|
|
|
|
|
2021-06-23 16:27:51 +08:00
|
|
|
public static getAudioInput(): string {
|
2021-06-23 15:26:33 +08:00
|
|
|
return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_audioinput");
|
|
|
|
}
|
|
|
|
|
2021-06-23 16:27:51 +08:00
|
|
|
public static getVideoInput(): string {
|
2021-06-23 15:26:33 +08:00
|
|
|
return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_videoinput");
|
|
|
|
}
|
2022-05-04 22:41:56 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the current set deviceId for a device kind
|
|
|
|
* @param {MediaDeviceKindEnum} kind of the device that will be returned
|
|
|
|
* @returns {string} the deviceId
|
|
|
|
*/
|
|
|
|
public static getDevice(kind: MediaDeviceKindEnum): string {
|
|
|
|
switch (kind) {
|
|
|
|
case MediaDeviceKindEnum.AudioOutput: return this.getAudioOutput();
|
|
|
|
case MediaDeviceKindEnum.AudioInput: return this.getAudioInput();
|
|
|
|
case MediaDeviceKindEnum.VideoInput: return this.getVideoInput();
|
|
|
|
}
|
|
|
|
}
|
2022-08-31 03:13:39 +08:00
|
|
|
|
|
|
|
public static get startWithAudioMuted(): boolean {
|
|
|
|
return SettingsStore.getValue("audioInputMuted");
|
|
|
|
}
|
|
|
|
public static set startWithAudioMuted(value: boolean) {
|
|
|
|
SettingsStore.setValue("audioInputMuted", null, SettingLevel.DEVICE, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static get startWithVideoMuted(): boolean {
|
|
|
|
return SettingsStore.getValue("videoInputMuted");
|
|
|
|
}
|
|
|
|
public static set startWithVideoMuted(value: boolean) {
|
|
|
|
SettingsStore.setValue("videoInputMuted", null, SettingLevel.DEVICE, value);
|
|
|
|
}
|
2021-06-23 15:26:33 +08:00
|
|
|
}
|