From 2b97b81b6ea0cd0ad26c8af7a41fdc8fb51c0d3a Mon Sep 17 00:00:00 2001 From: David Zhao Date: Tue, 26 Oct 2021 13:12:23 -0700 Subject: [PATCH] Fix restartTrack for audio in Safari On Safari we need to stop the current audio device prior to acquiring the new one. Fixes #63 --- src/room/events.ts | 3 +++ src/room/participant/LocalParticipant.ts | 2 +- src/room/track/LocalAudioTrack.ts | 5 ++--- src/room/track/LocalTrack.ts | 26 ++++++++++++++++-------- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/room/events.ts b/src/room/events.ts index 0479cdc6ea..ca27157035 100644 --- a/src/room/events.ts +++ b/src/room/events.ts @@ -104,6 +104,8 @@ export enum RoomEvent { /** * A track was published successfully + * + * args: ([[LocalTrackPublication]], [[LocalParticipant]]) */ LocalTrackPublished = 'localTrackPublished', @@ -188,6 +190,7 @@ export enum TrackEvent { Message = 'message', Muted = 'muted', Unmuted = 'unmuted', + Ended = 'ended', /** @internal */ UpdateSettings = 'updateSettings', /** @internal */ diff --git a/src/room/participant/LocalParticipant.ts b/src/room/participant/LocalParticipant.ts index e6202a3931..41acf765d3 100644 --- a/src/room/participant/LocalParticipant.ts +++ b/src/room/participant/LocalParticipant.ts @@ -179,7 +179,7 @@ export default class LocalParticipant extends Participant { // handle track actions track.on(TrackEvent.Muted, this.onTrackMuted); track.on(TrackEvent.Unmuted, this.onTrackUnmuted); - track.mediaStreamTrack.addEventListener('ended', () => { + track.on(TrackEvent.Ended, () => { this.unpublishTrack(track); }); diff --git a/src/room/track/LocalAudioTrack.ts b/src/room/track/LocalAudioTrack.ts index 998e514468..1611b308d9 100644 --- a/src/room/track/LocalAudioTrack.ts +++ b/src/room/track/LocalAudioTrack.ts @@ -18,9 +18,8 @@ export default class LocalAudioTrack extends LocalTrack { return; } this.constraints.deviceId = deviceId; - await this.restartTrack(); - if (this.isMuted) { - this.mediaStreamTrack.enabled = false; + if (!this.isMuted) { + await this.restartTrack(); } } diff --git a/src/room/track/LocalTrack.ts b/src/room/track/LocalTrack.ts index d9182be902..22d6d3fcbb 100644 --- a/src/room/track/LocalTrack.ts +++ b/src/room/track/LocalTrack.ts @@ -15,6 +15,7 @@ export default class LocalTrack extends Track { protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind, name?: string, constraints?: MediaTrackConstraints) { super(mediaTrack, kind, name); + this.mediaStreamTrack.addEventListener('ended', this.handleEnded); this.constraints = constraints ?? mediaTrack.getConstraints(); } @@ -129,19 +130,22 @@ export default class LocalTrack extends Track { streamConstraints.audio = constraints; } - // TODO: for safari, there is a bug that might cause this to be wonky - // _workaroundWebKitBug1208516 - const mediaStream = await navigator.mediaDevices.getUserMedia(streamConstraints); - const newTrack = mediaStream.getTracks()[0]; - log.info('re-acquired MediaStreamTrack'); - - // detach and reattach - this.mediaStreamTrack.stop(); + // detach this.attachedElements.forEach((el) => { detachTrack(this.mediaStreamTrack, el); }); + this.mediaStreamTrack.removeEventListener('ended', this.handleEnded); + // on Safari, the old audio track must be stopped before attempting to acquire + // the new track, otherwise the new track will stop with + // 'A MediaStreamTrack ended due to a capture failure` + this.mediaStreamTrack.stop(); + + // create new track and attach + const mediaStream = await navigator.mediaDevices.getUserMedia(streamConstraints); + const newTrack = mediaStream.getTracks()[0]; + newTrack.addEventListener('ended', this.handleEnded); + log.debug('re-acquired MediaStreamTrack'); - newTrack.enabled = this.mediaStreamTrack.enabled; await this.sender.replaceTrack(newTrack); this.mediaStreamTrack = newTrack; @@ -162,4 +166,8 @@ export default class LocalTrack extends Track { this.mediaStreamTrack.enabled = !muted; this.emit(muted ? TrackEvent.Muted : TrackEvent.Unmuted, this); } + + private handleEnded = () => { + this.emit(TrackEvent.Ended); + }; }