Handle mid-call output changes
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
parent
9bceb40820
commit
58151d71c5
3 changed files with 40 additions and 11 deletions
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import { SettingLevel } from "./settings/SettingLevel";
|
import { SettingLevel } from "./settings/SettingLevel";
|
||||||
import { setMatrixCallAudioInput, setMatrixCallVideoInput } from "matrix-js-sdk/src/matrix";
|
import { setMatrixCallAudioInput, setMatrixCallVideoInput } from "matrix-js-sdk/src/matrix";
|
||||||
|
import EventEmitter from 'events';
|
||||||
|
|
||||||
interface IMediaDevices {
|
interface IMediaDevices {
|
||||||
audioOutput: Array<MediaDeviceInfo>;
|
audioOutput: Array<MediaDeviceInfo>;
|
||||||
|
@ -25,7 +26,22 @@ interface IMediaDevices {
|
||||||
videoInput: Array<MediaDeviceInfo>;
|
videoInput: Array<MediaDeviceInfo>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MediaDeviceHandler {
|
export enum MediaDeviceHandlerEvent {
|
||||||
|
AudioOutputChanged = "audio_output_changed",
|
||||||
|
AudioInputChanged = "audio_input_changed",
|
||||||
|
VideoInputChanged = "video_input_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;
|
||||||
|
}
|
||||||
|
|
||||||
static async hasAnyLabeledDevices(): Promise<boolean> {
|
static async hasAnyLabeledDevices(): Promise<boolean> {
|
||||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
return devices.some(d => Boolean(d.label));
|
return devices.some(d => Boolean(d.label));
|
||||||
|
@ -68,18 +84,21 @@ export default class MediaDeviceHandler {
|
||||||
setMatrixCallVideoInput(videoDeviceId);
|
setMatrixCallVideoInput(videoDeviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
static setAudioOutput(deviceId: string) {
|
public setAudioOutput(deviceId: string) {
|
||||||
SettingsStore.setValue("webrtc_audiooutput", null, SettingLevel.DEVICE, deviceId);
|
SettingsStore.setValue("webrtc_audiooutput", null, SettingLevel.DEVICE, deviceId);
|
||||||
|
this.emit(MediaDeviceHandlerEvent.AudioOutputChanged, deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
static setAudioInput(deviceId: string) {
|
public setAudioInput(deviceId: string) {
|
||||||
SettingsStore.setValue("webrtc_audioinput", null, SettingLevel.DEVICE, deviceId);
|
SettingsStore.setValue("webrtc_audioinput", null, SettingLevel.DEVICE, deviceId);
|
||||||
setMatrixCallAudioInput(deviceId);
|
setMatrixCallAudioInput(deviceId);
|
||||||
|
this.emit(MediaDeviceHandlerEvent.AudioInputChanged, deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
static setVideoInput(deviceId: string) {
|
public setVideoInput(deviceId: string) {
|
||||||
SettingsStore.setValue("webrtc_videoinput", null, SettingLevel.DEVICE, deviceId);
|
SettingsStore.setValue("webrtc_videoinput", null, SettingLevel.DEVICE, deviceId);
|
||||||
setMatrixCallVideoInput(deviceId);
|
setMatrixCallVideoInput(deviceId);
|
||||||
|
this.emit(MediaDeviceHandlerEvent.VideoInputChanged, deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getAudioOutput(): string {
|
static getAudioOutput(): string {
|
||||||
|
|
|
@ -100,21 +100,21 @@ export default class VoiceUserSettingsTab extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
_setAudioOutput = (e) => {
|
_setAudioOutput = (e) => {
|
||||||
MediaDeviceHandler.setAudioOutput(e.target.value);
|
MediaDeviceHandler.instance.setAudioOutput(e.target.value);
|
||||||
this.setState({
|
this.setState({
|
||||||
activeAudioOutput: e.target.value,
|
activeAudioOutput: e.target.value,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_setAudioInput = (e) => {
|
_setAudioInput = (e) => {
|
||||||
MediaDeviceHandler.setAudioInput(e.target.value);
|
MediaDeviceHandler.instance.setAudioInput(e.target.value);
|
||||||
this.setState({
|
this.setState({
|
||||||
activeAudioInput: e.target.value,
|
activeAudioInput: e.target.value,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_setVideoInput = (e) => {
|
_setVideoInput = (e) => {
|
||||||
MediaDeviceHandler.setVideoInput(e.target.value);
|
MediaDeviceHandler.instance.setVideoInput(e.target.value);
|
||||||
this.setState({
|
this.setState({
|
||||||
activeVideoInput: e.target.value,
|
activeVideoInput: e.target.value,
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import { CallFeed, CallFeedEvent } from 'matrix-js-sdk/src/webrtc/callFeed';
|
import { CallFeed, CallFeedEvent } from 'matrix-js-sdk/src/webrtc/callFeed';
|
||||||
import { logger } from 'matrix-js-sdk/src/logger';
|
import { logger } from 'matrix-js-sdk/src/logger';
|
||||||
import MediaDeviceHandler from "../../../MediaDeviceHandler";
|
import MediaDeviceHandler, { MediaDeviceHandlerEvent } from "../../../MediaDeviceHandler";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
feed: CallFeed,
|
feed: CallFeed,
|
||||||
|
@ -27,19 +27,25 @@ export default class AudioFeed extends React.Component<IProps> {
|
||||||
private element = createRef<HTMLAudioElement>();
|
private element = createRef<HTMLAudioElement>();
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
MediaDeviceHandler.instance.addListener(
|
||||||
|
MediaDeviceHandlerEvent.AudioOutputChanged,
|
||||||
|
this.onAudioOutputChanged,
|
||||||
|
);
|
||||||
this.props.feed.addListener(CallFeedEvent.NewStream, this.onNewStream);
|
this.props.feed.addListener(CallFeedEvent.NewStream, this.onNewStream);
|
||||||
this.playMedia();
|
this.playMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
MediaDeviceHandler.instance.removeListener(
|
||||||
|
MediaDeviceHandlerEvent.AudioOutputChanged,
|
||||||
|
this.onAudioOutputChanged,
|
||||||
|
);
|
||||||
this.props.feed.removeListener(CallFeedEvent.NewStream, this.onNewStream);
|
this.props.feed.removeListener(CallFeedEvent.NewStream, this.onNewStream);
|
||||||
this.stopMedia();
|
this.stopMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
private playMedia() {
|
private onAudioOutputChanged = (audioOutput: string) => {
|
||||||
const element = this.element.current;
|
const element = this.element.current;
|
||||||
const audioOutput = MediaDeviceHandler.getAudioOutput();
|
|
||||||
|
|
||||||
if (audioOutput) {
|
if (audioOutput) {
|
||||||
try {
|
try {
|
||||||
// This seems quite unreliable in Chrome, although I haven't yet managed to make a jsfiddle where
|
// This seems quite unreliable in Chrome, although I haven't yet managed to make a jsfiddle where
|
||||||
|
@ -52,7 +58,11 @@ export default class AudioFeed extends React.Component<IProps> {
|
||||||
logger.warn("Couldn't set requested audio output device: using default", e);
|
logger.warn("Couldn't set requested audio output device: using default", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private playMedia() {
|
||||||
|
const element = this.element.current;
|
||||||
|
this.onAudioOutputChanged(MediaDeviceHandler.getAudioOutput());
|
||||||
element.muted = false;
|
element.muted = false;
|
||||||
element.srcObject = this.props.feed.stream;
|
element.srcObject = this.props.feed.stream;
|
||||||
element.autoplay = true;
|
element.autoplay = true;
|
||||||
|
|
Loading…
Reference in a new issue