in src/audiomixcontroller/DefaultAudioMixController.ts [93:158]
private async bindAudioMix(): Promise<void> {
if (!this.audioElement) {
return;
}
const previousStream = this.audioElement.srcObject as MediaStream;
if (this.audioStream) {
this.audioElement.srcObject = this.audioStream;
}
if (previousStream !== this.audioStream) {
this.forEachObserver((observer: AudioMixObserver) => {
if (previousStream) {
observer.meetingAudioStreamBecameInactive(previousStream);
}
if (this.audioStream) {
observer.meetingAudioStreamBecameActive(this.audioStream);
}
});
}
// In usual operation, the output device is undefined, and so is the element
// sink ID. In this case, don't throw an error -- we're being called as a side
// effect of just binding the audio element, not choosing an output device.
const shouldSetSinkId =
this.audioDevice?.deviceId !== (this.audioElement as AudioElementWithSinkId).sinkId;
if (
shouldSetSinkId &&
typeof (this.audioElement as AudioElementWithSinkId).sinkId === 'undefined'
) {
throw new Error(
'Cannot select audio output device. This browser does not support setSinkId.'
);
}
const newSinkId = this.audioDevice ? this.audioDevice.deviceId : '';
const oldSinkId: string = (this.audioElement as AudioElementWithSinkId).sinkId;
if (newSinkId === oldSinkId) {
return;
}
// Take the existing stream and temporarily unbind it while we change
// the sink ID.
const existingAudioElement: AudioElementWithSinkId = this
.audioElement as AudioElementWithSinkId;
const existingStream = this.audioStream;
if (this.browserBehavior.hasChromiumWebRTC()) {
existingAudioElement.srcObject = null;
}
if (shouldSetSinkId) {
try {
await existingAudioElement.setSinkId(newSinkId);
} catch (error) {
this.logger?.error(`Failed to set sinkId for audio element: ${error}`);
throw error;
}
}
if (this.browserBehavior.hasChromiumWebRTC()) {
existingAudioElement.srcObject = existingStream;
}
}