This commit is contained in:
RMidhunSuresh 2023-01-16 13:41:47 +05:30
parent 50f46a21bd
commit f6c94ecc5a
No known key found for this signature in database
5 changed files with 59 additions and 41 deletions

View File

@ -136,7 +136,7 @@ class OwnMemberViewModel extends ViewModel<Options> implements IStreamViewModel
} }
get stream(): Stream | undefined { get stream(): Stream | undefined {
return this.call.localMedia?.userMediaPreview; return this.call.localPreviewMedia?.userMedia;
} }
private get call(): GroupCall { private get call(): GroupCall {

View File

@ -20,24 +20,12 @@ import {SDPStreamMetadata} from "./callEventTypes";
import {getStreamVideoTrack, getStreamAudioTrack} from "./common"; import {getStreamVideoTrack, getStreamAudioTrack} from "./common";
export class LocalMedia { export class LocalMedia {
// the userMedia stream without audio, to play in the UI
// without our own audio being played back to us
public readonly userMediaPreview?: Stream;
constructor( constructor(
public readonly userMedia?: Stream, public readonly userMedia?: Stream,
public readonly screenShare?: Stream, public readonly screenShare?: Stream,
public readonly dataChannelOptions?: RTCDataChannelInit, public readonly dataChannelOptions?: RTCDataChannelInit,
) { ) {}
if (userMedia && userMedia.getVideoTracks().length > 0) {
this.userMediaPreview = userMedia.clone();
const audioTrack = getStreamAudioTrack(this.userMediaPreview);
if (audioTrack) {
audioTrack.stop();
this.userMediaPreview.removeTrack(audioTrack);
}
}
}
withUserMedia(stream: Stream) { withUserMedia(stream: Stream) {
return new LocalMedia(stream, this.screenShare, this.dataChannelOptions); return new LocalMedia(stream, this.screenShare, this.dataChannelOptions);
@ -51,6 +39,22 @@ export class LocalMedia {
return new LocalMedia(this.userMedia, this.screenShare, options); return new LocalMedia(this.userMedia, this.screenShare, options);
} }
/**
* Create an instance of LocalMedia without audio track (for user preview)
*/
asPreview(): LocalMedia {
const media = new LocalMedia(this.userMedia, this.screenShare, this.dataChannelOptions);
const userMedia = media.userMedia;
if (userMedia && userMedia.getVideoTracks().length > 0) {
const audioTrack = getStreamAudioTrack(userMedia);
if (audioTrack) {
audioTrack.stop();
userMedia.removeTrack(audioTrack);
}
}
return media;
}
/** @internal */ /** @internal */
replaceClone(oldClone: LocalMedia | undefined, oldOriginal: LocalMedia | undefined): LocalMedia { replaceClone(oldClone: LocalMedia | undefined, oldOriginal: LocalMedia | undefined): LocalMedia {
const cloneOrAdoptStream = (oldOriginalStream: Stream | undefined, oldCloneStream: Stream | undefined, newStream: Stream | undefined): Stream | undefined => { const cloneOrAdoptStream = (oldOriginalStream: Stream | undefined, oldCloneStream: Stream | undefined, newStream: Stream | undefined): Stream | undefined => {
@ -76,7 +80,6 @@ export class LocalMedia {
dispose() { dispose() {
getStreamAudioTrack(this.userMedia)?.stop(); getStreamAudioTrack(this.userMedia)?.stop();
getStreamVideoTrack(this.userMedia)?.stop(); getStreamVideoTrack(this.userMedia)?.stop();
getStreamVideoTrack(this.userMediaPreview)?.stop();
getStreamVideoTrack(this.screenShare)?.stop(); getStreamVideoTrack(this.screenShare)?.stop();
} }
} }

View File

@ -20,7 +20,7 @@ import {recursivelyAssign} from "../../utils/recursivelyAssign";
import {Disposables, Disposable, IDisposable} from "../../utils/Disposables"; import {Disposables, Disposable, IDisposable} from "../../utils/Disposables";
import {WebRTC, PeerConnection, Transceiver, TransceiverDirection, Sender, Receiver, PeerConnectionEventMap} from "../../platform/types/WebRTC"; import {WebRTC, PeerConnection, Transceiver, TransceiverDirection, Sender, Receiver, PeerConnectionEventMap} from "../../platform/types/WebRTC";
import {MediaDevices, Track, TrackKind, Stream, StreamTrackEvent} from "../../platform/types/MediaDevices"; import {MediaDevices, Track, TrackKind, Stream, StreamTrackEvent} from "../../platform/types/MediaDevices";
import {getStreamVideoTrack, getStreamAudioTrack, MuteSettings} from "./common"; import {getStreamVideoTrack, getStreamAudioTrack, MuteSettings, mute} from "./common";
import { import {
SDPStreamMetadataKey, SDPStreamMetadataKey,
SDPStreamMetadataPurpose, SDPStreamMetadataPurpose,
@ -266,14 +266,7 @@ export class PeerCall implements IDisposable {
log.set("microphoneMuted", localMuteSettings.microphone); log.set("microphoneMuted", localMuteSettings.microphone);
if (this.localMedia) { if (this.localMedia) {
const userMediaAudio = getStreamAudioTrack(this.localMedia.userMedia); mute(this.localMedia, localMuteSettings, log);
if (userMediaAudio) {
this.muteTrack(userMediaAudio, this.localMuteSettings.microphone, log);
}
const userMediaVideo = getStreamVideoTrack(this.localMedia.userMedia);
if (userMediaVideo) {
this.muteTrack(userMediaVideo, this.localMuteSettings.camera, log);
}
const content: MCallSDPStreamMetadataChanged<MCallBase> = { const content: MCallSDPStreamMetadataChanged<MCallBase> = {
call_id: this.callId, call_id: this.callId,
version: 1, version: 1,
@ -290,22 +283,6 @@ export class PeerCall implements IDisposable {
}); });
} }
private muteTrack(track: Track, muted: boolean, log: ILogItem): void {
log.wrap({l: "track", kind: track.kind, id: track.id}, log => {
const enabled = !muted;
log.set("enabled", enabled);
const transceiver = this.findTransceiverForTrack(track);
if (transceiver) {
if (transceiver.sender.track) {
transceiver.sender.track.enabled = enabled;
}
log.set("fromDirection", transceiver.direction);
// enableSenderOnTransceiver(transceiver, enabled);
log.set("toDirection", transceiver.direction);
}
});
}
private async _hangup(errorCode: CallErrorCode, log: ILogItem): Promise<void> { private async _hangup(errorCode: CallErrorCode, log: ILogItem): Promise<void> {
if (this._state === CallState.Ended) { if (this._state === CallState.Ended) {
return; return;

View File

@ -14,7 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {ILogItem} from "../../logging/types";
import type {Track, Stream} from "../../platform/types/MediaDevices"; import type {Track, Stream} from "../../platform/types/MediaDevices";
import {LocalMedia} from "./LocalMedia";
export function getStreamAudioTrack(stream: Stream | undefined): Track | undefined { export function getStreamAudioTrack(stream: Stream | undefined): Track | undefined {
return stream?.getAudioTracks()[0]; return stream?.getAudioTracks()[0];
@ -24,6 +26,29 @@ export function getStreamVideoTrack(stream: Stream | undefined): Track | undefin
return stream?.getVideoTracks()[0]; return stream?.getVideoTracks()[0];
} }
export function mute(localMedia: LocalMedia, localMuteSettings: MuteSettings, log: ILogItem) {
return log.wrap("setMuted", log => {
log.set("cameraMuted", localMuteSettings.camera);
log.set("microphoneMuted", localMuteSettings.microphone);
// Mute audio
const userMediaAudio = getStreamAudioTrack(localMedia.userMedia);
if (userMediaAudio) {
const enabled = !localMuteSettings.microphone;
log.set("microphone enabled", enabled);
userMediaAudio.enabled = enabled;
}
// Mute video
const userMediaVideo = getStreamVideoTrack(localMedia.userMedia);
if (userMediaVideo) {
const enabled = !localMuteSettings.camera;
log.set("camera enabled", enabled);
userMediaVideo.enabled = enabled;
}
});
}
export class MuteSettings { export class MuteSettings {
constructor ( constructor (
private readonly isMicrophoneMuted: boolean = false, private readonly isMicrophoneMuted: boolean = false,

View File

@ -17,7 +17,7 @@ limitations under the License.
import {ObservableMap} from "../../../observable/map/ObservableMap"; import {ObservableMap} from "../../../observable/map/ObservableMap";
import {Member, isMemberExpired, memberExpiresAt} from "./Member"; import {Member, isMemberExpired, memberExpiresAt} from "./Member";
import {LocalMedia} from "../LocalMedia"; import {LocalMedia} from "../LocalMedia";
import {MuteSettings, CALL_LOG_TYPE, CALL_MEMBER_VALIDITY_PERIOD_MS} from "../common"; import {MuteSettings, CALL_LOG_TYPE, CALL_MEMBER_VALIDITY_PERIOD_MS, mute} from "../common";
import {MemberChange, RoomMember} from "../../room/members/RoomMember"; import {MemberChange, RoomMember} from "../../room/members/RoomMember";
import {EventEmitter} from "../../../utils/EventEmitter"; import {EventEmitter} from "../../../utils/EventEmitter";
import {EventType, CallIntent} from "../callEventTypes"; import {EventType, CallIntent} from "../callEventTypes";
@ -72,12 +72,14 @@ class JoinedData {
public readonly logItem: ILogItem, public readonly logItem: ILogItem,
public readonly membersLogItem: ILogItem, public readonly membersLogItem: ILogItem,
public localMedia: LocalMedia, public localMedia: LocalMedia,
public localPreviewMedia: LocalMedia,
public localMuteSettings: MuteSettings, public localMuteSettings: MuteSettings,
public readonly turnServer: BaseObservableValue<RTCIceServer> public readonly turnServer: BaseObservableValue<RTCIceServer>
) {} ) {}
dispose() { dispose() {
this.localMedia.dispose(); this.localMedia.dispose();
this.localPreviewMedia.dispose();
this.logItem.finish(); this.logItem.finish();
this.renewMembershipTimeout?.dispose(); this.renewMembershipTimeout?.dispose();
} }
@ -125,6 +127,7 @@ export class GroupCall extends EventEmitter<{change: never}> {
} }
get localMedia(): LocalMedia | undefined { return this.joinedData?.localMedia; } get localMedia(): LocalMedia | undefined { return this.joinedData?.localMedia; }
get localPreviewMedia(): LocalMedia | undefined { return this.joinedData?.localPreviewMedia; }
get members(): BaseObservableMap<string, Member> { return this._members; } get members(): BaseObservableMap<string, Member> { return this._members; }
get isTerminated(): boolean { get isTerminated(): boolean {
@ -165,10 +168,12 @@ export class GroupCall extends EventEmitter<{change: never}> {
const membersLogItem = logItem.child("member connections"); const membersLogItem = logItem.child("member connections");
const localMuteSettings = new MuteSettings(); const localMuteSettings = new MuteSettings();
localMuteSettings.updateTrackInfo(localMedia.userMedia); localMuteSettings.updateTrackInfo(localMedia.userMedia);
const localPreviewMedia = localMedia.asPreview();
const joinedData = new JoinedData( const joinedData = new JoinedData(
logItem, logItem,
membersLogItem, membersLogItem,
localMedia, localMedia,
localPreviewMedia,
localMuteSettings, localMuteSettings,
turnServer turnServer
); );
@ -219,6 +224,14 @@ export class GroupCall extends EventEmitter<{change: never}> {
muteSettings.updateTrackInfo(joinedData.localMedia.userMedia); muteSettings.updateTrackInfo(joinedData.localMedia.userMedia);
joinedData.localMuteSettings = muteSettings; joinedData.localMuteSettings = muteSettings;
if (!prevMuteSettings.equals(muteSettings)) { if (!prevMuteSettings.equals(muteSettings)) {
// Mute our copies of LocalMedias;
// otherwise the camera lights will still be on.
if (this.localPreviewMedia) {
mute(this.localPreviewMedia, muteSettings, this.joinedData!.logItem);
}
if (this.localMedia) {
mute(this.localMedia, muteSettings, this.joinedData!.logItem);
}
await Promise.all(Array.from(this._members.values()).map(m => { await Promise.all(Array.from(this._members.values()).map(m => {
return m.setMuted(joinedData.localMuteSettings); return m.setMuted(joinedData.localMuteSettings);
})); }));